From 07a8bc3f2b77373d2a8a6b248ff9b171026518eb Mon Sep 17 00:00:00 2001 From: nico Date: Tue, 21 Mar 2023 11:53:40 -0500 Subject: [PATCH] init --- .circleci/config.yml | 627 + .credo.exs | 150 + .dialyzer-ignore | 35 + .dockerignore | 9 + .formatter.exs | 11 + .github/dependabot.yml | 19 + .github/workflows/codeql-analysis.yml | 72 + .github/workflows/config.yml | 584 + .github/workflows/e2e-tests.yml | 86 + .../publish-docker-image-every-push.yml | 82 + .../publish-docker-image-release.yml | 111 + .gitignore | 57 + .pairs | 13 + .tool-versions | 3 + CHANGELOG.md | 2046 + CODE_OF_CONDUCT.md | 73 + CONTRIBUTING.md | 62 + ISSUE_TEMPLATE.md | 22 + LICENSE | 674 + PULL_REQUEST_TEMPLATE.md | 29 + README.md | 52 + apps/block_scout_web/.sobelow-conf | 12 + apps/block_scout_web/API blueprint.md | 2228 ++ apps/block_scout_web/API.md | 2227 ++ apps/block_scout_web/README.md | 43 + apps/block_scout_web/assets/.babelrc | 3 + apps/block_scout_web/assets/.eslintrc | 6 + apps/block_scout_web/assets/README.md | 9 + .../assets/__mocks__/css/app.scss.js | 4 + .../__tests__/lib/async_listing_load.js | 82 + .../assets/__tests__/lib/autocomplete.js | 31 + .../assets/__tests__/lib/currency.js | 18 + .../lib/smart_contract/common_helpers.js | 285 + .../assets/__tests__/lib/utils.js | 12 + .../assets/__tests__/pages/address.js | 66 + .../pages/address/internal_transactions.js | 151 + .../__tests__/pages/address/transactions.js | 128 + .../__tests__/pages/address/validations.js | 50 + .../assets/__tests__/pages/blocks.js | 156 + .../assets/__tests__/pages/chain.js | 419 + .../__tests__/pages/pending_transactions.js | 212 + .../assets/__tests__/pages/transaction.js | 18 + .../assets/__tests__/pages/transactions.js | 133 + apps/block_scout_web/assets/close.svg | 4 + apps/block_scout_web/assets/css/_code.scss | 46 + .../block_scout_web/assets/css/_elements.scss | 29 + apps/block_scout_web/assets/css/_helpers.scss | 54 + apps/block_scout_web/assets/css/_layout.scss | 16 + apps/block_scout_web/assets/css/_mixins.scss | 253 + .../assets/css/_typography.scss | 130 + apps/block_scout_web/assets/css/app.scss | 136 + .../assets/css/components/_account.scss | 143 + .../assets/css/components/_ad.scss | 9 + .../css/components/_address-overview.scss | 84 + .../assets/css/components/_address_link.scss | 23 + .../assets/css/components/_alerts.scss | 9 + .../assets/css/components/_animations.scss | 94 + .../assets/css/components/_api.scss | 254 + .../assets/css/components/_badge.scss | 45 + .../assets/css/components/_btn_add_to_mm.scss | 28 + .../css/components/_btn_address_card.scss | 11 + .../assets/css/components/_btn_contract.scss | 18 + .../assets/css/components/_btn_copy.scss | 61 + .../css/components/_btn_dropdown_line.scss | 21 + .../assets/css/components/_btn_full.scss | 6 + .../assets/css/components/_btn_line.scss | 16 + .../assets/css/components/_btn_no_border.scss | 30 + .../assets/css/components/_btn_qr.scss | 6 + .../assets/css/components/_btn_swap.scss | 32 + .../assets/css/components/_btn_wallet.scss | 10 + .../assets/css/components/_button.scss | 104 + .../assets/css/components/_card.scss | 347 + .../assets/css/components/_check.scss | 48 + .../assets/css/components/_check_tooltip.scss | 27 + .../css/components/_coin-balance-tile.scss | 9 + .../css/components/_custom_tooltips.scss | 135 + .../css/components/_dashboard-banner.scss | 289 + .../css/components/_description-list.scss | 3 + .../assets/css/components/_dot.scss | 23 + .../assets/css/components/_dropdown.scss | 116 + .../assets/css/components/_dropzone.scss | 541 + .../_erc721_token_image_container.scss | 11 + .../assets/css/components/_errors.scss | 137 + .../assets/css/components/_external_link.scss | 21 + .../assets/css/components/_filter.scss | 4 + .../css/components/_fontawesome_icon.scss | 55 + .../assets/css/components/_footer.scss | 121 + .../assets/css/components/_form.scss | 152 + .../assets/css/components/_highlight.scss | 17 + .../assets/css/components/_i_tooltip.scss | 27 + .../assets/css/components/_i_tooltip_2.scss | 11 + .../assets/css/components/_icon-link.scss | 36 + ...entory_token_instance_image_container.scss | 11 + .../assets/css/components/_label.scss | 104 + .../css/components/_loading-spinner.scss | 87 + .../assets/css/components/_log-search.scss | 69 + .../assets/css/components/_me_tooltip.scss | 25 + .../assets/css/components/_modal.scss | 110 + .../assets/css/components/_modal_stake.scss | 140 + .../assets/css/components/_modal_status.scss | 103 + .../css/components/_modal_variables.scss | 5 + .../assets/css/components/_nav_tabs.scss | 39 + .../assets/css/components/_navbar.scss | 304 + .../css/components/_new_smart_contract.scss | 212 + .../css/components/_nounderline-link.scss | 3 + .../css/components/_pagination_container.scss | 196 + .../assets/css/components/_panels.scss | 14 + .../assets/css/components/_qr-code.scss | 6 + .../assets/css/components/_radio.scss | 49 + .../assets/css/components/_radio_big.scss | 55 + .../assets/css/components/_search.scss | 72 + .../assets/css/components/_stakes_table.scss | 218 + .../css/components/_stakes_variables.scss | 2 + .../assets/css/components/_table.scss | 30 + .../assets/css/components/_tile.scss | 612 + .../components/_token-balance-dropdown.scss | 117 + .../assets/css/components/_token.scss | 10 + .../css/components/_token_tile_view_more.scss | 47 + .../assets/css/components/_tooltip.scss | 25 + .../css/components/_transaction-input.scss | 14 + .../assets/css/components/_transaction.scss | 70 + .../components/_verify_other_explorers.scss | 216 + .../assets/css/components/datepicker.scss | 1 + .../assets/css/export-csv.scss | 12 + .../assets/css/export-vars-to-js.module.scss | 35 + .../block_scout_web/assets/css/main-page.scss | 76 + .../assets/css/non-critical.scss | 20 + .../assets/css/theme/_base_variables.scss | 995 + .../assets/css/theme/_callisto_variables.scss | 3 + .../theme/_dai_variables-non-critical.scss | 7 + .../assets/css/theme/_dai_variables.scss | 282 + .../assets/css/theme/_dark-theme.scss | 1133 + .../assets/css/theme/_ellaism_variables.scss | 3 + .../theme/_ether1_variables-non-critical.scss | 7 + .../assets/css/theme/_ether1_variables.scss | 49 + .../_ethercore_variables-non-critical.scss | 7 + .../css/theme/_ethercore_variables.scss | 84 + ...hereum_classic_variables-non-critical.scss | 7 + .../theme/_ethereum_classic_variables.scss | 84 + .../_ethereum_variables-non-critical.scss | 7 + .../assets/css/theme/_ethereum_variables.scss | 75 + .../assets/css/theme/_expanse_variables.scss | 3 + .../assets/css/theme/_fonts.scss | 2 + .../assets/css/theme/_gochain_variables.scss | 3 + .../theme/_goerli_variables-non-critical.scss | 8 + .../assets/css/theme/_goerli_variables.scss | 87 + .../theme/_kovan_variables-non-critical.scss | 7 + .../assets/css/theme/_kovan_variables.scss | 84 + .../theme/_lukso_variables-non-critical.scss | 7 + .../assets/css/theme/_lukso_variables.scss | 174 + .../assets/css/theme/_musicoin_variables.scss | 3 + .../_neutral_variables-non-critical.scss | 7 + .../assets/css/theme/_neutral_variables.scss | 80 + .../assets/css/theme/_pirl_variables.scss | 8 + .../theme/_poa_variables-non-critical.scss | 7 + .../assets/css/theme/_poa_variables.scss | 81 + .../_rinkeby_variables-non-critical.scss | 7 + .../assets/css/theme/_rinkeby_variables.scss | 66 + .../_ropsten_variables-non-critical.scss | 7 + .../assets/css/theme/_ropsten_variables.scss | 66 + .../theme/_rsk_variables-non-critical.scss | 7 + .../assets/css/theme/_rsk_variables.scss | 73 + .../assets/css/theme/_social_variables.scss | 8 + .../theme/_sokol_variables-non-critical.scss | 8 + .../assets/css/theme/_sokol_variables.scss | 81 + .../assets/css/theme/_tobalaba_variables.scss | 8 + .../css/theme/_tomochain_variables.scss | 4 + .../css/theme/_variables-non-critical.scss | 23 + .../assets/css/theme/_variables.scss | 32 + .../assets/css/theme/_wanchain_variables.scss | 3 + .../theme/_xusdt_variables-non-critical.scss | 7 + .../assets/css/theme/_xusdt_variables.scss | 97 + .../custom_contracts/_circles-theme.scss | 717 + .../custom_contracts/_dark-forest-theme.scss | 1035 + apps/block_scout_web/assets/js/app.js | 40 + .../assets/js/balance-chart-loader.js | 13 + .../block_scout_web/assets/js/chart-loader.js | 11 + .../assets/js/lib/ace/src-min/ace.js | 17 + .../assets/js/lib/ace/src-min/mode-csharp.js | 8 + .../assets/js/lib/ace/src-min/theme-chrome.js | 8 + apps/block_scout_web/assets/js/lib/ad.js | 99 + .../assets/js/lib/add_chain_to_mm.js | 49 + .../assets/js/lib/async_listing_load.js | 380 + .../assets/js/lib/autocomplete.js | 189 + apps/block_scout_web/assets/js/lib/banner.js | 15 + .../assets/js/lib/card_tabs.js | 26 + .../assets/js/lib/clipboard_buttons.js | 22 + .../js/lib/coin_balance_history_chart.js | 83 + .../assets/js/lib/csv_download.js | 82 + .../block_scout_web/assets/js/lib/currency.js | 72 + .../assets/js/lib/custom_ad.json | 1 + .../block_scout_web/assets/js/lib/dropzone.js | 1 + .../block_scout_web/assets/js/lib/from_now.js | 35 + .../assets/js/lib/history_chart.js | 342 + .../block_scout_web/assets/js/lib/indexing.js | 50 + .../assets/js/lib/infinite_scroll_helpers.js | 107 + .../assets/js/lib/list_morph.js | 130 + .../assets/js/lib/loading_element.js | 5 + apps/block_scout_web/assets/js/lib/modals.js | 196 + .../js/lib/pending_transactions_toggle.js | 20 + .../assets/js/lib/pretty_json.js | 9 + .../assets/js/lib/public_tags_request_form.js | 43 + apps/block_scout_web/assets/js/lib/queue.js | 72 + .../assets/js/lib/random_access_pagination.js | 369 + .../assets/js/lib/redux_helpers.js | 128 + .../assets/js/lib/reload_button.js | 3 + .../js/lib/smart_contract/common_helpers.js | 312 + .../assets/js/lib/smart_contract/connect.js | 179 + .../assets/js/lib/smart_contract/functions.js | 106 + .../assets/js/lib/smart_contract/index.js | 3 + .../assets/js/lib/smart_contract/interact.js | 115 + .../lib/smart_contract/wei_ether_converter.js | 28 + .../assets/js/lib/stop_propagation.js | 3 + apps/block_scout_web/assets/js/lib/text_ad.js | 8 + .../assets/js/lib/token_balance_dropdown.js | 41 + .../js/lib/token_balance_dropdown_search.js | 43 + .../assets/js/lib/token_icon.js | 55 + .../assets/js/lib/token_transfers_toggle.js | 17 + apps/block_scout_web/assets/js/lib/tooltip.js | 5 + .../js/lib/transaction_input_dropdown.js | 13 + apps/block_scout_web/assets/js/lib/try_api.js | 137 + .../assets/js/lib/try_eth_api.js | 82 + apps/block_scout_web/assets/js/lib/utils.js | 38 + .../assets/js/lib/validation.js | 64 + apps/block_scout_web/assets/js/locale.js | 8 + .../js/pages/account/delete_item_handler.js | 19 + .../assets/js/pages/address.js | 321 + .../assets/js/pages/address/coin_balances.js | 68 + .../js/pages/address/internal_transactions.js | 117 + .../assets/js/pages/address/logs.js | 104 + .../js/pages/address/token_transfers.js | 107 + .../assets/js/pages/address/transactions.js | 181 + .../assets/js/pages/address/utils.js | 5 + .../assets/js/pages/address/validations.js | 69 + .../assets/js/pages/admin/tasks.js | 27 + .../block_scout_web/assets/js/pages/blocks.js | 129 + apps/block_scout_web/assets/js/pages/chain.js | 412 + .../assets/js/pages/dark-mode-switcher.js | 11 + .../block_scout_web/assets/js/pages/layout.js | 56 + .../assets/js/pages/pending_transactions.js | 135 + .../assets/js/pages/search-results/search.js | 53 + .../assets/js/pages/token/overview.js | 11 + .../assets/js/pages/token/search.js | 58 + .../assets/js/pages/token/token_transfers.js | 87 + .../assets/js/pages/token_contract.js | 2 + .../assets/js/pages/token_counters.js | 97 + .../assets/js/pages/transaction.js | 167 + .../assets/js/pages/transactions.js | 106 + .../assets/js/pages/verification_form.js | 367 + .../assets/js/pages/verified_contracts.js | 94 + apps/block_scout_web/assets/js/socket.js | 38 + .../address_contract/code_highlighting.js | 30 + .../raw_trace/code_highlighting.js | 6 + apps/block_scout_web/assets/package-lock.json | 32539 ++++++++++++++++ apps/block_scout_web/assets/package.json | 110 + apps/block_scout_web/assets/postcss.config.js | 9 + .../assets/static/android-chrome-192x192.png | Bin 0 -> 16924 bytes .../assets/static/android-chrome-512x512.png | Bin 0 -> 64101 bytes .../assets/static/apple-touch-icon.png | Bin 0 -> 15254 bytes .../assets/static/browserconfig.xml | 9 + .../assets/static/images/average_time.svg | 1 + .../assets/static/images/block.svg | 1 + .../assets/static/images/blocks.svg | 3 + .../assets/static/images/blockscout_logo.svg | 1 + .../assets/static/images/callisto_logo.svg | 1 + .../static/images/classic_ethereum_logo.svg | 1 + .../assets/static/images/classic_logo.svg | 1 + .../assets/static/images/controller.svg | 1 + .../assets/static/images/cube.svg | 1 + .../images/custom-themes/circles/balance.svg | 19 + .../custom-themes/circles/copy-circles.svg | 1 + .../custom-themes/circles/footer_logo.svg | 1 + .../images/custom-themes/circles/logo.svg | 1 + .../custom-themes/circles/qr-circles.svg | 1 + .../custom-themes/dark-forest/copy-df.svg | 1 + .../dark-forest/dark_forest_logo.svg | 1 + .../custom-themes/dark-forest/pic_balance.svg | 1 + .../custom-themes/dark-forest/planet.svg | 1 + .../custom-themes/dark-forest/qr-df.svg | 1 + .../custom-themes/dark-forest/union.svg | 1 + .../assets/static/images/dai_logo.svg | 1 + .../assets/static/images/ellaism_logo.svg | 1 + .../images/errors-img/etc-block-not-found.png | Bin 0 -> 11447 bytes .../errors-img/etc-block-not-found@2x.png | Bin 0 -> 24234 bytes .../images/errors-img/etc-page-not-found.png | Bin 0 -> 13383 bytes .../errors-img/etc-page-not-found@2x.png | Bin 0 -> 29207 bytes .../images/errors-img/etc-tx-not-found.png | Bin 0 -> 10565 bytes .../images/errors-img/etc-tx-not-found@2x.png | Bin 0 -> 21965 bytes .../images/errors-img/eth-block-not-found.png | Bin 0 -> 11346 bytes .../errors-img/eth-block-not-found@2x.png | Bin 0 -> 23984 bytes .../images/errors-img/eth-page-not-found.png | Bin 0 -> 13434 bytes .../errors-img/eth-page-not-found@2x.png | Bin 0 -> 29356 bytes .../images/errors-img/eth-tx-not-found.png | Bin 0 -> 10643 bytes .../images/errors-img/eth-tx-not-found@2x.png | Bin 0 -> 22224 bytes .../errors-img/goerli-block-not-found.png | Bin 0 -> 11360 bytes .../errors-img/goerli-block-not-found@2x.png | Bin 0 -> 24034 bytes .../errors-img/goerli-page-not-found.png | Bin 0 -> 13387 bytes .../errors-img/goerli-page-not-found@2x.png | Bin 0 -> 29168 bytes .../images/errors-img/goerli-tx-not-found.png | Bin 0 -> 10309 bytes .../errors-img/goerli-tx-not-found@2x.png | Bin 0 -> 21439 bytes .../errors-img/koan-block-not-found@2x.png | Bin 0 -> 26425 bytes .../errors-img/kovan-block-not-found.png | Bin 0 -> 11515 bytes .../errors-img/kovan-block-not-found@2x.png | Bin 0 -> 24324 bytes .../errors-img/kovan-page-not-found.png | Bin 0 -> 13510 bytes .../errors-img/kovan-page-not-found@2x.png | Bin 0 -> 29445 bytes .../images/errors-img/kovan-tx-not-found.png | Bin 0 -> 10528 bytes .../errors-img/kovan-tx-not-found@2x.png | Bin 0 -> 21863 bytes .../errors-img/lukso-block-not-found.png | Bin 0 -> 11875 bytes .../errors-img/lukso-block-not-found@2x.png | Bin 0 -> 24689 bytes .../errors-img/lukso-page-not-found.png | Bin 0 -> 13113 bytes .../errors-img/lukso-page-not-found@2x.png | Bin 0 -> 28578 bytes .../images/errors-img/lukso-tx-not-found.png | Bin 0 -> 13640 bytes .../errors-img/lukso-tx-not-found@2x.png | Bin 0 -> 27875 bytes .../static/images/errors-img/pic-404.svg | 27 + .../images/errors-img/poa-block-not-found.png | Bin 0 -> 11331 bytes .../errors-img/poa-block-not-found@2x.png | Bin 0 -> 23998 bytes .../images/errors-img/poa-page-not-found.png | Bin 0 -> 13372 bytes .../errors-img/poa-page-not-found@2x.png | Bin 0 -> 29117 bytes .../images/errors-img/poa-tx-not-found.png | Bin 0 -> 10192 bytes .../images/errors-img/poa-tx-not-found@2x.png | Bin 0 -> 21210 bytes .../errors-img/rinkeby-block-not-found.png | Bin 0 -> 11333 bytes .../errors-img/rinkeby-block-not-found@2x.png | Bin 0 -> 23948 bytes .../errors-img/rinkeby-page-not-found.png | Bin 0 -> 13184 bytes .../errors-img/rinkeby-page-not-found@2x.png | Bin 0 -> 28743 bytes .../errors-img/rinkeby-tx-not-found.png | Bin 0 -> 10356 bytes .../errors-img/rinkeby-tx-not-found@2x.png | Bin 0 -> 21581 bytes .../errors-img/rinnkeby-block-not-found.png | Bin 0 -> 10661 bytes .../rinnkeby-block-not-found@2x.png | Bin 0 -> 23316 bytes .../errors-img/ropsten-block-not-found.png | Bin 0 -> 11245 bytes .../errors-img/ropsten-block-not-found@2x.png | Bin 0 -> 23815 bytes .../errors-img/ropsten-page-not-found.png | Bin 0 -> 13184 bytes .../errors-img/ropsten-page-not-found@2x.png | Bin 0 -> 28749 bytes .../errors-img/ropsten-tx-not-found.png | Bin 0 -> 10354 bytes .../errors-img/ropsten-tx-not-found@2x.png | Bin 0 -> 21564 bytes .../images/errors-img/rsk-block-not-found.png | Bin 0 -> 11360 bytes .../errors-img/rsk-block-not-found@2x.png | Bin 0 -> 24074 bytes .../images/errors-img/rsk-page-not-found.png | Bin 0 -> 13520 bytes .../errors-img/rsk-page-not-found@2x.png | Bin 0 -> 29527 bytes .../images/errors-img/rsk-tx-not-found.png | Bin 0 -> 10994 bytes .../images/errors-img/rsk-tx-not-found@2x.png | Bin 0 -> 22889 bytes .../errors-img/sokol-block-not-found.png | Bin 0 -> 11360 bytes .../errors-img/sokol-block-not-found@2x.png | Bin 0 -> 24074 bytes .../errors-img/sokol-page-not-found.png | Bin 0 -> 13520 bytes .../errors-img/sokol-page-not-found@2x.png | Bin 0 -> 29527 bytes .../images/errors-img/sokol-tx-not-found.png | Bin 0 -> 10994 bytes .../errors-img/sokol-tx-not-found@2x.png | Bin 0 -> 22889 bytes .../errors-img/xdai-block-not-found.png | Bin 0 -> 11335 bytes .../errors-img/xdai-block-not-found@2x.png | Bin 0 -> 24073 bytes .../images/errors-img/xdai-page-not-found.png | Bin 0 -> 13319 bytes .../errors-img/xdai-page-not-found@2x.png | Bin 0 -> 28979 bytes .../images/errors-img/xdai-tx-not-found.png | Bin 0 -> 10309 bytes .../errors-img/xdai-tx-not-found@2x.png | Bin 0 -> 21439 bytes .../assets/static/images/eth.png | Bin 0 -> 1292 bytes .../assets/static/images/ether1_logo.svg | 1 + .../assets/static/images/ethercore_logo.svg | 10139 +++++ .../static/images/ethercore_testnet_logo.svg | 5382 +++ .../assets/static/images/ethereum.png | Bin 0 -> 9853 bytes .../assets/static/images/ethereum_logo.svg | 1 + .../assets/static/images/expanse_logo.png | Bin 0 -> 35288 bytes .../assets/static/images/favicon-16x16.png | Bin 0 -> 728 bytes .../assets/static/images/favicon-32x32.png | Bin 0 -> 1618 bytes .../assets/static/images/favicon.ico | Bin 0 -> 15406 bytes .../assets/static/images/gc_logo_2.svg | 6 + .../assets/static/images/gc_logo_old.svg | 1 + .../assets/static/images/gc_logo_rebrand.svg | 3 + .../assets/static/images/gochain_logo.png | Bin 0 -> 13703 bytes .../assets/static/images/goerli_logo.svg | 1 + .../assets/static/images/icons/blockchair.png | Bin 0 -> 562 bytes .../static/images/icons/blockchair@2x.png | Bin 0 -> 1404 bytes .../assets/static/images/icons/check-1.svg | 3 + .../assets/static/images/icons/copy.svg | 3 + .../assets/static/images/icons/dots.svg | 3 + .../assets/static/images/icons/etherchain.png | Bin 0 -> 305 bytes .../static/images/icons/etherchain@2x.png | Bin 0 -> 569 bytes .../assets/static/images/icons/etherscan.png | Bin 0 -> 796 bytes .../static/images/icons/etherscan@2x.png | Bin 0 -> 1781 bytes .../images/icons/fontawesome/bar-chart.svg | 1 + .../images/icons/fontawesome/github.svg | 1 + .../images/icons/fontawesome/info-circle.svg | 1 + .../static/images/icons/fontawesome/tag.svg | 2 + .../images/icons/fontawesome/telegram.svg | 1 + .../images/icons/fontawesome/twitter.svg | 1 + .../assets/static/images/icons/link.svg | 3 + .../static/images/icons/metamask-fox.svg | 61 + .../assets/static/images/icons/pic-empty.svg | 1 + .../assets/static/images/icons/plus.svg | 1 + .../assets/static/images/icons/remove.svg | 1 + .../assets/static/images/icons/swap/1inch.svg | 16 + .../static/images/icons/swap/component.png | Bin 0 -> 25775 bytes .../static/images/icons/swap/cowswap.png | Bin 0 -> 14780 bytes .../assets/static/images/icons/swap/curve.svg | 1 + .../static/images/icons/swap/honeyswap.png | Bin 0 -> 3773 bytes .../assets/static/images/icons/swap/sushi.svg | 1 + .../assets/static/images/icons/swap/swapr.svg | 1 + .../assets/static/images/icons/withdraw.svg | 3 + .../assets/static/images/kaly_footer.png | Bin 0 -> 1618 bytes .../assets/static/images/kalycoin-logo.png | Bin 0 -> 16631 bytes .../assets/static/images/kovan_logo.svg | 1 + .../assets/static/images/last_block.svg | 1 + .../assets/static/images/logo.svg | 1 + .../static/images/lukso_dashboard_image.png | Bin 0 -> 22431 bytes .../assets/static/images/lukso_logo.png | Bin 0 -> 2326 bytes .../static/images/lukso_logo_footer.png | Bin 0 -> 2263 bytes .../assets/static/images/musicoin_logo.svg | 1 + .../callisto-mainnet.svg | 1 + .../network-selector-icons/circle-xusdt.svg | 1 + .../ethereum-classic.svg | 1 + .../ethereum-mainnet.svg | 1 + .../network-selector-icons/goerli-testnet.svg | 1 + .../network-selector-icons/kovan-testnet.svg | 1 + .../lukso-l14-testnet.svg | 1 + .../network-selector-icons/poa-core.svg | 1 + .../network-selector-icons/poa-sokol.svg | 1 + .../rinkeby-testnet.svg | 1 + .../ropsten-testnet.svg | 1 + .../network-selector-icons/rsk-mainnet.svg | 1 + .../network-selector-icons/xdai-chain.svg | 1 + .../assets/static/images/pirl_logo.svg | 1 + .../assets/static/images/poa_logo.svg | 3 + .../assets/static/images/purple-block.svg | 1 + .../assets/static/images/rinkeby_logo.svg | 1 + .../assets/static/images/ropsten_logo.svg | 1 + .../assets/static/images/rsk_logo.svg | 1 + .../assets/static/images/smart_contract.svg | 1 + .../assets/static/images/social_logo.svg | 1 + .../assets/static/images/sokol_logo.svg | 1 + .../assets/static/images/spinner.svg | 52 + .../assets/static/images/tobalaba_logo.svg | 1 + .../assets/static/images/token.svg | 1 + .../assets/static/images/tomochain_logo.svg | 1 + .../assets/static/images/transaction.svg | 3 + .../assets/static/images/transactions.svg | 3 + .../assets/static/images/wanchain_logo.png | Bin 0 -> 2416 bytes .../assets/static/images/xdai_alternative.svg | 1 + .../assets/static/images/xdai_logo.svg | 1 + .../static/images/xusdt-logo-footer.svg | 1 + .../assets/static/images/xusdt-logo-top.svg | 1 + .../assets/static/manifest.webmanifest | 19 + .../assets/static/mstile-150x150.png | Bin 0 -> 7418 bytes apps/block_scout_web/assets/static/robots.txt | 5 + .../assets/static/safari-pinned-tab.svg | 39 + apps/block_scout_web/assets/webpack.config.js | 173 + apps/block_scout_web/config/config.exs | 115 + apps/block_scout_web/config/dev.exs | 66 + apps/block_scout_web/config/prod.exs | 33 + apps/block_scout_web/config/runtime/test.exs | 21 + apps/block_scout_web/config/test.exs | 35 + apps/block_scout_web/lib/block_scout_web.ex | 86 + .../lib/block_scout_web/admin_router.ex | 51 + .../lib/block_scout_web/api_router.ex | 199 + .../lib/block_scout_web/application.ex | 52 + .../lib/block_scout_web/captcha_helper.ex | 27 + .../lib/block_scout_web/chain.ex | 413 + .../channels/address_channel.ex | 335 + .../block_scout_web/channels/block_channel.ex | 65 + .../channels/exchange_rate_channel.ex | 35 + .../channels/reward_channel.ex | 46 + .../block_scout_web/channels/token_channel.ex | 51 + .../channels/transaction_channel.ex | 92 + .../block_scout_web/channels/user_socket.ex | 21 + .../channels/user_socket_v2.ex | 19 + .../lib/block_scout_web/checksum_address.ex | 58 + .../lib/block_scout_web/cldr.ex | 13 + .../lib/block_scout_web/controller.ex | 65 + .../account/api/v1/fallback_controller.ex | 83 + .../account/api/v1/tags_controller.ex | 87 + .../account/api/v1/user_controller.ex | 476 + .../controllers/account/api_key_controller.ex | 65 + .../controllers/account/auth_controller.ex | 74 + .../account/custom_abi_controller.ex | 87 + .../account/public_tags_request_controller.ex | 114 + .../account/tag_address_controller.ex | 49 + .../account/tag_transaction_controller.ex | 49 + .../account/watchlist_address_controller.ex | 92 + .../account/watchlist_controller.ex | 26 + .../address_coin_balance_by_day_controller.ex | 27 + .../address_coin_balance_controller.ex | 119 + .../address_contract_controller.ex | 49 + ...ddress_contract_verification_controller.ex | 309 + ...ification_via_flattened_code_controller.ex | 60 + ...ntract_verification_via_json_controller.ex | 35 + ...ication_via_multi_part_files_controller.ex | 41 + ...tion_via_standard_json_input_controller.ex | 40 + ..._contract_verification_vyper_controller.ex | 58 + .../controllers/address_controller.ex | 226 + .../address_decompiled_contract_controller.ex | 36 + ...address_internal_transaction_controller.ex | 130 + .../controllers/address_logs_controller.ex | 125 + .../address_read_contract_controller.ex | 92 + .../address_read_proxy_controller.ex | 45 + .../address_token_balance_controller.ex | 50 + .../controllers/address_token_controller.ex | 93 + .../address_token_transfer_controller.ex | 221 + .../address_transaction_controller.ex | 292 + .../address_validation_controller.ex | 100 + .../address_write_contract_controller.ex | 84 + .../address_write_proxy_controller.ex | 45 + .../controllers/admin/dashboard_controller.ex | 7 + .../controllers/admin/session_controller.ex | 37 + .../controllers/admin/setup_controller.ex | 90 + .../controllers/admin/tasks_controller.ex | 22 + .../controllers/api/api_logger.ex | 25 + .../controllers/api/eth_rpc/eth_controller.ex | 63 + .../controllers/api/rpc/address_controller.ex | 524 + .../controllers/api/rpc/block_controller.ex | 60 + .../api/rpc/contract_controller.ex | 611 + .../controllers/api/rpc/helpers.ex | 42 + .../controllers/api/rpc/logs_controller.ex | 238 + .../controllers/api/rpc/rpc_translator.ex | 127 + .../controllers/api/rpc/stats_controller.ex | 90 + .../controllers/api/rpc/token_controller.ex | 60 + .../api/rpc/transaction_controller.ex | 102 + .../decompiled_smart_contract_controller.ex | 69 + .../api/v1/gas_price_oracle_controller.ex | 48 + .../controllers/api/v1/health_controller.ex | 64 + .../controllers/api/v1/supply_controller.ex | 14 + .../v1/verified_smart_contract_controller.ex | 65 + .../controllers/api/v2/address_controller.ex | 238 + .../controllers/api/v2/block_controller.ex | 81 + .../controllers/api/v2/config_controller.ex | 11 + .../controllers/api/v2/fallback_controller.ex | 38 + .../api/v2/main_page_controller.ex | 40 + .../controllers/api/v2/search_controller.ex | 24 + .../controllers/api/v2/stats_controller.ex | 104 + .../api/v2/transaction_controller.ex | 221 + .../controllers/api_docs_controller.ex | 18 + .../controllers/block_controller.ex | 116 + .../block_transaction_controller.ex | 169 + .../chain/market_history_chart_controller.ex | 53 + .../transaction_history_chart_controller.ex | 43 + .../controllers/chain_controller.ex | 178 + .../common_components_controller.ex | 12 + .../controllers/csv_export_controller.ex | 35 + .../controllers/page_not_found_controller.ex | 9 + .../pending_transaction_controller.ex | 69 + .../recent_transactions_controller.ex | 45 + .../controllers/search_controller.ex | 77 + .../controllers/smart_contract_controller.ex | 252 + .../controllers/tokens/contract_controller.ex | 56 + .../controllers/tokens/holder_controller.ex | 86 + .../tokens/instance/holder_controller.ex | 78 + .../tokens/instance/metadata_controller.ex | 35 + .../tokens/instance/transfer_controller.ex | 80 + .../controllers/tokens/instance_controller.ex | 26 + .../tokens/inventory_controller.ex | 87 + .../controllers/tokens/token_controller.ex | 55 + .../controllers/tokens/tokens_controller.ex | 74 + .../controllers/tokens/transfer_controller.ex | 91 + .../controllers/transaction_controller.ex | 252 + ...saction_internal_transaction_controller.ex | 130 + .../controllers/transaction_log_controller.ex | 123 + .../transaction_raw_trace_controller.ex | 111 + .../transaction_state_controller.ex | 464 + .../transaction_token_transfer_controller.ex | 137 + .../verified_contracts_controller.ex | 75 + .../counters/blocks_indexed_counter.ex | 58 + .../internal_transactions_indexed_counter.ex | 58 + .../lib/block_scout_web/csp_header.ex | 30 + .../lib/block_scout_web/endpoint.ex | 90 + .../lib/block_scout_web/etherscan.ex | 3011 ++ .../lib/block_scout_web/gettext.ex | 24 + .../models/get_address_tags.ex | 74 + .../models/get_transaction_tags.ex | 48 + .../block_scout_web/models/user_from_auth.ex | 136 + .../lib/block_scout_web/notifier.ex | 401 + .../lib/block_scout_web/paging_helper.ex | 117 + .../plug/admin/check_owner_registered.ex | 29 + .../plug/admin/require_admin_role.ex | 26 + .../lib/block_scout_web/plug/allow_iframe.ex | 14 + .../block_scout_web/plug/check_account_api.ex | 21 + .../block_scout_web/plug/check_account_web.ex | 31 + .../lib/block_scout_web/plug/check_api_v2.ex | 21 + .../plug/fetch_user_from_session.ex | 20 + .../lib/block_scout_web/plug/graphql.ex | 12 + .../lib/block_scout_web/plug/redis_cookie.ex | 223 + .../block_scout_web/prometheus/exporter.ex | 9 + .../prometheus/instrumenter.ex | 16 + .../block_scout_web/realtime_event_handler.ex | 41 + .../lib/block_scout_web/resolvers/address.ex | 19 + .../lib/block_scout_web/resolvers/block.ex | 12 + .../resolvers/internal_transaction.ex | 23 + .../resolvers/token_transfer.ex | 24 + .../block_scout_web/resolvers/transaction.ex | 28 + .../lib/block_scout_web/router.ex | 89 + .../lib/block_scout_web/schema.ex | 124 + .../lib/block_scout_web/schema/scalars.ex | 121 + .../block_scout_web/schema/scalars/JSON.ex | 33 + .../lib/block_scout_web/schema/types.ex | 176 + .../lib/block_scout_web/social_media.ex | 25 + .../templates/account/api_key/form.html.eex | 34 + .../templates/account/api_key/index.html.eex | 51 + .../templates/account/api_key/row.html.eex | 18 + .../templates/account/auth/profile.html.eex | 36 + .../templates/account/common/_nav.html.eex | 25 + .../account/custom_abi/form.html.eex | 39 + .../account/custom_abi/index.html.eex | 51 + .../templates/account/custom_abi/row.html.eex | 18 + .../address_field.html.eex | 8 + .../account/public_tags_request/form.html.eex | 72 + .../public_tags_request/index.html.eex | 44 + .../account/public_tags_request/row.html.eex | 20 + .../account/tag_address/form.html.eex | 33 + .../account/tag_address/index.html.eex | 41 + .../account/tag_address/row.html.eex | 15 + .../account/tag_transaction/form.html.eex | 33 + .../account/tag_transaction/index.html.eex | 41 + .../account/tag_transaction/row.html.eex | 18 + .../templates/account/watchlist/show.html.eex | 42 + .../account/watchlist_address/form.html.eex | 91 + .../account/watchlist_address/row.html.eex | 29 + .../address/_balance_dropdown.html.eex | 12 + .../templates/address/_block_link.html.eex | 1 + .../address/_current_coin_balance.html.eex | 15 + .../address/_custom_view_df_title.html.eex | 19 + .../templates/address/_labels.html.eex | 19 + .../templates/address/_link.html.eex | 15 + .../templates/address/_metatags.html.eex | 15 + .../address/_responsive_hash.html.eex | 30 + .../_show_address_transactions.html.eex | 1 + .../templates/address/_tabs.html.eex | 107 + .../templates/address/_tile.html.eex | 34 + .../_validator_metadata_modal.html.eex | 41 + .../address/_verify_other_explorer.html.eex | 15 + .../_verify_other_explorer_modal.html.eex | 22 + .../address/_verify_other_explorers.html.eex | 38 + .../templates/address/index.html.eex | 58 + .../templates/address/overview.html.eex | 292 + .../_coin_balances.html.eex | 31 + .../address_coin_balance/index.html.eex | 48 + .../address_contract/_metatags.html.eex | 1 + .../templates/address_contract/index.html.eex | 224 + .../new.html.eex | 129 + .../_compiler_field.html.eex | 10 + .../_constructor_args.html.eex | 10 + .../_contract_address_field.html.eex | 10 + .../_contract_name_field.html.eex | 10 + .../_fetch_constructor_args.html.eex | 20 + .../_include_nightly_builds_field.html.eex | 21 + .../_libraries_other.html.eex | 8 + .../_library_address.html.eex | 10 + .../_library_first.html.eex | 13 + .../_library_name.html.eex | 10 + .../new.html.eex | 119 + .../new.html.eex | 49 + .../new.html.eex | 115 + .../new.html.eex | 63 + .../new.html.eex | 59 + .../_metatags.html.eex | 1 + .../index.html.eex | 33 + .../_metatags.html.eex | 1 + .../index.html.eex | 72 + .../templates/address_logs/_logs.html.eex | 111 + .../templates/address_logs/index.html.eex | 46 + .../address_read_contract/_metatags.html.eex | 1 + .../address_read_contract/index.html.eex | 58 + .../address_read_proxy/_metatags.html.eex | 1 + .../address_read_proxy/index.html.eex | 17 + .../address_token/_metatags.html.eex | 1 + .../templates/address_token/_tokens.html.eex | 58 + .../templates/address_token/index.html.eex | 75 + .../templates/address_token/overview.html.eex | 73 + .../address_token/overview_item.html.eex | 14 + .../_token_balances.html.eex | 78 + .../address_token_balance/_tokens.html.eex | 65 + .../address_token_transfer/_metatags.html.eex | 1 + .../address_token_transfer/index.html.eex | 76 + .../address_transaction/_metatags.html.eex | 1 + .../address_transaction/index.html.eex | 72 + .../address_validation/_metatags.html.eex | 1 + .../address_validation/index.html.eex | 33 + .../address_write_contract/_metatags.html.eex | 1 + .../address_write_contract/index.html.eex | 57 + .../address_write_proxy/_metatags.html.eex | 1 + .../address_write_proxy/index.html.eex | 17 + .../templates/admin/dashboard/index.html.eex | 37 + .../admin/session/login_form.html.eex | 17 + .../admin/setup/admin_registration.html.eex | 19 + .../templates/admin/setup/verify.html.eex | 25 + .../banners_ad/_banner_728.html.eex | 6 + .../advertisement/text_ad/index.html.eex | 4 + .../templates/api_docs/_action_tile.html.eex | 259 + .../templates/api_docs/_eth_rpc_item.html.eex | 182 + .../templates/api_docs/_metatags.html.eex | 5 + .../templates/api_docs/_model_table.html.eex | 65 + .../templates/api_docs/_module_card.html.eex | 12 + .../templates/api_docs/_module_item.html.eex | 4 + .../templates/api_docs/eth_rpc.html.eex | 25 + .../templates/api_docs/index.html.eex | 18 + .../templates/block/_link.html.eex | 4 + .../templates/block/_metatags.html.eex | 13 + .../templates/block/_tile.html.eex | 82 + .../templates/block/index.html.eex | 25 + .../templates/block/overview.html.eex | 271 + .../templates/block_transaction/404.html.eex | 13 + .../block_transaction/_metatags.html.eex | 1 + .../block_transaction/index.html.eex | 41 + .../templates/chain/_block.html.eex | 32 + .../templates/chain/_metatags.html.eex | 5 + .../gas_price_oracle_legend_item.html.eex | 43 + .../templates/chain/show.html.eex | 231 + .../common_components/_btn_add_full.html.eex | 9 + .../common_components/_btn_add_line.html.eex | 9 + .../common_components/_btn_copy.html.eex | 14 + .../_btn_copy_for_table.html.eex | 15 + .../_btn_external_link.html.eex | 8 + .../common_components/_btn_line.html.eex | 6 + .../common_components/_btn_qr_code.html.eex | 17 + .../_changed_bytecode_warning.html.eex | 4 + .../_channel_disconnected_message.html.eex | 5 + .../common_components/_check_tooltip.html.eex | 14 + .../_csv_export_button.html.eex | 7 + .../common_components/_i_tooltip.html.eex | 14 + .../common_components/_i_tooltip_2.html.eex | 11 + .../_icon_error_modal.html.eex | 29 + .../_icon_question_modal.html.eex | 29 + .../_icon_success_modal.html.eex | 29 + .../_icon_warning_modal.html.eex | 29 + .../common_components/_info.html.eex | 1 + .../common_components/_input_group.html.eex | 17 + .../_loading_spinner.html.eex | 6 + .../_minimal_proxy_pattern.html.eex | 10 + .../_modal_bottom_disclaimer.html.eex | 8 + .../_modal_close_button.html.eex | 5 + .../common_components/_modal_qr_code.html.eex | 18 + .../common_components/_modal_status.html.eex | 32 + .../_pagination_container.html.eex | 66 + .../_progress_from_to.html.eex | 9 + .../_rap_pagination_container.html.eex | 15 + .../common_components/_status_icon.html.eex | 10 + .../common_components/_svg_minus.html.eex | 4 + .../common_components/_svg_pen.html.eex | 3 + .../common_components/_svg_plus.html.eex | 4 + .../common_components/_svg_trash.html.eex | 3 + .../common_components/_table-loader.html.eex | 9 + .../common_components/_tenderly_link.html.eex | 4 + .../common_components/_tile-loader.html.eex | 120 + ..._token_transfer_type_display_name.html.eex | 12 + .../templates/csv_export/index.html.eex | 52 + .../templates/error422/index.html.eex | 12 + .../templates/form/_tag.html.eex | 3 + .../templates/form/text_field.html.eex | 15 + .../templates/icons/_accounts_icon.html.eex | 4 + .../templates/icons/_active_icon.html.eex | 3 + .../templates/icons/_api_icon.html.eex | 5 + .../templates/icons/_apps_icon.html.eex | 5 + .../templates/icons/_block_icon.html.eex | 5 + .../templates/icons/_blockchain_icon.html.eex | 3 + .../icons/_check_dark_forest_icon.html.eex | 1 + .../templates/icons/_external_link.html.eex | 1 + .../templates/icons/_gas_price_icon.html.eex | 3 + .../templates/icons/_guage_icon.html.eex | 18 + .../templates/icons/_hourglass_icon.html.eex | 17 + .../templates/icons/_inactive_icon.html.eex | 3 + .../templates/icons/_network_icon.html.eex | 3 + .../templates/icons/_search_icon.html.eex | 3 + .../templates/icons/_smart_contract.html.eex | 22 + .../icons/_test_network_icon.html.eex | 3 + .../templates/icons/_tokens_icon.html.eex | 4 + .../icons/_transaction_icon.html.eex | 4 + .../internal_transaction/_tile.html.eex | 48 + .../layout/_account_menu_item.html.eex | 33 + .../layout/_add_chain_to_mm.html.eex | 13 + .../templates/layout/_default_title.html.eex | 3 + .../templates/layout/_footer.html.eex | 58 + .../templates/layout/_search.html.eex | 35 + .../templates/layout/_topnav.html.eex | 172 + .../templates/layout/app.html.eex | 308 + .../templates/log/_data_decoded_view.html.eex | 37 + .../templates/page_not_found/index.html.eex | 12 + .../pending_transaction/index.html.eex | 35 + .../templates/search/_empty_td.html.eex | 4 + .../templates/search/_name_td.html.eex | 7 + .../templates/search/_tile.html.eex | 94 + .../templates/search/results.html.eex | 51 + .../_connect_container.html.eex | 16 + .../_function_response.html.eex | 44 + .../smart_contract/_functions.html.eex | 155 + .../_pending_contract_write.html.eex | 20 + .../templates/tokens/_tile.html.eex | 49 + .../templates/tokens/_token_icon.html.eex | 2 + .../tokens/contract/_metatags.html.eex | 1 + .../templates/tokens/contract/index.html.eex | 23 + .../tokens/holder/_metatags.html.eex | 1 + .../tokens/holder/_token_balances.html.eex | 19 + .../templates/tokens/holder/index.html.eex | 44 + .../templates/tokens/index.html.eex | 60 + .../tokens/instance/holder/index.html.eex | 41 + .../tokens/instance/metadata/index.html.eex | 30 + .../instance/overview/_details.html.eex | 97 + .../tokens/instance/overview/_tabs.html.eex | 22 + .../tokens/instance/transfer/index.html.eex | 41 + .../tokens/inventory/_metatags.html.eex | 1 + .../tokens/inventory/_token.html.eex | 58 + .../templates/tokens/inventory/index.html.eex | 42 + .../tokens/overview/_details.html.eex | 156 + .../tokens/overview/_metatags.html.eex | 5 + .../templates/tokens/overview/_tabs.html.eex | 53 + .../tokens/transfer/_metatags.html.eex | 1 + .../tokens/transfer/_token_transfer.html.eex | 50 + .../templates/tokens/transfer/index.html.eex | 42 + .../transaction/_decoded_input.html.eex | 44 + .../transaction/_decoded_input_body.html.eex | 62 + .../_emission_reward_tile.html.eex | 34 + .../templates/transaction/_link.html.eex | 4 + .../_link_to_token_instance.html.eex | 1 + .../_link_to_token_symbol.html.eex | 1 + .../templates/transaction/_metatags.html.eex | 14 + .../transaction/_pending_tile.html.eex | 25 + .../templates/transaction/_tabs.html.eex | 34 + .../templates/transaction/_tile.html.eex | 99 + .../transaction/_token_transfer.html.eex | 25 + .../transaction/_total_transfers.html.eex | 23 + .../_total_transfers_from_to.html.eex | 48 + .../_transfer_token_with_id.html.eex | 2 + .../templates/transaction/index.html.eex | 45 + .../templates/transaction/invalid.html.eex | 14 + .../templates/transaction/not_found.html.eex | 34 + .../templates/transaction/overview.html.eex | 512 + .../show_internal_transactions.html.eex | 1 + .../transaction/show_token_transfers.html.eex | 1 + .../_metatags.html.eex | 1 + .../index.html.eex | 29 + .../templates/transaction_log/_logs.html.eex | 139 + .../transaction_log/_metatags.html.eex | 1 + .../templates/transaction_log/index.html.eex | 32 + .../transaction_raw_trace/_metatags.html.eex | 1 + .../transaction_raw_trace/index.html.eex | 28 + .../transaction_state/_metatags.html.eex | 1 + .../transaction_state/_state_change.html.eex | 67 + .../transaction_state/_token_balance.html.eex | 1 + .../transaction_state/index.html.eex | 51 + .../_metatags.html.eex | 1 + .../_token_transfer.html.eex | 20 + .../transaction_token_transfer/index.html.eex | 32 + .../verified_contracts/_contract.html.eex | 61 + .../verified_contracts/_metatags.html.eex | 8 + .../verified_contracts/_stats.html.eex | 33 + .../verified_contracts/index.html.eex | 101 + .../lib/block_scout_web/tracer.ex | 5 + .../views/abi_encoded_value_view.ex | 213 + .../block_scout_web/views/access_helpers.ex | 177 + .../views/account/api/v1/account_view.ex | 7 + .../views/account/api/v1/tags_view.ex | 27 + .../views/account/api/v1/user_view.ex | 141 + .../views/account/api_key_view.ex | 5 + .../views/account/auth_view.ex | 3 + .../views/account/common_view.ex | 11 + .../views/account/custom_abi_view.ex | 22 + .../views/account/public_tags_request_view.ex | 70 + .../views/account/tag_address_view.ex | 7 + .../views/account/tag_transaction_view.ex | 5 + .../views/account/watchlist_address_view.ex | 11 + .../views/account/watchlist_view.ex | 17 + .../views/address_coin_balance_view.ex | 33 + ...ontract_verification_common_fields_view.ex | 3 + ...ct_verification_via_flattened_code_view.ex | 6 + ...ess_contract_verification_via_json_view.ex | 3 + ..._verification_via_multi_part_files_view.ex | 6 + ...rification_via_standard_json_input_view.ex | 6 + .../address_contract_verification_view.ex | 5 + ...ddress_contract_verification_vyper_view.ex | 6 + .../views/address_contract_view.ex | 151 + .../views/address_decompiled_contract_view.ex | 272 + .../address_internal_transaction_view.ex | 14 + .../views/address_logs_view.ex | 9 + .../views/address_read_contract_view.ex | 3 + .../views/address_read_proxy_view.ex | 3 + .../views/address_token_balance_view.ex | 133 + .../views/address_token_transfer_view.ex | 14 + .../views/address_token_view.ex | 7 + .../views/address_transaction_view.ex | 14 + .../views/address_validation_view.ex | 5 + .../lib/block_scout_web/views/address_view.ex | 481 + .../views/address_write_contract_view.ex | 3 + .../views/address_write_proxy_view.ex | 3 + .../views/admin/dashboard_view.ex | 3 + .../views/admin/session_view.ex | 7 + .../block_scout_web/views/admin/setup_view.ex | 7 + .../views/advertisement/banners_ad_view.ex | 3 + .../views/advertisement/text_ad_view.ex | 3 + .../block_scout_web/views/api/eth_rpc/view.ex | 84 + .../views/api/rpc/address_view.ex | 215 + .../views/api/rpc/block_view.ex | 44 + .../views/api/rpc/contract_view.ex | 221 + .../views/api/rpc/logs_view.ex | 52 + .../block_scout_web/views/api/rpc/rpc_view.ex | 27 + .../views/api/rpc/stats_view.ex | 53 + .../views/api/rpc/token_view.ex | 37 + .../views/api/rpc/transaction_view.ex | 90 + .../views/api/v1/supply_view.ex | 10 + .../views/api/v2/address_view.ex | 59 + .../block_scout_web/views/api/v2/api_v2.ex | 9 + .../block_scout_web/views/api/v2/api_view.ex | 7 + .../views/api/v2/block_view.ex | 106 + .../views/api/v2/config_view.ex | 7 + .../block_scout_web/views/api/v2/helper.ex | 108 + .../views/api/v2/search_view.ex | 53 + .../views/api/v2/token_view.ex | 13 + .../views/api/v2/transaction_view.ex | 447 + .../block_scout_web/views/api_docs_view.ex | 90 + .../views/block_transaction_view.ex | 17 + .../lib/block_scout_web/views/block_view.ex | 85 + .../lib/block_scout_web/views/chain_view.ex | 72 + .../views/cldr_helper/number.ex | 41 + .../views/common_components_view.ex | 7 + .../lib/block_scout_web/views/csv_export.ex | 44 + .../block_scout_web/views/currency_helpers.ex | 95 + .../lib/block_scout_web/views/error_422.ex | 5 + .../block_scout_web/views/error_helpers.ex | 59 + .../lib/block_scout_web/views/error_view.ex | 34 + .../lib/block_scout_web/views/form_view.ex | 66 + .../lib/block_scout_web/views/icons_view.ex | 3 + .../views/internal_transaction_view.ex | 29 + .../lib/block_scout_web/views/layout_view.ex | 281 + .../lib/block_scout_web/views/log_view.ex | 3 + .../block_scout_web/views/page_not_found.ex | 5 + .../views/pending_transaction_view.ex | 5 + .../block_scout_web/views/render_helpers.ex | 21 + .../block_scout_web/views/script_helpers.ex | 28 + .../lib/block_scout_web/views/search_view.ex | 19 + .../views/smart_contract_view.ex | 288 + .../lib/block_scout_web/views/tab_helpers.ex | 66 + .../views/tokens/contract_view.ex | 6 + .../block_scout_web/views/tokens/helpers.ex | 133 + .../views/tokens/holder_view.ex | 76 + .../views/tokens/instance/holder_view.ex | 5 + .../views/tokens/instance/metadata_view.ex | 9 + .../views/tokens/instance/overview_view.ex | 199 + .../views/tokens/instance/transfer_view.ex | 5 + .../views/tokens/instance_view.ex | 3 + .../views/tokens/inventory_view.ex | 8 + .../views/tokens/overview_view.ex | 85 + .../views/tokens/transfer_view.ex | 7 + .../lib/block_scout_web/views/tokens_view.ex | 22 + .../transaction_internal_transaction_view.ex | 4 + .../views/transaction_log_view.ex | 11 + .../views/transaction_raw_trace_view.ex | 18 + .../views/transaction_state_view.ex | 48 + .../views/transaction_token_transfer_view.ex | 5 + .../block_scout_web/views/transaction_view.ex | 599 + .../views/verified_contracts_view.ex | 14 + .../lib/block_scout_web/views/wei_helpers.ex | 79 + .../lib/block_scout_web/web_router.ex | 499 + apps/block_scout_web/lib/phoenix/html/safe.ex | 32 + apps/block_scout_web/lib/phoenix/param.ex | 29 + apps/block_scout_web/mix.exs | 167 + apps/block_scout_web/priv/gettext/default.pot | 3426 ++ .../priv/gettext/en/LC_MESSAGES/default.po | 3426 ++ .../priv/gettext/en/LC_MESSAGES/errors.po | 94 + apps/block_scout_web/priv/gettext/errors.pot | 94 + .../test/block_scout_web/chain_test.exs | 82 + .../channels/address_channel_test.exs | 231 + .../channels/block_channel_test.exs | 22 + .../channels/exchange_rate_channel_test.exs | 100 + .../channels/reward_channel_test.exs | 57 + .../channels/transaction_channel_test.exs | 63 + .../account/api/v1/user_controller_test.exs | 919 + .../account/custom_abi_controller_test.exs | 186 + ...ss_coin_balance_by_day_controller_test.exs | 27 + .../address_contract_controller_test.exs | 57 + .../controllers/address_controller_test.exs | 97 + ...s_internal_transaction_controller_test.exs | 405 + .../address_read_contract_controller_test.exs | 130 + .../address_read_proxy_controller_test.exs | 128 + .../address_token_balance_controller_test.exs | 46 + .../address_token_controller_test.exs | 172 + ...address_token_transfer_controller_test.exs | 274 + .../address_transaction_controller_test.exs | 405 + ...address_write_contract_controller_test.exs | 132 + .../address_write_proxy_controller_test.exs | 130 + .../admin/dashboard_controller_test.exs | 26 + .../admin/session_controller_test.exs | 83 + .../admin/setup_controller_test.exs | 109 + .../api/rpc/address_controller_test.exs | 3164 ++ .../api/rpc/block_controller_test.exs | 238 + .../api/rpc/contract_controller_test.exs | 971 + .../api/rpc/eth_controller_test.exs | 540 + .../api/rpc/logs_controller_test.exs | 820 + .../api/rpc/rpc_translator_test.exs | 82 + .../api/rpc/stats_controller_test.exs | 247 + .../api/rpc/token_controller_test.exs | 109 + .../api/rpc/transaction_controller_test.exs | 773 + ...ompiled_smart_contract_controller_test.exs | 121 + .../api/v1/health_controller_test.exs | 94 + .../api/v1/supply_controller_test.exs | 17 + ...erified_smart_contract_controller_test.exs | 82 + .../controllers/api_docs_controller_test.exs | 18 + .../controllers/block_controller_test.exs | 193 + .../block_transaction_controller_test.exs | 204 + .../market_history_chart_controller_test.exs | 24 + ...nsaction_history_chart_controller_test.exs | 57 + .../controllers/chain_controller_test.exs | 241 + .../page_not_found_controller_test.exs | 11 + .../pending_transaction_controller_test.exs | 84 + .../recent_transactions_controller_test.exs | 54 + .../smart_contract_controller_test.exs | 351 + .../tokens/holder_controller_test.exs | 93 + .../instance/transfer_controller_test.exs | 30 + .../tokens/instance_controller_test.exs | 46 + .../tokens/inventory_controller_test.exs | 141 + .../tokens/read_contract_controller_test.exs | 104 + .../tokens/token_controller_test.exs | 39 + .../transaction_controller_test.exs | 137 + ...n_internal_transaction_controller_test.exs | 231 + .../transaction_log_controller_test.exs | 188 + .../transaction_state_controller_test.exs | 187 + ...saction_token_transfer_controller_test.exs | 217 + .../verified_contracts_controller_test.exs | 155 + .../address_contract_verification_test.exs | 70 + .../features/pages/address_contract_page.ex | 23 + .../features/pages/address_page.ex | 178 + .../features/pages/app_page.ex | 19 + .../features/pages/block_list_page.ex | 33 + .../features/pages/block_page.ex | 50 + .../features/pages/chain_page.ex | 48 + .../features/pages/contract_verify_page.ex | 58 + .../features/pages/token_page.ex | 27 + .../features/pages/transaction_list_page.ex | 33 + .../features/pages/transaction_logs_page.ex | 23 + .../features/pages/transaction_page.ex | 25 + .../features/viewing_addresses_test.exs | 496 + .../features/viewing_app_test.exs | 144 + .../features/viewing_blocks_test.exs | 185 + .../features/viewing_chain_test.exs | 160 + .../features/viewing_tokens_test.exs | 21 + .../features/viewing_transactions_test.exs | 163 + .../models/user_from_auth_test.exs | 107 + .../admin/check_owner_registered_test.exs | 27 + .../plug/admin/require_admin_role_test.exs | 42 + .../plug/fetch_user_from_session_test.exs | 46 + .../schema/query/address_test.exs | 594 + .../schema/query/addresses_test.exs | 185 + .../schema/query/block_test.exs | 108 + .../schema/query/node_test.exs | 209 + .../schema/query/token_transfers_test.exs | 326 + .../schema/query/transaction_test.exs | 552 + .../subscription/token_transfers_test.exs | 117 + .../block_scout_web/social_media_test.exs | 25 + .../views/abi_encoded_value_view_test.exs | 127 + .../views/address_coin_balance_view_test.exs | 62 + .../views/address_contract_view_test.exs | 79 + .../address_decompiled_contract_view_test.exs | 110 + .../views/address_token_balance_view_test.exs | 182 + .../views/address_transaction_view_test.exs | 5 + .../views/address_view_test.exs | 393 + .../views/api_docs_view_test.exs | 117 + .../block_scout_web/views/block_view_test.exs | 98 + .../views/currency_helpers_test.exs | 84 + .../views/error_helpers_test.exs | 42 + .../block_scout_web/views/error_view_test.exs | 22 + .../views/internal_transaction_view_test.exs | 40 + .../views/layout_view_test.exs | 203 + .../views/render_helpers_test.exs | 22 + .../views/search_view_test.exs | 44 + .../views/smart_contract_view_test.exs | 65 + .../views/tab_helpers_test.exs | 57 + .../views/tokens/helpers_test.exs | 72 + .../views/tokens/holder_view_test.exs | 74 + .../tokens/instance/overview_view_test.exs | 122 + .../views/tokens/overview_view_test.exs | 167 + .../views/tokens/read_contract_view_test.exs | 3 + .../views/tokens/smart_contract_view_test.exs | 141 + .../views/tokens/transfer_view_test.exs | 3 + .../views/transaction_view_test.exs | 289 + .../views/wei_helpers_test.exs | 8 + .../param/explorer/chain/block_test.exs | 17 + .../test/support/channel_case.ex | 41 + .../block_scout_web/test/support/conn_case.ex | 54 + .../test/support/feature_case.ex | 40 + .../smart_contract/compiler_tests.json | 13 + .../smart_contract/contract_with_lib.json | 14 + .../fixture/smart_contract/solc_bin.json | 7894 ++++ .../test/support/subscription_case.ex | 27 + apps/block_scout_web/test/test_helper.exs | 34 + apps/ethereum_jsonrpc/.gitignore | 24 + apps/ethereum_jsonrpc/README.md | 85 + apps/ethereum_jsonrpc/config/config.exs | 35 + apps/ethereum_jsonrpc/config/dev.exs | 7 + apps/ethereum_jsonrpc/config/prod.exs | 8 + apps/ethereum_jsonrpc/config/runtime/test.exs | 8 + apps/ethereum_jsonrpc/config/test.exs | 23 + apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex | 506 + .../lib/ethereum_jsonrpc/application.ex | 63 + .../lib/ethereum_jsonrpc/arbitrum.ex | 47 + .../lib/ethereum_jsonrpc/besu.ex | 61 + .../lib/ethereum_jsonrpc/besu/trace.ex | 47 + .../lib/ethereum_jsonrpc/besu/traces.ex | 16 + .../lib/ethereum_jsonrpc/block.ex | 615 + .../lib/ethereum_jsonrpc/block/by_hash.ex | 11 + .../lib/ethereum_jsonrpc/block/by_nephew.ex | 15 + .../lib/ethereum_jsonrpc/block/by_number.ex | 11 + .../lib/ethereum_jsonrpc/block/by_tag.ex | 23 + .../lib/ethereum_jsonrpc/blocks.ex | 338 + .../lib/ethereum_jsonrpc/contract.ex | 238 + .../lib/ethereum_jsonrpc/decode_error.ex | 75 + .../lib/ethereum_jsonrpc/encoder.ex | 120 + .../lib/ethereum_jsonrpc/erigon.ex | 56 + .../lib/ethereum_jsonrpc/fetched_balance.ex | 62 + .../lib/ethereum_jsonrpc/fetched_balances.ex | 49 + .../ethereum_jsonrpc/fetched_beneficiaries.ex | 195 + .../ethereum_jsonrpc/fetched_beneficiary.ex | 17 + .../lib/ethereum_jsonrpc/fetched_code.ex | 50 + .../lib/ethereum_jsonrpc/fetched_codes.ex | 49 + .../lib/ethereum_jsonrpc/ganache.ex | 47 + .../lib/ethereum_jsonrpc/geth.ex | 249 + .../lib/ethereum_jsonrpc/geth/call.ex | 512 + .../lib/ethereum_jsonrpc/geth/calls.ex | 239 + .../lib/ethereum_jsonrpc/geth/tracer.ex | 284 + .../lib/ethereum_jsonrpc/http.ex | 182 + .../lib/ethereum_jsonrpc/http/httpoison.ex | 22 + .../lib/ethereum_jsonrpc/ipc.ex | 94 + .../lib/ethereum_jsonrpc/log.ex | 199 + .../lib/ethereum_jsonrpc/logs.ex | 21 + .../lib/ethereum_jsonrpc/nethermind.ex | 65 + .../lib/ethereum_jsonrpc/nethermind/trace.ex | 501 + .../nethermind/trace/action.ex | 54 + .../nethermind/trace/result.ex | 42 + .../lib/ethereum_jsonrpc/nethermind/traces.ex | 16 + .../ethereum_jsonrpc/pending_transaction.ex | 71 + .../lib/ethereum_jsonrpc/receipt.ex | 315 + .../lib/ethereum_jsonrpc/receipts.ex | 253 + .../ethereum_jsonrpc/request_coordinator.ex | 188 + .../lib/ethereum_jsonrpc/rolling_window.ex | 226 + .../lib/ethereum_jsonrpc/subscription.ex | 64 + .../trace_replay_block_transactions.ex | 250 + .../lib/ethereum_jsonrpc/tracer.ex | 5 + .../lib/ethereum_jsonrpc/transaction.ex | 491 + .../lib/ethereum_jsonrpc/transactions.ex | 159 + .../lib/ethereum_jsonrpc/transport.ex | 126 + .../lib/ethereum_jsonrpc/uncle.ex | 42 + .../lib/ethereum_jsonrpc/uncles.ex | 37 + .../lib/ethereum_jsonrpc/variant.ex | 115 + .../lib/ethereum_jsonrpc/web_socket.ex | 115 + .../web_socket/registration.ex | 81 + .../web_socket/web_socket_client.ex | 573 + .../web_socket/web_socket_client/options.ex | 17 + apps/ethereum_jsonrpc/lib/rsk.ex | 13 + apps/ethereum_jsonrpc/mix.exs | 91 + .../geth/debug_traceTransaction/tracer.js | 466 + .../test/ethereum_jsonrpc/block_test.exs | 65 + .../test/ethereum_jsonrpc/blocks_test.exs | 5 + .../test/ethereum_jsonrpc/contract_test.exs | 138 + .../test/ethereum_jsonrpc/encoder_test.exs | 139 + .../fetched_beneficiaries_test.exs | 260 + .../test/ethereum_jsonrpc/geth/call_test.exs | 48 + .../ethereum_jsonrpc/geth/calls_tests.exs | 5 + .../test/ethereum_jsonrpc/geth_test.exs | 216 + .../test/ethereum_jsonrpc/http/mox_test.exs | 307 + .../test/ethereum_jsonrpc/log_test.exs | 32 + .../test/ethereum_jsonrpc/mox_test.exs | 31 + .../nethermind/trace/action_test.exs | 5 + .../nethermind/trace/result_test.exs | 5 + .../nethermind/trace_test.exs | 5 + .../test/ethereum_jsonrpc/nethermind_test.exs | 616 + .../test/ethereum_jsonrpc/receipt_test.exs | 40 + .../test/ethereum_jsonrpc/receipts_test.exs | 180 + .../request_coordinator_test.exs | 60 + .../ethereum_jsonrpc/rolling_window_test.exs | 119 + .../ethereum_jsonrpc/transaction_test.exs | 15 + .../ethereum_jsonrpc/transactions_test.exs | 45 + .../test/ethereum_jsonrpc/uncle_test.exs | 5 + .../test/ethereum_jsonrpc/uncles_test.exs | 5 + .../web_socket/web_socket_client_test.exs | 171 + .../test/ethereum_jsonrpc/web_socket_test.exs | 231 + .../test/ethereum_jsonrpc_test.exs | 977 + .../test/support/ethereum_jsonrpc/case.ex | 56 + .../case/geth/http_websocket.ex | 20 + .../support/ethereum_jsonrpc/case/geth/mox.ex | 13 + .../case/nethermind/http_websocket.ex | 20 + .../ethereum_jsonrpc/case/nethermind/mox.ex | 17 + .../support/ethereum_jsonrpc/http/case.ex | 32 + .../ethereum_jsonrpc/web_socket/case.ex | 9 + .../ethereum_jsonrpc/web_socket/case/geth.ex | 25 + .../ethereum_jsonrpc/web_socket/case/mox.ex | 76 + .../web_socket/case/nethermind.ex | 25 + .../web_socket/cowboy/websocket_handler.ex | 67 + apps/ethereum_jsonrpc/test/test_helper.exs | 16 + apps/explorer/.gitignore | 3 + apps/explorer/.sobelow-conf | 12 + apps/explorer/README.md | 38 + .../recent_collated_transactions.benchee | Bin 0 -> 4301269 bytes .../chain/recent_collated_transactions.exs | 56 + apps/explorer/config/config.exs | 139 + apps/explorer/config/dev.exs | 28 + apps/explorer/config/dev/arbitrum.exs | 20 + apps/explorer/config/dev/besu.exs | 25 + apps/explorer/config/dev/erigon.exs | 25 + apps/explorer/config/dev/ganache.exs | 20 + apps/explorer/config/dev/geth.exs | 20 + apps/explorer/config/dev/nethermind.exs | 25 + apps/explorer/config/dev/rsk.exs | 25 + apps/explorer/config/prod.exs | 36 + apps/explorer/config/prod/arbitrum.exs | 20 + apps/explorer/config/prod/besu.exs | 25 + apps/explorer/config/prod/erigon.exs | 25 + apps/explorer/config/prod/ganache.exs | 20 + apps/explorer/config/prod/geth.exs | 20 + apps/explorer/config/prod/nethermind.exs | 25 + apps/explorer/config/prod/rsk.exs | 25 + apps/explorer/config/runtime/test.exs | 36 + apps/explorer/config/test.exs | 42 + apps/explorer/config/test/arbitrum.exs | 13 + apps/explorer/config/test/besu.exs | 14 + apps/explorer/config/test/erigon.exs | 14 + apps/explorer/config/test/ganache.exs | 13 + apps/explorer/config/test/geth.exs | 13 + apps/explorer/config/test/nethermind.exs | 14 + apps/explorer/config/test/rsk.exs | 14 + apps/explorer/coveralls.json | 5 + apps/explorer/lib/encrypt.ex | 116 + apps/explorer/lib/explorer.ex | 22 + apps/explorer/lib/explorer/account.ex | 9 + apps/explorer/lib/explorer/account/api/key.ex | 129 + .../explorer/lib/explorer/account/api/plan.ex | 13 + .../lib/explorer/account/custom_abi.ex | 226 + .../explorer/lib/explorer/account/identity.ex | 41 + .../lib/explorer/account/notifier/email.ex | 155 + .../account/notifier/forbidden_address.ex | 67 + .../lib/explorer/account/notifier/notify.ex | 153 + .../lib/explorer/account/notifier/summary.ex | 227 + apps/explorer/lib/explorer/account/notify.ex | 44 + .../explorer/account/public_tags_request.ex | 255 + .../lib/explorer/account/tag_address.ex | 156 + .../lib/explorer/account/tag_transaction.ex | 161 + .../lib/explorer/account/watchlist.ex | 27 + .../lib/explorer/account/watchlist_address.ex | 176 + .../account/watchlist_notification.ex | 65 + .../lib/explorer/accounts/accounts.ex | 93 + apps/explorer/lib/explorer/accounts/user.ex | 50 + .../explorer/accounts/user/authenticate.ex | 22 + .../explorer/accounts/user/registration.ex | 31 + .../lib/explorer/accounts/user_contact.ex | 57 + apps/explorer/lib/explorer/admin.ex | 76 + .../lib/explorer/admin/administrator.ex | 41 + apps/explorer/lib/explorer/admin/recovery.ex | 67 + apps/explorer/lib/explorer/admin/role.ex | 32 + apps/explorer/lib/explorer/application.ex | 189 + apps/explorer/lib/explorer/chain.ex | 6630 ++++ apps/explorer/lib/explorer/chain/address.ex | 292 + .../explorer/chain/address/coin_balance.ex | 193 + .../chain/address/coin_balance_daily.ex | 76 + .../chain/address/current_token_balance.ex | 259 + .../lib/explorer/chain/address/name.ex | 57 + .../lib/explorer/chain/address/token.ex | 120 + .../explorer/chain/address/token_balance.ex | 107 + ...dress_internal_transaction_csv_exporter.ex | 108 + .../chain/address_log_csv_exporter.ex | 84 + .../address_token_transfer_csv_exporter.ex | 102 + .../chain/address_transaction_csv_exporter.ex | 144 + apps/explorer/lib/explorer/chain/block.ex | 156 + .../explorer/chain/block/emission_reward.ex | 33 + .../lib/explorer/chain/block/range.ex | 190 + .../lib/explorer/chain/block/reward.ex | 236 + .../chain/block/reward/address_type.ex | 103 + .../chain/block/second_degree_relation.ex | 68 + .../lib/explorer/chain/cache/accounts.ex | 73 + .../lib/explorer/chain/cache/address_sum.ex | 53 + .../chain/cache/address_sum_minus_burnt.ex | 60 + .../lib/explorer/chain/cache/block.ex | 100 + .../lib/explorer/chain/cache/block_number.ex | 38 + .../lib/explorer/chain/cache/blocks.ex | 41 + .../explorer/chain/cache/contracts_counter.ex | 96 + .../explorer/chain/cache/gas_price_oracle.ex | 167 + .../lib/explorer/chain/cache/gas_usage.ex | 99 + .../explorer/chain/cache/min_missing_block.ex | 52 + .../lib/explorer/chain/cache/net_version.ex | 27 + .../chain/cache/new_contracts_counter.ex | 97 + .../cache/new_verified_contracts_counter.ex | 97 + .../chain/cache/token_exchange_rate.ex | 166 + .../lib/explorer/chain/cache/transaction.ex | 86 + .../lib/explorer/chain/cache/transactions.ex | 30 + .../lib/explorer/chain/cache/uncles.ex | 48 + .../chain/cache/verified_contracts_counter.ex | 96 + .../lib/explorer/chain/contract_method.ex | 94 + .../lib/explorer/chain/currency_helpers.ex | 12 + apps/explorer/lib/explorer/chain/data.ex | 404 + .../chain/decompiled_smart_contract.ex | 33 + .../lib/explorer/chain/events/db_sender.ex | 36 + .../lib/explorer/chain/events/listener.ex | 76 + .../lib/explorer/chain/events/publisher.ex | 38 + .../explorer/chain/events/simple_sender.ex | 21 + .../lib/explorer/chain/events/subscriber.ex | 48 + apps/explorer/lib/explorer/chain/gas.ex | 10 + apps/explorer/lib/explorer/chain/hash.ex | 246 + .../lib/explorer/chain/hash/address.ex | 270 + apps/explorer/lib/explorer/chain/hash/full.ex | 150 + .../explorer/lib/explorer/chain/hash/nonce.ex | 146 + apps/explorer/lib/explorer/chain/import.ex | 356 + .../lib/explorer/chain/import/runner.ex | 61 + .../import/runner/address/coin_balances.ex | 140 + .../runner/address/coin_balances_daily.ex | 145 + .../runner/address/current_token_balances.ex | 371 + .../import/runner/address/token_balances.ex | 196 + .../explorer/chain/import/runner/addresses.ex | 211 + .../chain/import/runner/block/rewards.ex | 84 + .../runner/block/second_degree_relations.ex | 104 + .../explorer/chain/import/runner/blocks.ex | 742 + .../import/runner/internal_transactions.ex | 731 + .../lib/explorer/chain/import/runner/logs.ex | 114 + .../chain/import/runner/token_transfers.ex | 108 + .../explorer/chain/import/runner/tokens.ex | 231 + .../chain/import/runner/transaction/forks.ex | 93 + .../chain/import/runner/transactions.ex | 235 + .../lib/explorer/chain/import/stage.ex | 70 + .../chain/import/stage/address_referencing.ex | 26 + .../explorer/chain/import/stage/addresses.ex | 22 + .../chain/import/stage/block_following.ex | 28 + .../chain/import/stage/block_pending.ex | 27 + .../chain/import/stage/block_referencing.ex | 30 + .../explorer/chain/internal_transaction.ex | 740 + .../chain/internal_transaction/action.ex | 42 + .../chain/internal_transaction/call_type.ex | 116 + .../chain/internal_transaction/result.ex | 32 + .../chain/internal_transaction/type.ex | 144 + apps/explorer/lib/explorer/chain/log.ex | 250 + apps/explorer/lib/explorer/chain/map_cache.ex | 217 + .../lib/explorer/chain/method_identifier.ex | 36 + .../lib/explorer/chain/ordered_cache.ex | 354 + .../explorer/chain/pending_block_operation.ex | 87 + .../lib/explorer/chain/smart_contract.ex | 488 + .../chain/smart_contract/external_library.ex | 12 + .../smart_contract/verification_status.ex | 133 + .../smart_contract_additional_sources.ex | 63 + apps/explorer/lib/explorer/chain/supply.ex | 32 + .../explorer/chain/supply/exchange_rate.ex | 22 + .../chain/supply/proof_of_authority.ex | 57 + .../explorer/lib/explorer/chain/supply/rsk.ex | 150 + apps/explorer/lib/explorer/chain/token.ex | 144 + .../lib/explorer/chain/token/instance.ex | 49 + .../lib/explorer/chain/token_transfer.ex | 330 + .../lib/explorer/chain/transaction.ex | 808 + .../lib/explorer/chain/transaction/fork.ex | 63 + .../chain/transaction/history/historian.ex | 148 + .../transaction/history/transaction_stats.ex | 49 + .../lib/explorer/chain/transaction/status.ex | 109 + apps/explorer/lib/explorer/chain/wei.ex | 275 + .../lib/explorer/chain_spec/genesis_data.ex | 120 + .../lib/explorer/chain_spec/geth/importer.ex | 93 + .../explorer/chain_spec/parity/importer.ex | 188 + .../lib/explorer/chain_spec/poa/importer.ex | 111 + .../counters/address_gas_usage_counter.ex | 100 + .../address_token_transfers_counter.ex | 98 + .../counters/address_tokens_usd_sum.ex | 101 + .../counters/address_transactions_counter.ex | 101 + .../explorer/counters/addresses_counter.ex | 125 + .../addresses_with_balance_counter.ex | 125 + .../explorer/counters/average_block_time.ex | 138 + .../average_block_time_duration_format.ex | 104 + .../counters/block_burned_fee_counter.ex | 81 + .../counters/block_priority_fee_counter.ex | 81 + apps/explorer/lib/explorer/counters/helper.ex | 48 + .../explorer/counters/last_fetched_counter.ex | 30 + .../counters/token_holders_counter.ex | 93 + .../counters/token_transfers_counter.ex | 90 + .../lib/explorer/custom_contracts_helpers.ex | 22 + .../lib/explorer/encrypted/address_hash.ex | 5 + .../explorer/lib/explorer/encrypted/binary.ex | 5 + .../explorer/encrypted/transaction_hash.ex | 5 + .../explorer/encrypted/types/address_hash.ex | 27 + .../encrypted/types/transaction_hash.ex | 27 + .../lib/explorer/env_var_translator.ex | 24 + apps/explorer/lib/explorer/eth_rpc.ex | 452 + apps/explorer/lib/explorer/etherscan.ex | 620 + .../lib/explorer/etherscan/addresses.ex | 28 + .../explorer/lib/explorer/etherscan/blocks.ex | 95 + .../lib/explorer/etherscan/contracts.ex | 217 + apps/explorer/lib/explorer/etherscan/logs.ex | 284 + apps/explorer/lib/explorer/etherscan/rpc.ex | 40 + .../explorer/exchange_rates/exchange_rates.ex | 145 + .../lib/explorer/exchange_rates/source.ex | 148 + .../exchange_rates/source/coin_gecko.ex | 226 + .../exchange_rates/source/coin_market_cap.ex | 192 + .../lib/explorer/exchange_rates/token.ex | 87 + apps/explorer/lib/explorer/graphql.ex | 100 + .../lib/explorer/history/historian.ex | 44 + apps/explorer/lib/explorer/history/process.ex | 102 + .../lib/explorer/known_tokens/known_tokens.ex | 147 + .../lib/explorer/known_tokens/source.ex | 33 + .../known_tokens/source/my_ether_wallet.ex | 19 + apps/explorer/lib/explorer/logger.ex | 19 + apps/explorer/lib/explorer/mailer.ex | 12 + .../lib/explorer/market/history/cataloger.ex | 113 + .../lib/explorer/market/history/source.ex | 19 + .../market/history/source/crypto_compare.ex | 81 + apps/explorer/lib/explorer/market/market.ex | 95 + .../lib/explorer/market/market_history.ex | 26 + .../explorer/market/market_history_cache.ex | 78 + apps/explorer/lib/explorer/paging_options.ex | 24 + .../lib/explorer/prometheus/instrumenter.ex | 23 + apps/explorer/lib/explorer/repo.ex | 181 + .../lib/explorer/repo/config_helper.ex | 80 + .../lib/explorer/repo/prometheus_logger.ex | 9 + apps/explorer/lib/explorer/schema.ex | 13 + .../smart_contract/compiler_version.ex | 209 + .../lib/explorer/smart_contract/helper.ex | 104 + .../lib/explorer/smart_contract/reader.ex | 728 + .../smart_contract/rust_verifier_interface.ex | 151 + .../smart_contract/solc_downloader.ex | 99 + .../smart_contract/solidity/code_compiler.ex | 324 + .../smart_contract/solidity/publisher.ex | 279 + .../solidity/publisher_worker.ex | 63 + .../smart_contract/solidity/verifier.ex | 535 + .../smart_contract/vyper/code_compiler.ex | 74 + .../smart_contract/vyper/publisher.ex | 95 + .../smart_contract/vyper/publisher_worker.ex | 23 + .../explorer/smart_contract/vyper/verifier.ex | 100 + .../smart_contract/vyper_downloader.ex | 127 + .../lib/explorer/smart_contract/writer.ex | 52 + .../explorer/lib/explorer/tags/address_tag.ex | 98 + .../explorer/tags/address_tag_cataloger.ex | 198 + .../lib/explorer/tags/address_to_tag.ex | 145 + .../third_party_integrations/airtable.ex | 56 + .../third_party_integrations/sourcify.ex | 388 + .../lib/explorer/token/balance_reader.ex | 111 + .../token/instance_metadata_retriever.ex | 297 + .../explorer/token/instance_owner_reader.ex | 76 + .../lib/explorer/token/metadata_retriever.ex | 351 + apps/explorer/lib/explorer/tracer.ex | 5 + .../explorer/utility/event_notification.ex | 20 + .../explorer/validator/metadata_importer.ex | 44 + .../explorer/validator/metadata_processor.ex | 31 + .../explorer/validator/metadata_retriever.ex | 88 + apps/explorer/lib/explorer/vault.ex | 22 + apps/explorer/lib/release_tasks.ex | 116 + apps/explorer/mix.exs | 150 + apps/explorer/package-lock.json | 169 + apps/explorer/package.json | 18 + ...211031164954_create_account_identities.exs | 13 + ...211105114502_create_account_watchlists.exs | 14 + ...907_create_account_watchlist_addresses.exs | 28 + ...create_account_watchlist_notifications.exs | 31 + ...add_email_and_name_to_account_identity.exs | 10 + ...212222222_create_account_tag_addresses.exs | 17 + ...133333_create_account_tag_transactions.exs | 17 + ...add_subject_to_watchlist_notifications.exs | 9 + ...07134152_add_api_keys_and_plans_tables.exs | 33 + .../20220510094118_add_custom_abis_table.exs | 18 + ...94836_add_account_public_tags_requests.exs | 23 + ...0620182600_add_account_identity_fields.exs | 10 + .../20220624142547_add_unique_constraints.exs | 9 + ...migrate_public_tags_addresses_to_array.exs | 34 + .../20220706114430_encrypt_account_data.exs | 60 + ...220706153506_remove_unencrypted_fields.exs | 79 + .../20220706211444_set_new_indexes.exs | 43 + .../20220905195203_remove_guardian_tokens.exs | 7 + apps/explorer/priv/compile_solc.js | 54 + .../priv/compile_solc_standard_json_input.js | 15 + .../priv/contracts_abi/poa/metadata.json | 579 + .../priv/contracts_abi/poa/validators.json | 462 + .../contracts_abi/posdao/BlockRewardAuRa.json | 636 + .../priv/contracts_abi/posdao/README.md | 1 + .../contracts_abi/posdao/StakingAuRa.json | 1127 + .../priv/contracts_abi/posdao/Token.json | 968 + .../posdao/ValidatorSetAuRa.json | 734 + apps/explorer/priv/repo/migrations/.gitkeep | 0 .../20180117221921_create_address.exs | 14 + .../20180117221922_create_blocks.exs | 29 + .../20180117221923_create_transactions.exs | 225 + .../migrations/20180212222309_create_logs.exs | 39 + ...221001948_create_internal_transactions.exs | 82 + .../20180424203101_create_market_history.exs | 13 + .../20180508183700_create_users.exs | 14 + .../20180508191045_create_user_contacts.exs | 22 + ...8221256_create_smart_contract_verified.exs | 19 + ...0522154252_create_btree_gist_extension.exs | 7 + .../20180522154253_create_block_rewards.exs | 12 + .../20180606135149_create_tokens.exs | 26 + .../20180606135150_create_token_transfers.exs | 32 + ...143840_add_inserted_at_index_to_blocks.exs | 7 + ...717204948_create_address_coin_balances.exs | 27 + ...17021704_create_address_token_balances.exs | 32 + .../20180821142139_create_address_names.exs | 33 + ...9_create_block_second_degree_relations.exs | 22 + ...20180918200001_create_transaction_fork.exs | 16 + ...9175123_alter_token_decimals_to_bigint.exs | 15 + .../20181008195723_create_administrators.exs | 15 + ...12_add_fields_to_internal_transactions.exs | 17 + ...5173318_add_case_insensitive_extension.exs | 10 + .../20181015173319_modify_users_username.exs | 23 + ...81016163236_modify_user_contacts_email.exs | 23 + ...dd_index_to_internal_transaction_table.exs | 17 + ...nal_transactions_composite_primary_key.exs | 22 + ...81024164623_logs_composite_primary_key.exs | 22 + ..._token_transfers_composite_primary_key.exs | 22 + ..._create_address_current_token_balances.exs | 32 + ..._table_decimals_from_bigint_to_numeric.exs | 17 + .../20181106152300_add_nonce_to_addresses.exs | 26 + .../repo/migrations/20181107164103_eip6.exs | 40 + ...ional_internal_transaction_constraints.exs | 45 + ...16_add_block_number_to_token_transfers.exs | 18 + .../20181126203826_add_index_to_addresses.exs | 13 + ...name_block_rewards_to_emission_rewards.exs | 7 + ...0181206200312_create_new_block_rewards.exs | 16 + ...2115448_add_indexes_to_token_transfers.exs | 8 + ...56_add_metadata_field_to_address_names.exs | 9 + ...1143000_create_blocks_miner_hash_index.exs | 7 + .../20181221145054_add_contract_methods.exs | 15 + ...0102141900_index_current_token_holders.exs | 11 + ...20190114204640_add_tokens_holder_count.exs | 10 + ...ed_contract_indexed_at_to_transactions.exs | 12 + ...190118040301_create_tokens_primary_key.exs | 12 + ...degree_relations_composite_primary_key.exs | 12 + ...15_change_transaction_error_constraint.exs | 88 + ...ransaction_nonce_and_from_address_hash.exs | 8 + ...0208113202_add_unique_index_to_rewards.exs | 12 + ...address_hash_for_current_token_balance.exs | 7 + ...liest_processing_start_to_transactions.exs | 9 + ...block_number_to_address_token_balances.exs | 7 + ...x_on_fetched_coin_balance_to_addresses.exs | 7 + ..._compound_index_address_token_balances.exs | 7 + ...nstructor_arguments_to_smart_contracts.exs | 11 + ...636_add_indexes_for_block_reward_query.exs | 8 + ...dd_index_created_contract_address_hash.exs | 7 + ...3_change_constructor_arguments_to_text.exs | 15 + ...rnal_transactions_indexed_at_to_blocks.exs | 10 + ...190301095620_remove_duplicated_indexes.exs | 32 + .../20190301120328_add_index_to_consensus.exs | 7 + ...05095926_add_index_to_value_fetched_at.exs | 7 + ...90313085740_add_index_symobl_in_tokens.exs | 7 + ...3912_change_transactions_v_column_type.exs | 15 + ...314084907_add_index_to_to_address_hash.exs | 7 + ...1809_add_inserted_at_index_to_accounts.exs | 7 + ...1821_create_decompiled_smart_contracts.exs | 15 + ...d_old_value_for_current_token_balances.exs | 10 + ...ique_address_hash_decompiled_contracts.exs | 11 + ...0403080447_add_full_text_search_tokens.exs | 16 + .../20190421143300_add_index_to_bsdr.exs | 10 + ...24170833_change_block_size_to_nullable.exs | 15 + ...22_add_old_block_hash_for_transactions.exs | 12 + ...0513134025_add_refetch_needed_to_block.exs | 11 + ...sh_index_to_decompiled_smart_contracts.exs | 14 + ...ompiled_and_verified_flag_to_addresses.exs | 18 + .../20190521104412_create_staking_pools.exs | 27 + ...112839_create_staking_pools_delegators.exs | 26 + ...13065856_add_tx_hash_inserted_at_index.exs | 7 + ...3_reduce_transaction_status_constraint.exs | 132 + ...5085852_add_additional_contract_fields.exs | 11 + ...0190709043832_create_transaction_stats.exs | 12 + ..._external_libraries_to_smart_contracts.exs | 13 + ...0190807111216_remove_duplicate_indexes.exs | 17 + ...0190807113117_create_suggested_indexes.exs | 8 + ...4_add_index_on_token_transfer_token_id.exs | 7 + .../20190905083522_create_token_instances.exs | 22 + ...er_in_token_transfers_and_transactions.exs | 7 + ..._add_indexes_for_token_instances_query.exs | 8 + ...635_add_token_transfer_sorting_indexes.exs | 12 + ...010075740_add_error_to_token_instances.exs | 11 + ...120546_create_pending_block_operations.exs | 14 + ...054_add_pending_internal_txs_operation.exs | 79 + ...add_block_hash_and_block_index_to_logs.exs | 43 + ...2035_add_block_hash_to_token_transfers.exs | 40 + ...emove_duplicate_indexes_token_entities.exs | 9 + ...transactions_add_to_address_hash_index.exs | 25 + ...block_rewards_block_hash_partial_index.exs | 9 + ...18120138_logs_block_number_index_index.exs | 7 + ...ck_operations_block_hash_partial_index.exs | 9 + ...4152058_add_token_id_to_token_balances.exs | 12 + ...nces_token_contract_address_hash_value.exs | 7 + ...ken_contract_address_hash_block_number.exs | 7 + .../migrations/20200421102450_pending_txs.exs | 16 + ...ess_hash_address_type_block_hash_index.exs | 11 + ...e_index_blocks_miner_hash_number_index.exs | 7 + ...20200521090250_recreate_staking_tables.exs | 101 + ...dress_hash_token_id_block_number_index.exs | 7 + ...0525115811_address_coin_balances_daily.exs | 17 + .../20200527144742_add_counters_table.exs | 12 + ...22_alter_transactions_add_error_reason.exs | 9 + ...0200806125649_token_add_bridged_column.exs | 9 + .../20200807064700_bridged_tokens_table.exs | 20 + ...3050_add_addresses_contract_code_index.exs | 7 + ...5501_add_bridged_token_custom_metadata.exs | 9 + .../20200929075625_add_bridged_token_type.exs | 9 + ...transactions_stat_add_gas_usage_column.exs | 9 + .../20201214203532_support_sourcify.exs | 22 + .../migrations/20210219080523_add_tags.exs | 24 + ...exchange_rate_column_to_bridged_tokens.exs | 9 + ...309104122_add_bridged_token_custom_cap.exs | 10 + ...210331074008_add_pool_name_description.exs | 10 + ...add_token_id_to_current_token_balances.exs | 12 + ..._balances_add_token_id_to_unique_index.exs | 25 + ..._balances_add_token_id_to_unique_index.exs | 25 + ..._unfetched_token_balances_unique_index.exs | 32 + ...108_extend_token_transfers_for_erc1155.exs | 10 + ...0210524165427_min_missing_block_number.exs | 12 + ...transaction_stats_add_total_fee_column.exs | 9 + ...0552_smart_contracts_add_is_vyper_flag.exs | 9 + .../20210701084814_support_partial_match.exs | 9 + .../20210811140837_add_1559_support.exs | 15 + ...3144531_tokens_add_metadata_fetch_flag.exs | 9 + ...4_add_file_path_for_sourcify_contracts.exs | 9 + ...20211006121008_add_block_is_empty_flag.exs | 11 + ...e_duplicates_of_current_token_balances.exs | 55 + ...5545_migrate_optimization_runs_to_int8.exs | 15 + .../20211018072347_add_is_empty_index.exs | 17 + ...n_balances_contract_address_hash_index.exs | 12 + ...164843_transactions_block_number_index.exs | 12 + ...ntract_address_hash_block_number_index.exs | 12 + ...gs_address_hash_transaction_hash_index.exs | 12 + ...block_rewards_block_hash_partial_index.exs | 14 + ...64817_add_check_for_bytecode_actuality.exs | 13 + ...add_contract_verification_status_table.exs | 13 + .../20211204184037_address_add_gas_used.exs | 9 + ...6071033_modify_address_gas_used_bigint.exs | 15 + ...184136_add_display_name_to_address_tag.exs | 15 + ...or_in_iternal_txs_field_to_transaction.exs | 9 + .../20220111085751_address_add_counters.exs | 10 + ...3252_smart_contracts_contract_code_md5.exs | 23 + ...20220306091504_add_implementation_name.exs | 9 + .../20220622114402_remove_staking_tables.exs | 13 + .../20220622140604_remove_bridged_tokens.exs | 11 + ...ss_coin_balances_daily_add_primary_key.exs | 17 + ..._address_coin_balances_add_primary_key.exs | 17 + ...504_transactions_forks_add_primary_key.exs | 17 + ...06102746_block_rewards_add_primary_key.exs | 18 + ...05925_emission_rewards_add_primary_key.exs | 16 + ...06111510_address_names_add_primary_key.exs | 9 + ...20804114005_create_event_notifications.exs | 9 + .../20220902083436_extend_token_name_type.exs | 9 + ...create_index_token_transfers_token_ids.exs | 13 + ...s_token_contract_address_hash_token_id.exs | 8 + .../20220919105140_add_method_id_index.exs | 15 + ...0220926122620_extend_token_symbol_type.exs | 9 + .../scripts/20181107164103_eip6.sql | 64 + ...ional_internal_transaction_constraints.sql | 88 + ...ional_internal_transaction_constraints.sql | 82 + ...ansfers_update_block_number_in_batches.sql | 39 + .../20181126182700_migrate_address_nonce.sql | 55 + ...2921_lose_consensus_for_invalid_blocks.sql | 6 + ...ock_hash_block_index_dups_to_carnitine.sql | 73 + ...ress_current_token_balances_in_batches.sql | 133 + ...internal_transaction_update_in_batches.sql | 59 + .../scripts/transaction_update_in_batches.sql | 55 + ...ress_current_token_balances_in_batches.sql | 125 + ...ate_new_tokens_holder_count_in_batches.sql | 95 + .../scripts/update_replaced_transaction.sql | 92 + apps/explorer/priv/repo/seeds.exs | 11 + .../priv/repo_api/migrations/.gitkeep | 0 .../explorer/account/notify/email_test.exs | 124 + .../explorer/account/notify/notify_test.exs | 91 + .../explorer/account/notify/summary_test.exs | 273 + .../test/explorer/accounts/accounts_test.exs | 126 + .../explorer/accounts/user_contact_test.exs | 21 + .../test/explorer/accounts/user_test.exs | 30 + .../admin/administrator/role_test.exs | 43 + .../test/explorer/admin/recovery_test.exs | 74 + apps/explorer/test/explorer/admin_test.exs | 57 + .../chain/address/coin_balance_test.exs | 301 + .../address/current_token_balance_test.exs | 203 + .../chain/address/token_balance_test.exs | 122 + .../explorer/chain/address/token_test.exs | 330 + ...internal_transaction_csv_exporter_test.exs | 189 + .../chain/address_log_csv_exporter_test.exs | 117 + .../test/explorer/chain/address_test.exs | 64 + ...dress_token_transfer_csv_exporter_test.exs | 121 + .../address_transaction_csv_exporter_test.exs | 127 + .../test/explorer/chain/block/range_test.exs | 110 + .../block/second_degree_relation_test.exs | 56 + .../test/explorer/chain/block_test.exs | 61 + .../explorer/chain/cache/accounts_test.exs | 42 + .../chain/cache/address_sum_minus_burnt.exs | 58 + .../explorer/chain/cache/address_sum_test.exs | 57 + .../chain/cache/block_number_test.exs | 73 + .../test/explorer/chain/cache/block_test.exs | 56 + .../test/explorer/chain/cache/blocks_test.exs | 61 + .../chain/cache/contracts_counter_test.exs | 18 + .../chain/cache/gas_price_oracle_test.exs | 221 + .../cache/new_contracts_counter_test.exs | 29 + .../new_verified_contracts_counter_test.exs | 18 + .../explorer/chain/cache/transaction_test.exs | 54 + .../chain/cache/transactions_test.exs | 95 + .../test/explorer/chain/cache/uncles_test.exs | 27 + .../cache/verified_contracts_counter_test.exs | 17 + .../test/explorer/chain/data_test.exs | 5 + .../chain/decompiled_smart_contract_test.exs | 20 + .../explorer/chain/events/publisher_test.exs | 65 + .../explorer/chain/events/subscriber_test.exs | 33 + .../test/explorer/chain/hash/address_test.exs | 36 + .../test/explorer/chain/hash/full_test.exs | 17 + .../test/explorer/chain/hash/nonce_test.exs | 5 + .../test/explorer/chain/hash_test.exs | 5 + .../address/current_token_balances_test.exs | 430 + .../runner/address/token_balances_test.exs | 165 + .../chain/import/runner/addresses_test.exs | 69 + .../chain/import/runner/blocks_test.exs | 400 + .../runner/internal_transactions_test.exs | 424 + .../chain/import/runner/tokens_test.exs | 47 + .../chain/import/runner/transactions_test.exs | 50 + .../test/explorer/chain/import_test.exs | 2310 ++ .../internal_transaction/call_type_test.exs | 5 + .../chain/internal_transaction/type_test.exs | 5 + .../chain/internal_transaction_test.exs | 244 + .../explorer/test/explorer/chain/log_test.exs | 227 + .../chain/supply/proof_of_authority_test.exs | 40 + .../test/explorer/chain/supply/rsk_test.exs | 147 + .../test/explorer/chain/token_test.exs | 29 + .../explorer/chain/token_transfer_test.exs | 378 + .../explorer/chain/transaction/fork_test.exs | 23 + .../transaction/history/historian_test.exs | 111 + .../history/transaction_stats_test.exs | 30 + .../chain/transaction/status_test.exs | 5 + .../test/explorer/chain/transaction_test.exs | 349 + .../explorer/test/explorer/chain/wei_test.exs | 134 + .../chain_spec/geth/importer_test.exs | 145 + .../chain_spec/parity/importer_test.exs | 255 + apps/explorer/test/explorer/chain_test.exs | 6183 +++ .../counters/addresses_counter_test.exs | 16 + .../addresses_tokens_usd_sum_counter_test.exs | 34 + .../addresses_with_balance_counter_test.exs | 16 + .../counters/average_block_time_test.exs | 133 + .../test/explorer/etherscan/logs_test.exs | 670 + .../explorer/test/explorer/etherscan_test.exs | 1636 + .../exchange_rates/exchange_rates_test.exs | 150 + .../exchange_rates/source/coin_gecko_test.exs | 188 + apps/explorer/test/explorer/graphql_test.exs | 347 + .../test/explorer/history/process_test.exs | 103 + .../known_tokens/known_tokens_test.exs | 140 + .../market/history/cataloger_test.exs | 49 + .../history/source/crypto_compare_test.exs | 144 + .../market/market_history_cache_test.exs | 90 + .../test/explorer/market/market_test.exs | 113 + .../test/explorer/repo/config_helper_test.exs | 107 + apps/explorer/test/explorer/repo_test.exs | 53 + .../smart_contract/compiler_version_test.exs | 60 + .../explorer/smart_contract/helper_test.exs | 127 + .../explorer/smart_contract/reader_test.exs | 415 + .../solidity/code_compiler_test.exs | 393 + .../solidity/publisher_test.exs | 188 + .../smart_contract/solidity/verifier_test.exs | 1272 + .../explorer/smart_contract/writer_test.exs | 331 + .../explorer/token/balance_reader_test.exs | 97 + .../instance_metadata_retriever_test.exs | 290 + .../token/metadata_retriever_test.exs | 532 + .../validator/metadata_importer_test.exs | 52 + .../validator/metadata_retriever_test.exs | 151 + .../chars/explorer/chain/address_test.exs | 5 + .../string/chars/explorer/chain/data_test.exs | 5 + .../test/support/chain/import/runner_case.ex | 78 + apps/explorer/test/support/data_case.ex | 74 + apps/explorer/test/support/factory.ex | 905 + .../test/support/fakes/no_op_source.ex | 19 + .../test/support/fakes/one_coin_source.ex | 35 + .../support/fixture/chain_spec/classic.json | 31157 +++++++++++++++ .../fixture/chain_spec/foundation.json | 1533 + .../fixture/chain_spec/qdai_genesis.json | 53 + .../fixture/exchange_rates/coin_gecko.json | 1806 + .../support/fixture/smart_contract/ERC677.sol | 1163 + .../smart_contract/compiler_tests.json | 24 + .../smart_contract/contract_from_factory.sol | 18 + .../smart_contract/contract_with_lib.json | 13 + .../smart_contract/contract_with_lib.sol | 25 + .../fixture/smart_contract/home_bridge.sol | 259 + .../fixture/smart_contract/issue_3082.sol | 594 + .../fixture/smart_contract/issue_4758.sol | 1718 + .../fixture/smart_contract/issue_5114.sol | 686 + .../fixture/smart_contract/issue_5127.sol | 554 + .../fixture/smart_contract/issue_5431.sol | 28 + .../issue_with_constructor_args.sol | 573 + .../smart_contract/large_smart_contract.sol | 3874 ++ .../fixture/smart_contract/solc_bin.json | 6035 +++ .../solidity_0.5.9_smart_contract.sol | 76 + .../solidity_5.11_new_whisper_metadata.json | 8 + .../solidity_5.11_new_whisper_metadata.sol | 412 + .../support/fixture/vcr_cassettes/.gitkeep | 0 ..._download_block_1_downloads_the_block.json | 30 + ...ock_importer_import_1_duplicate_block.json | 30 + .../block_importer_import_1_pending.json | 30 + ...ock_importer_import_1_saves_the_block.json | 30 + ...reumex_extensions_trace_transaction_1.json | 30 + .../import_block_perform_1_duplicate.json | 30 + .../import_block_perform_1_earliest.json | 30 + .../import_block_perform_1_integer.json | 30 + .../import_block_perform_1_latest.json | 30 + .../import_block_perform_1_string.json | 30 + .../import_block_perform_later_1_latest.json | 1 + ...import_internal_transaction_perform_1.json | 1 + .../import_receipt_perform_1.json | 30 + .../import_skipped_blocks_perform_1.json | 1 + .../import_transaction_perform_1.json | 1 + ...nternal_transaction_importer_import_1.json | 30 + ...ion_importer_import_1_from_core-trace.json | 1 + ...orter_import_1_with_contract_creation.json | 30 + ..._importer_binds_internal_transactions.json | 30 + ...ction_importer_creates_a_from_address.json | 30 + ...saction_importer_creates_a_to_address.json | 30 + ...ter_creates_a_to_address_from_creates.json | 30 + ...saction_importer_download_transaction.json | 30 + ..._download_transaction_with_a_bad_hash.json | 30 + .../transaction_importer_import_1_failed.json | 30 + ...nsaction_importer_import_1_out_of_gas.json | 30 + ...transaction_importer_import_1_pending.json | 30 + ...transaction_importer_import_1_receipt.json | 30 + ...importer_import_saves_the_transaction.json | 30 + ...action_importer_saves_the_association.json | 30 + ...ransaction_importer_txn_without_block.json | 30 + ...tion_importer_updates_the_association.json | 30 + apps/explorer/test/test_helper.exs | 22 + apps/indexer/.gitignore | 24 + apps/indexer/README.md | 156 + apps/indexer/config/config.exs | 25 + apps/indexer/config/dev.exs | 37 + apps/indexer/config/dev/arbitrum.exs | 26 + apps/indexer/config/dev/besu.exs | 32 + apps/indexer/config/dev/erigon.exs | 32 + apps/indexer/config/dev/ganache.exs | 26 + apps/indexer/config/dev/geth.exs | 26 + apps/indexer/config/dev/nethermind.exs | 49 + apps/indexer/config/dev/rsk.exs | 33 + apps/indexer/config/prod.exs | 44 + apps/indexer/config/prod/arbitrum.exs | 26 + apps/indexer/config/prod/besu.exs | 31 + apps/indexer/config/prod/erigon.exs | 31 + apps/indexer/config/prod/ganache.exs | 26 + apps/indexer/config/prod/geth.exs | 26 + apps/indexer/config/prod/nethermind.exs | 31 + apps/indexer/config/prod/rsk.exs | 33 + apps/indexer/config/runtime/test.exs | 8 + apps/indexer/config/test.exs | 22 + apps/indexer/config/test/arbitrum.exs | 8 + apps/indexer/config/test/besu.exs | 8 + apps/indexer/config/test/erigon.exs | 8 + apps/indexer/config/test/ganache.exs | 8 + apps/indexer/config/test/geth.exs | 8 + apps/indexer/config/test/nethermind.exs | 8 + apps/indexer/config/test/rsk.exs | 8 + apps/indexer/lib/indexer.ex | 5 + apps/indexer/lib/indexer/application.ex | 43 + .../catchup/bound_interval_supervisor.ex | 336 + .../lib/indexer/block/catchup/fetcher.ex | 465 + .../lib/indexer/block/catchup/sequence.ex | 358 + .../lib/indexer/block/catchup/supervisor.ex | 38 + apps/indexer/lib/indexer/block/fetcher.ex | 539 + .../lib/indexer/block/fetcher/receipts.ex | 73 + .../lib/indexer/block/realtime/fetcher.ex | 491 + .../lib/indexer/block/realtime/supervisor.ex | 53 + apps/indexer/lib/indexer/bound_interval.ex | 31 + apps/indexer/lib/indexer/bound_queue.ex | 152 + apps/indexer/lib/indexer/buffered_task.ex | 531 + apps/indexer/lib/indexer/fetcher.ex | 77 + .../lib/indexer/fetcher/block_reward.ex | 340 + .../lib/indexer/fetcher/coin_balance.ex | 267 + .../indexer/fetcher/coin_balance_on_demand.ex | 252 + .../lib/indexer/fetcher/contract_code.ex | 165 + .../indexer/fetcher/empty_blocks_sanitizer.ex | 180 + .../indexer/fetcher/internal_transaction.ex | 276 + .../indexer/fetcher/pending_transaction.ex | 193 + .../indexer/fetcher/replaced_transaction.ex | 133 + apps/indexer/lib/indexer/fetcher/token.ex | 80 + .../lib/indexer/fetcher/token_balance.ex | 216 + .../fetcher/token_balance_on_demand.ex | 112 + .../lib/indexer/fetcher/token_instance.ex | 181 + .../fetcher/token_total_supply_on_demand.ex | 48 + .../lib/indexer/fetcher/token_updater.ex | 90 + .../lib/indexer/fetcher/uncle_block.ex | 274 + apps/indexer/lib/indexer/logger.ex | 33 + apps/indexer/lib/indexer/memory/monitor.ex | 172 + apps/indexer/lib/indexer/memory/shrinkable.ex | 25 + .../lib/indexer/pending_ops_cleaner.ex | 45 + .../indexer/pending_transactions_sanitizer.ex | 176 + .../lib/indexer/prometheus/instrumenter.ex | 61 + .../pending_block_operations_collector.ex | 29 + apps/indexer/lib/indexer/supervisor.ex | 159 + .../temporary/blocks_transactions_mismatch.ex | 140 + .../temporary/uncataloged_token_transfers.ex | 99 + .../indexer/temporary/uncles_without_index.ex | 166 + apps/indexer/lib/indexer/token_balances.ex | 207 + apps/indexer/lib/indexer/tracer.ex | 5 + .../transform/address_coin_balances.ex | 90 + .../transform/address_coin_balances_daily.ex | 72 + .../transform/address_token_balances.ex | 64 + .../lib/indexer/transform/addresses.ex | 529 + apps/indexer/lib/indexer/transform/blocks.ex | 103 + .../lib/indexer/transform/blocks/base.ex | 14 + .../lib/indexer/transform/blocks/clique.ex | 18 + .../lib/indexer/transform/mint_transfers.ex | 63 + .../lib/indexer/transform/token_transfers.ex | 257 + apps/indexer/mix.exs | 65 + .../bound_interval_supervisor_test.exs | 583 + .../indexer/block/catchup/fetcher_test.exs | 629 + .../indexer/block/catchup/sequence_test.exs | 248 + .../indexer/block/fetcher/receipts_test.exs | 151 + .../test/indexer/block/fetcher_test.exs | 865 + .../indexer/block/realtime/fetcher_test.exs | 1054 + .../test/indexer/buffered_task_test.exs | 292 + .../indexer/fetcher/block_reward_test.exs | 735 + .../fetcher/coin_balance_on_demand_test.exs | 222 + .../indexer/fetcher/coin_balance_test.exs | 510 + .../indexer/fetcher/contract_code_test.exs | 115 + .../fetcher/internal_transaction_test.exs | 309 + .../fetcher/pending_transaction_test.exs | 70 + .../fetcher/replaced_transaction_test.exs | 196 + .../indexer/fetcher/token_balance_test.exs | 279 + .../indexer/fetcher/token_instance_test.exs | 87 + .../test/indexer/fetcher/token_test.exs | 81 + .../indexer/fetcher/token_updater_test.exs | 87 + .../test/indexer/fetcher/uncle_block_test.exs | 248 + .../test/indexer/pending_ops_cleaner_test.exs | 34 + .../uncataloged_token_transfers_test.exs | 110 + .../test/indexer/token_balances_test.exs | 501 + .../transform/address_coin_balances_test.exs | 190 + .../transform/address_token_balances_test.exs | 88 + .../test/indexer/transform/addresses.exs | 325 + .../indexer/transform/blocks/base_test.exs | 42 + .../indexer/transform/blocks/clique_test.exs | 43 + .../test/indexer/transform/blocks_test.exs | 85 + .../indexer/transform/mint_transfers_test.exs | 57 + .../transform/token_transfers_test.exs | 235 + apps/indexer/test/indexer_test.exs | 5 + .../indexer/block/catchup_supervisor_case.ex | 9 + .../fetcher/block_reward_supervisor_case.ex | 17 + .../fetcher/coin_balance_supervisor_case.ex | 17 + .../fetcher/contract_code_supervisor_case.ex | 17 + .../internal_transaction_supervisor_case.ex | 18 + .../pending_transaction_supervisor_case.ex | 17 + .../replaced_transaction_supervisor_case.ex | 17 + .../fetcher/token_balance_supervisor_case.ex | 17 + .../indexer/fetcher/token_supervisor_case.ex | 17 + .../fetcher/token_updater_supervisor_case.ex | 17 + .../fetcher/uncle_block_supervisor_case.ex | 17 + apps/indexer/test/test_helper.exs | 24 + appspec.yml | 20 + bin/deploy | 124 + bin/deployment/build | 26 + bin/deployment/health_check | 16 + bin/deployment/migrate | 19 + bin/deployment/start | 7 + bin/deployment/stop | 9 + bin/install_chrome_headless.sh | 11 + bin/test | 9 + blockscout.png | Bin 0 -> 17833 bytes config/config.exs | 67 + config/dev.exs | 24 + config/prod.exs | 20 + config/runtime.exs | 465 + config/runtime/dev.exs | 85 + config/runtime/prod.exs | 64 + config/runtime/test.exs | 25 + config/test.exs | 17 + coveralls.json | 9 + docker-compose/README.md | 46 + .../docker-compose-no-build-erigon.yml | 60 + .../docker-compose-no-build-ganache.yml | 62 + .../docker-compose-no-build-geth.yml | 60 + ...ocker-compose-no-build-hardhat-network.yml | 59 + .../docker-compose-no-build-nethermind.yml | 60 + ...ocker-compose-no-build-no-db-container.yml | 43 + .../docker-compose-no-rust-verification.yml | 58 + docker-compose/docker-compose.yml | 66 + docker-compose/envs/common-blockscout.env | 146 + .../envs/common-smart-contract-verifier.env | 20 + docker/Dockerfile | 75 + docker/Makefile | 656 + docker/README.md | 5 + mix.exs | 103 + mix.lock | 141 + prometheus.yml | 6 + rel/commands/migrate.sh | 3 + rel/commands/seed.sh | 3 + rel/config.exs | 86 + rel/plugins/.gitignore | 3 + rel/vm.args | 30 + 1960 files changed, 302938 insertions(+) create mode 100644 .circleci/config.yml create mode 100644 .credo.exs create mode 100644 .dialyzer-ignore create mode 100644 .dockerignore create mode 100644 .formatter.exs create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .github/workflows/config.yml create mode 100644 .github/workflows/e2e-tests.yml create mode 100644 .github/workflows/publish-docker-image-every-push.yml create mode 100644 .github/workflows/publish-docker-image-release.yml create mode 100644 .gitignore create mode 100644 .pairs create mode 100644 .tool-versions create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 ISSUE_TEMPLATE.md create mode 100644 LICENSE create mode 100644 PULL_REQUEST_TEMPLATE.md create mode 100644 README.md create mode 100644 apps/block_scout_web/.sobelow-conf create mode 100644 apps/block_scout_web/API blueprint.md create mode 100644 apps/block_scout_web/API.md create mode 100644 apps/block_scout_web/README.md create mode 100644 apps/block_scout_web/assets/.babelrc create mode 100644 apps/block_scout_web/assets/.eslintrc create mode 100644 apps/block_scout_web/assets/README.md create mode 100644 apps/block_scout_web/assets/__mocks__/css/app.scss.js create mode 100644 apps/block_scout_web/assets/__tests__/lib/async_listing_load.js create mode 100644 apps/block_scout_web/assets/__tests__/lib/autocomplete.js create mode 100644 apps/block_scout_web/assets/__tests__/lib/currency.js create mode 100644 apps/block_scout_web/assets/__tests__/lib/smart_contract/common_helpers.js create mode 100644 apps/block_scout_web/assets/__tests__/lib/utils.js create mode 100644 apps/block_scout_web/assets/__tests__/pages/address.js create mode 100644 apps/block_scout_web/assets/__tests__/pages/address/internal_transactions.js create mode 100644 apps/block_scout_web/assets/__tests__/pages/address/transactions.js create mode 100644 apps/block_scout_web/assets/__tests__/pages/address/validations.js create mode 100644 apps/block_scout_web/assets/__tests__/pages/blocks.js create mode 100644 apps/block_scout_web/assets/__tests__/pages/chain.js create mode 100644 apps/block_scout_web/assets/__tests__/pages/pending_transactions.js create mode 100644 apps/block_scout_web/assets/__tests__/pages/transaction.js create mode 100644 apps/block_scout_web/assets/__tests__/pages/transactions.js create mode 100644 apps/block_scout_web/assets/close.svg create mode 100644 apps/block_scout_web/assets/css/_code.scss create mode 100644 apps/block_scout_web/assets/css/_elements.scss create mode 100644 apps/block_scout_web/assets/css/_helpers.scss create mode 100644 apps/block_scout_web/assets/css/_layout.scss create mode 100644 apps/block_scout_web/assets/css/_mixins.scss create mode 100644 apps/block_scout_web/assets/css/_typography.scss create mode 100644 apps/block_scout_web/assets/css/app.scss create mode 100644 apps/block_scout_web/assets/css/components/_account.scss create mode 100644 apps/block_scout_web/assets/css/components/_ad.scss create mode 100644 apps/block_scout_web/assets/css/components/_address-overview.scss create mode 100644 apps/block_scout_web/assets/css/components/_address_link.scss create mode 100644 apps/block_scout_web/assets/css/components/_alerts.scss create mode 100644 apps/block_scout_web/assets/css/components/_animations.scss create mode 100644 apps/block_scout_web/assets/css/components/_api.scss create mode 100644 apps/block_scout_web/assets/css/components/_badge.scss create mode 100644 apps/block_scout_web/assets/css/components/_btn_add_to_mm.scss create mode 100644 apps/block_scout_web/assets/css/components/_btn_address_card.scss create mode 100644 apps/block_scout_web/assets/css/components/_btn_contract.scss create mode 100644 apps/block_scout_web/assets/css/components/_btn_copy.scss create mode 100644 apps/block_scout_web/assets/css/components/_btn_dropdown_line.scss create mode 100644 apps/block_scout_web/assets/css/components/_btn_full.scss create mode 100644 apps/block_scout_web/assets/css/components/_btn_line.scss create mode 100644 apps/block_scout_web/assets/css/components/_btn_no_border.scss create mode 100644 apps/block_scout_web/assets/css/components/_btn_qr.scss create mode 100644 apps/block_scout_web/assets/css/components/_btn_swap.scss create mode 100644 apps/block_scout_web/assets/css/components/_btn_wallet.scss create mode 100644 apps/block_scout_web/assets/css/components/_button.scss create mode 100644 apps/block_scout_web/assets/css/components/_card.scss create mode 100644 apps/block_scout_web/assets/css/components/_check.scss create mode 100644 apps/block_scout_web/assets/css/components/_check_tooltip.scss create mode 100644 apps/block_scout_web/assets/css/components/_coin-balance-tile.scss create mode 100644 apps/block_scout_web/assets/css/components/_custom_tooltips.scss create mode 100644 apps/block_scout_web/assets/css/components/_dashboard-banner.scss create mode 100644 apps/block_scout_web/assets/css/components/_description-list.scss create mode 100644 apps/block_scout_web/assets/css/components/_dot.scss create mode 100644 apps/block_scout_web/assets/css/components/_dropdown.scss create mode 100644 apps/block_scout_web/assets/css/components/_dropzone.scss create mode 100644 apps/block_scout_web/assets/css/components/_erc721_token_image_container.scss create mode 100644 apps/block_scout_web/assets/css/components/_errors.scss create mode 100644 apps/block_scout_web/assets/css/components/_external_link.scss create mode 100644 apps/block_scout_web/assets/css/components/_filter.scss create mode 100644 apps/block_scout_web/assets/css/components/_fontawesome_icon.scss create mode 100644 apps/block_scout_web/assets/css/components/_footer.scss create mode 100644 apps/block_scout_web/assets/css/components/_form.scss create mode 100644 apps/block_scout_web/assets/css/components/_highlight.scss create mode 100644 apps/block_scout_web/assets/css/components/_i_tooltip.scss create mode 100644 apps/block_scout_web/assets/css/components/_i_tooltip_2.scss create mode 100644 apps/block_scout_web/assets/css/components/_icon-link.scss create mode 100644 apps/block_scout_web/assets/css/components/_inventory_token_instance_image_container.scss create mode 100644 apps/block_scout_web/assets/css/components/_label.scss create mode 100644 apps/block_scout_web/assets/css/components/_loading-spinner.scss create mode 100644 apps/block_scout_web/assets/css/components/_log-search.scss create mode 100644 apps/block_scout_web/assets/css/components/_me_tooltip.scss create mode 100644 apps/block_scout_web/assets/css/components/_modal.scss create mode 100644 apps/block_scout_web/assets/css/components/_modal_stake.scss create mode 100644 apps/block_scout_web/assets/css/components/_modal_status.scss create mode 100644 apps/block_scout_web/assets/css/components/_modal_variables.scss create mode 100644 apps/block_scout_web/assets/css/components/_nav_tabs.scss create mode 100644 apps/block_scout_web/assets/css/components/_navbar.scss create mode 100644 apps/block_scout_web/assets/css/components/_new_smart_contract.scss create mode 100644 apps/block_scout_web/assets/css/components/_nounderline-link.scss create mode 100644 apps/block_scout_web/assets/css/components/_pagination_container.scss create mode 100644 apps/block_scout_web/assets/css/components/_panels.scss create mode 100644 apps/block_scout_web/assets/css/components/_qr-code.scss create mode 100644 apps/block_scout_web/assets/css/components/_radio.scss create mode 100644 apps/block_scout_web/assets/css/components/_radio_big.scss create mode 100644 apps/block_scout_web/assets/css/components/_search.scss create mode 100644 apps/block_scout_web/assets/css/components/_stakes_table.scss create mode 100644 apps/block_scout_web/assets/css/components/_stakes_variables.scss create mode 100644 apps/block_scout_web/assets/css/components/_table.scss create mode 100644 apps/block_scout_web/assets/css/components/_tile.scss create mode 100644 apps/block_scout_web/assets/css/components/_token-balance-dropdown.scss create mode 100644 apps/block_scout_web/assets/css/components/_token.scss create mode 100644 apps/block_scout_web/assets/css/components/_token_tile_view_more.scss create mode 100644 apps/block_scout_web/assets/css/components/_tooltip.scss create mode 100644 apps/block_scout_web/assets/css/components/_transaction-input.scss create mode 100644 apps/block_scout_web/assets/css/components/_transaction.scss create mode 100644 apps/block_scout_web/assets/css/components/_verify_other_explorers.scss create mode 100644 apps/block_scout_web/assets/css/components/datepicker.scss create mode 100644 apps/block_scout_web/assets/css/export-csv.scss create mode 100644 apps/block_scout_web/assets/css/export-vars-to-js.module.scss create mode 100644 apps/block_scout_web/assets/css/main-page.scss create mode 100644 apps/block_scout_web/assets/css/non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_base_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_callisto_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_dai_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_dai_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_dark-theme.scss create mode 100644 apps/block_scout_web/assets/css/theme/_ellaism_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_ether1_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_ether1_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_ethercore_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_ethercore_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_ethereum_classic_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_ethereum_classic_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_ethereum_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_ethereum_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_expanse_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_fonts.scss create mode 100644 apps/block_scout_web/assets/css/theme/_gochain_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_goerli_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_goerli_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_kovan_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_kovan_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_lukso_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_lukso_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_musicoin_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_neutral_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_neutral_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_pirl_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_poa_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_poa_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_rinkeby_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_rinkeby_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_ropsten_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_ropsten_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_rsk_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_rsk_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_social_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_sokol_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_sokol_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_tobalaba_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_tomochain_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_wanchain_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/_xusdt_variables-non-critical.scss create mode 100644 apps/block_scout_web/assets/css/theme/_xusdt_variables.scss create mode 100644 apps/block_scout_web/assets/css/theme/custom_contracts/_circles-theme.scss create mode 100644 apps/block_scout_web/assets/css/theme/custom_contracts/_dark-forest-theme.scss create mode 100644 apps/block_scout_web/assets/js/app.js create mode 100644 apps/block_scout_web/assets/js/balance-chart-loader.js create mode 100644 apps/block_scout_web/assets/js/chart-loader.js create mode 100644 apps/block_scout_web/assets/js/lib/ace/src-min/ace.js create mode 100644 apps/block_scout_web/assets/js/lib/ace/src-min/mode-csharp.js create mode 100644 apps/block_scout_web/assets/js/lib/ace/src-min/theme-chrome.js create mode 100644 apps/block_scout_web/assets/js/lib/ad.js create mode 100644 apps/block_scout_web/assets/js/lib/add_chain_to_mm.js create mode 100644 apps/block_scout_web/assets/js/lib/async_listing_load.js create mode 100644 apps/block_scout_web/assets/js/lib/autocomplete.js create mode 100644 apps/block_scout_web/assets/js/lib/banner.js create mode 100644 apps/block_scout_web/assets/js/lib/card_tabs.js create mode 100644 apps/block_scout_web/assets/js/lib/clipboard_buttons.js create mode 100644 apps/block_scout_web/assets/js/lib/coin_balance_history_chart.js create mode 100644 apps/block_scout_web/assets/js/lib/csv_download.js create mode 100644 apps/block_scout_web/assets/js/lib/currency.js create mode 100644 apps/block_scout_web/assets/js/lib/custom_ad.json create mode 100644 apps/block_scout_web/assets/js/lib/dropzone.js create mode 100644 apps/block_scout_web/assets/js/lib/from_now.js create mode 100644 apps/block_scout_web/assets/js/lib/history_chart.js create mode 100644 apps/block_scout_web/assets/js/lib/indexing.js create mode 100644 apps/block_scout_web/assets/js/lib/infinite_scroll_helpers.js create mode 100644 apps/block_scout_web/assets/js/lib/list_morph.js create mode 100644 apps/block_scout_web/assets/js/lib/loading_element.js create mode 100644 apps/block_scout_web/assets/js/lib/modals.js create mode 100644 apps/block_scout_web/assets/js/lib/pending_transactions_toggle.js create mode 100644 apps/block_scout_web/assets/js/lib/pretty_json.js create mode 100644 apps/block_scout_web/assets/js/lib/public_tags_request_form.js create mode 100644 apps/block_scout_web/assets/js/lib/queue.js create mode 100644 apps/block_scout_web/assets/js/lib/random_access_pagination.js create mode 100644 apps/block_scout_web/assets/js/lib/redux_helpers.js create mode 100644 apps/block_scout_web/assets/js/lib/reload_button.js create mode 100644 apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js create mode 100644 apps/block_scout_web/assets/js/lib/smart_contract/connect.js create mode 100644 apps/block_scout_web/assets/js/lib/smart_contract/functions.js create mode 100644 apps/block_scout_web/assets/js/lib/smart_contract/index.js create mode 100644 apps/block_scout_web/assets/js/lib/smart_contract/interact.js create mode 100644 apps/block_scout_web/assets/js/lib/smart_contract/wei_ether_converter.js create mode 100644 apps/block_scout_web/assets/js/lib/stop_propagation.js create mode 100644 apps/block_scout_web/assets/js/lib/text_ad.js create mode 100644 apps/block_scout_web/assets/js/lib/token_balance_dropdown.js create mode 100644 apps/block_scout_web/assets/js/lib/token_balance_dropdown_search.js create mode 100644 apps/block_scout_web/assets/js/lib/token_icon.js create mode 100644 apps/block_scout_web/assets/js/lib/token_transfers_toggle.js create mode 100644 apps/block_scout_web/assets/js/lib/tooltip.js create mode 100644 apps/block_scout_web/assets/js/lib/transaction_input_dropdown.js create mode 100644 apps/block_scout_web/assets/js/lib/try_api.js create mode 100644 apps/block_scout_web/assets/js/lib/try_eth_api.js create mode 100644 apps/block_scout_web/assets/js/lib/utils.js create mode 100644 apps/block_scout_web/assets/js/lib/validation.js create mode 100644 apps/block_scout_web/assets/js/locale.js create mode 100644 apps/block_scout_web/assets/js/pages/account/delete_item_handler.js create mode 100644 apps/block_scout_web/assets/js/pages/address.js create mode 100644 apps/block_scout_web/assets/js/pages/address/coin_balances.js create mode 100644 apps/block_scout_web/assets/js/pages/address/internal_transactions.js create mode 100644 apps/block_scout_web/assets/js/pages/address/logs.js create mode 100644 apps/block_scout_web/assets/js/pages/address/token_transfers.js create mode 100644 apps/block_scout_web/assets/js/pages/address/transactions.js create mode 100644 apps/block_scout_web/assets/js/pages/address/utils.js create mode 100644 apps/block_scout_web/assets/js/pages/address/validations.js create mode 100644 apps/block_scout_web/assets/js/pages/admin/tasks.js create mode 100644 apps/block_scout_web/assets/js/pages/blocks.js create mode 100644 apps/block_scout_web/assets/js/pages/chain.js create mode 100644 apps/block_scout_web/assets/js/pages/dark-mode-switcher.js create mode 100644 apps/block_scout_web/assets/js/pages/layout.js create mode 100644 apps/block_scout_web/assets/js/pages/pending_transactions.js create mode 100644 apps/block_scout_web/assets/js/pages/search-results/search.js create mode 100644 apps/block_scout_web/assets/js/pages/token/overview.js create mode 100644 apps/block_scout_web/assets/js/pages/token/search.js create mode 100644 apps/block_scout_web/assets/js/pages/token/token_transfers.js create mode 100644 apps/block_scout_web/assets/js/pages/token_contract.js create mode 100644 apps/block_scout_web/assets/js/pages/token_counters.js create mode 100644 apps/block_scout_web/assets/js/pages/transaction.js create mode 100644 apps/block_scout_web/assets/js/pages/transactions.js create mode 100644 apps/block_scout_web/assets/js/pages/verification_form.js create mode 100644 apps/block_scout_web/assets/js/pages/verified_contracts.js create mode 100644 apps/block_scout_web/assets/js/socket.js create mode 100644 apps/block_scout_web/assets/js/view_specific/address_contract/code_highlighting.js create mode 100644 apps/block_scout_web/assets/js/view_specific/raw_trace/code_highlighting.js create mode 100644 apps/block_scout_web/assets/package-lock.json create mode 100644 apps/block_scout_web/assets/package.json create mode 100644 apps/block_scout_web/assets/postcss.config.js create mode 100644 apps/block_scout_web/assets/static/android-chrome-192x192.png create mode 100644 apps/block_scout_web/assets/static/android-chrome-512x512.png create mode 100644 apps/block_scout_web/assets/static/apple-touch-icon.png create mode 100644 apps/block_scout_web/assets/static/browserconfig.xml create mode 100644 apps/block_scout_web/assets/static/images/average_time.svg create mode 100644 apps/block_scout_web/assets/static/images/block.svg create mode 100644 apps/block_scout_web/assets/static/images/blocks.svg create mode 100644 apps/block_scout_web/assets/static/images/blockscout_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/callisto_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/classic_ethereum_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/classic_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/controller.svg create mode 100644 apps/block_scout_web/assets/static/images/cube.svg create mode 100644 apps/block_scout_web/assets/static/images/custom-themes/circles/balance.svg create mode 100644 apps/block_scout_web/assets/static/images/custom-themes/circles/copy-circles.svg create mode 100644 apps/block_scout_web/assets/static/images/custom-themes/circles/footer_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/custom-themes/circles/logo.svg create mode 100644 apps/block_scout_web/assets/static/images/custom-themes/circles/qr-circles.svg create mode 100644 apps/block_scout_web/assets/static/images/custom-themes/dark-forest/copy-df.svg create mode 100644 apps/block_scout_web/assets/static/images/custom-themes/dark-forest/dark_forest_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/custom-themes/dark-forest/pic_balance.svg create mode 100644 apps/block_scout_web/assets/static/images/custom-themes/dark-forest/planet.svg create mode 100644 apps/block_scout_web/assets/static/images/custom-themes/dark-forest/qr-df.svg create mode 100644 apps/block_scout_web/assets/static/images/custom-themes/dark-forest/union.svg create mode 100644 apps/block_scout_web/assets/static/images/dai_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/ellaism_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/errors-img/etc-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/etc-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/etc-page-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/etc-page-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/etc-tx-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/etc-tx-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/eth-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/eth-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/eth-page-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/eth-page-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/eth-tx-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/eth-tx-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/goerli-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/goerli-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/goerli-page-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/goerli-page-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/goerli-tx-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/goerli-tx-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/koan-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/kovan-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/kovan-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/kovan-page-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/kovan-page-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/kovan-tx-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/kovan-tx-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/lukso-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/lukso-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/lukso-page-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/lukso-page-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/lukso-tx-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/lukso-tx-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/pic-404.svg create mode 100644 apps/block_scout_web/assets/static/images/errors-img/poa-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/poa-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/poa-page-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/poa-page-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/poa-tx-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/poa-tx-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rinkeby-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rinkeby-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rinkeby-page-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rinkeby-page-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rinkeby-tx-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rinkeby-tx-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rinnkeby-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rinnkeby-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/ropsten-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/ropsten-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/ropsten-page-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/ropsten-page-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/ropsten-tx-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/ropsten-tx-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rsk-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rsk-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rsk-page-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rsk-page-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rsk-tx-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/rsk-tx-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/sokol-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/sokol-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/sokol-page-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/sokol-page-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/sokol-tx-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/sokol-tx-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/xdai-block-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/xdai-block-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/xdai-page-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/xdai-page-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/xdai-tx-not-found.png create mode 100644 apps/block_scout_web/assets/static/images/errors-img/xdai-tx-not-found@2x.png create mode 100644 apps/block_scout_web/assets/static/images/eth.png create mode 100644 apps/block_scout_web/assets/static/images/ether1_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/ethercore_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/ethercore_testnet_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/ethereum.png create mode 100644 apps/block_scout_web/assets/static/images/ethereum_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/expanse_logo.png create mode 100644 apps/block_scout_web/assets/static/images/favicon-16x16.png create mode 100644 apps/block_scout_web/assets/static/images/favicon-32x32.png create mode 100644 apps/block_scout_web/assets/static/images/favicon.ico create mode 100644 apps/block_scout_web/assets/static/images/gc_logo_2.svg create mode 100644 apps/block_scout_web/assets/static/images/gc_logo_old.svg create mode 100644 apps/block_scout_web/assets/static/images/gc_logo_rebrand.svg create mode 100644 apps/block_scout_web/assets/static/images/gochain_logo.png create mode 100644 apps/block_scout_web/assets/static/images/goerli_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/blockchair.png create mode 100644 apps/block_scout_web/assets/static/images/icons/blockchair@2x.png create mode 100644 apps/block_scout_web/assets/static/images/icons/check-1.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/copy.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/dots.svg create mode 100755 apps/block_scout_web/assets/static/images/icons/etherchain.png create mode 100755 apps/block_scout_web/assets/static/images/icons/etherchain@2x.png create mode 100644 apps/block_scout_web/assets/static/images/icons/etherscan.png create mode 100644 apps/block_scout_web/assets/static/images/icons/etherscan@2x.png create mode 100644 apps/block_scout_web/assets/static/images/icons/fontawesome/bar-chart.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/fontawesome/github.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/fontawesome/info-circle.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/fontawesome/tag.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/fontawesome/telegram.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/fontawesome/twitter.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/link.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/metamask-fox.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/pic-empty.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/plus.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/remove.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/swap/1inch.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/swap/component.png create mode 100644 apps/block_scout_web/assets/static/images/icons/swap/cowswap.png create mode 100644 apps/block_scout_web/assets/static/images/icons/swap/curve.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/swap/honeyswap.png create mode 100644 apps/block_scout_web/assets/static/images/icons/swap/sushi.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/swap/swapr.svg create mode 100644 apps/block_scout_web/assets/static/images/icons/withdraw.svg create mode 100644 apps/block_scout_web/assets/static/images/kaly_footer.png create mode 100644 apps/block_scout_web/assets/static/images/kalycoin-logo.png create mode 100644 apps/block_scout_web/assets/static/images/kovan_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/last_block.svg create mode 100644 apps/block_scout_web/assets/static/images/logo.svg create mode 100755 apps/block_scout_web/assets/static/images/lukso_dashboard_image.png create mode 100755 apps/block_scout_web/assets/static/images/lukso_logo.png create mode 100755 apps/block_scout_web/assets/static/images/lukso_logo_footer.png create mode 100644 apps/block_scout_web/assets/static/images/musicoin_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/callisto-mainnet.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/circle-xusdt.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/ethereum-classic.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/ethereum-mainnet.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/goerli-testnet.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/kovan-testnet.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/lukso-l14-testnet.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/poa-core.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/poa-sokol.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/rinkeby-testnet.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/ropsten-testnet.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/rsk-mainnet.svg create mode 100644 apps/block_scout_web/assets/static/images/network-selector-icons/xdai-chain.svg create mode 100644 apps/block_scout_web/assets/static/images/pirl_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/poa_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/purple-block.svg create mode 100644 apps/block_scout_web/assets/static/images/rinkeby_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/ropsten_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/rsk_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/smart_contract.svg create mode 100644 apps/block_scout_web/assets/static/images/social_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/sokol_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/spinner.svg create mode 100644 apps/block_scout_web/assets/static/images/tobalaba_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/token.svg create mode 100644 apps/block_scout_web/assets/static/images/tomochain_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/transaction.svg create mode 100644 apps/block_scout_web/assets/static/images/transactions.svg create mode 100644 apps/block_scout_web/assets/static/images/wanchain_logo.png create mode 100644 apps/block_scout_web/assets/static/images/xdai_alternative.svg create mode 100644 apps/block_scout_web/assets/static/images/xdai_logo.svg create mode 100644 apps/block_scout_web/assets/static/images/xusdt-logo-footer.svg create mode 100644 apps/block_scout_web/assets/static/images/xusdt-logo-top.svg create mode 100644 apps/block_scout_web/assets/static/manifest.webmanifest create mode 100644 apps/block_scout_web/assets/static/mstile-150x150.png create mode 100644 apps/block_scout_web/assets/static/robots.txt create mode 100644 apps/block_scout_web/assets/static/safari-pinned-tab.svg create mode 100644 apps/block_scout_web/assets/webpack.config.js create mode 100644 apps/block_scout_web/config/config.exs create mode 100644 apps/block_scout_web/config/dev.exs create mode 100644 apps/block_scout_web/config/prod.exs create mode 100644 apps/block_scout_web/config/runtime/test.exs create mode 100644 apps/block_scout_web/config/test.exs create mode 100644 apps/block_scout_web/lib/block_scout_web.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/admin_router.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/api_router.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/application.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/captcha_helper.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/chain.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/channels/exchange_rate_channel.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/channels/reward_channel.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/channels/token_channel.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/channels/user_socket.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/checksum_address.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/cldr.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/fallback_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/tags_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/user_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/api_key_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/auth_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/custom_abi_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/public_tags_request_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/tag_address_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/tag_transaction_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/watchlist_address_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/account/watchlist_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_by_day_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_json_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_multi_part_files_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_standard_json_input_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_vyper_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_decompiled_contract_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_read_proxy_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_write_contract_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/address_write_proxy_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/admin/dashboard_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/admin/session_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/admin/setup_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/admin/tasks_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/api_logger.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/eth_rpc/eth_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/address_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/block_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/helpers.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/logs_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/token_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v1/gas_price_oracle_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v1/health_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v1/supply_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/api_docs_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/block_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/chain/transaction_history_chart_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/common_components_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/csv_export_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/page_not_found_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/contract_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/holder_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/metadata_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/token_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/tokens_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/verified_contracts_controller.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/counters/blocks_indexed_counter.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/counters/internal_transactions_indexed_counter.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/csp_header.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/endpoint.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/etherscan.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/gettext.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/models/get_address_tags.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/models/user_from_auth.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/notifier.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/paging_helper.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/plug/admin/check_owner_registered.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/plug/admin/require_admin_role.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/plug/allow_iframe.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/plug/check_account_api.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/plug/check_account_web.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/plug/check_api_v2.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/plug/fetch_user_from_session.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/plug/graphql.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/plug/redis_cookie.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/prometheus/exporter.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/prometheus/instrumenter.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/resolvers/address.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/resolvers/block.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/resolvers/internal_transaction.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/resolvers/token_transfer.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/resolvers/transaction.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/router.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/schema.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/schema/scalars.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/schema/scalars/JSON.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/schema/types.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/social_media.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/api_key/form.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/api_key/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/api_key/row.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/auth/profile.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/common/_nav.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/form.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/row.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/address_field.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/form.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/row.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/form.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/row.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/form.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/watchlist/show.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/watchlist_address/form.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/account/watchlist_address/row.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_balance_dropdown.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_block_link.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_current_coin_balance.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_custom_view_df_title.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_labels.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_link.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_responsive_hash.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_show_address_transactions.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_tile.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer_modal.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorers.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/_coin_balances.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_compiler_field.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_constructor_args.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_libraries_other.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_address.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_first.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_name.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_token/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_token/_tokens.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_token/overview.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_token/overview_item.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_tokens.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_transaction/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_validation/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/admin/dashboard/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/admin/session/login_form.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/admin/setup/admin_registration.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/admin/setup/verify.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/advertisement/banners_ad/_banner_728.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/advertisement/text_ad/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/api_docs/_action_tile.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/api_docs/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/api_docs/_model_table.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/api_docs/_module_card.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/api_docs/_module_item.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/api_docs/eth_rpc.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/api_docs/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/block/_link.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/block/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/block/_tile.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/block/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/block_transaction/404.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/block_transaction/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/block_transaction/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/chain/_block.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/chain/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_add_full.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_add_line.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_copy.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_copy_for_table.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_external_link.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_line.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_qr_code.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_channel_disconnected_message.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_check_tooltip.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_csv_export_button.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_i_tooltip.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_i_tooltip_2.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_error_modal.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_question_modal.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_success_modal.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_warning_modal.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_info.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_input_group.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_loading_spinner.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_minimal_proxy_pattern.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_bottom_disclaimer.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_close_button.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_qr_code.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_status.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_pagination_container.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_progress_from_to.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_status_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_minus.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_pen.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_plus.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_trash.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_table-loader.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_tenderly_link.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_tile-loader.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/error422/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/form/_tag.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/form/text_field.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_accounts_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_active_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_api_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_apps_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_block_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_blockchain_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_check_dark_forest_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_external_link.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_gas_price_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_guage_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_hourglass_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_inactive_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_network_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_search_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_smart_contract.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_test_network_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_tokens_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/icons/_transaction_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/internal_transaction/_tile.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/layout/_account_menu_item.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/layout/_add_chain_to_mm.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/layout/_default_title.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/layout/_search.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/log/_data_decoded_view.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/page_not_found/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/pending_transaction/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/search/_empty_td.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/search/_name_td.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/search/_tile.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/search/results.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_connect_container.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/_tile.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/_token_icon.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/contract/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/contract/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/holder/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_tabs.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_link.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_instance.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_symbol.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_tabs.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_token_transfer.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/_transfer_token_with_id.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/invalid.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/not_found.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/show_internal_transactions.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction/show_token_transfers.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_internal_transaction/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_log/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_token_balance.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_state/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_contract.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_metatags.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_stats.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/index.html.eex create mode 100644 apps/block_scout_web/lib/block_scout_web/tracer.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/access_helpers.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/api/v1/account_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/api/v1/tags_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/api/v1/user_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/api_key_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/auth_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/common_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/custom_abi_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/public_tags_request_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/tag_address_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/tag_transaction_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/watchlist_address_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/account/watchlist_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_common_fields_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_flattened_code_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_json_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_multi_part_files_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_standard_json_input_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_token_balance_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/admin/dashboard_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/admin/session_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/admin/setup_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/advertisement/banners_ad_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/advertisement/text_ad_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/eth_rpc/view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/rpc/logs_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/rpc/rpc_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/rpc/stats_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/rpc/token_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v1/supply_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/api_v2.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/api_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/config_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/api_docs_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/block_transaction_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/block_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/chain_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/cldr_helper/number.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/common_components_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/csv_export.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/currency_helpers.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/error_422.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/error_helpers.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/error_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/form_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/icons_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/layout_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/log_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/page_not_found.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/pending_transaction_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/render_helpers.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/script_helpers.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/search_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tab_helpers.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/contract_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/instance/holder_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/instance/metadata_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/instance_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/inventory_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/tokens_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/transaction_internal_transaction_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/transaction_log_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/transaction_raw_trace_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/verified_contracts_view.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/wei_helpers.ex create mode 100644 apps/block_scout_web/lib/block_scout_web/web_router.ex create mode 100644 apps/block_scout_web/lib/phoenix/html/safe.ex create mode 100644 apps/block_scout_web/lib/phoenix/param.ex create mode 100644 apps/block_scout_web/mix.exs create mode 100644 apps/block_scout_web/priv/gettext/default.pot create mode 100644 apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po create mode 100644 apps/block_scout_web/priv/gettext/en/LC_MESSAGES/errors.po create mode 100644 apps/block_scout_web/priv/gettext/errors.pot create mode 100644 apps/block_scout_web/test/block_scout_web/chain_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/channels/address_channel_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/channels/block_channel_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/channels/exchange_rate_channel_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/channels/reward_channel_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/channels/transaction_channel_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/account/api/v1/user_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/account/custom_abi_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_coin_balance_by_day_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_contract_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_token_balance_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_token_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_token_transfer_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/admin/dashboard_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/admin/session_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/admin/setup_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/rpc/rpc_translator_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/rpc/token_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/v1/health_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/v1/supply_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/api_docs_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/block_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/block_transaction_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/chain/market_history_chart_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/chain/transaction_history_chart_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/page_not_found_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/pending_transaction_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/recent_transactions_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/tokens/holder_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/tokens/instance/transfer_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/tokens/inventory_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/tokens/read_contract_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/tokens/token_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/transaction_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/transaction_internal_transaction_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/transaction_log_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/transaction_state_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/controllers/verified_contracts_controller_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/features/address_contract_verification_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/features/pages/address_contract_page.ex create mode 100644 apps/block_scout_web/test/block_scout_web/features/pages/address_page.ex create mode 100644 apps/block_scout_web/test/block_scout_web/features/pages/app_page.ex create mode 100644 apps/block_scout_web/test/block_scout_web/features/pages/block_list_page.ex create mode 100644 apps/block_scout_web/test/block_scout_web/features/pages/block_page.ex create mode 100644 apps/block_scout_web/test/block_scout_web/features/pages/chain_page.ex create mode 100644 apps/block_scout_web/test/block_scout_web/features/pages/contract_verify_page.ex create mode 100644 apps/block_scout_web/test/block_scout_web/features/pages/token_page.ex create mode 100644 apps/block_scout_web/test/block_scout_web/features/pages/transaction_list_page.ex create mode 100644 apps/block_scout_web/test/block_scout_web/features/pages/transaction_logs_page.ex create mode 100644 apps/block_scout_web/test/block_scout_web/features/pages/transaction_page.ex create mode 100644 apps/block_scout_web/test/block_scout_web/features/viewing_addresses_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/features/viewing_app_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/features/viewing_blocks_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/features/viewing_chain_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/features/viewing_tokens_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/models/user_from_auth_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/plug/admin/check_owner_registered_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/plug/admin/require_admin_role_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/plug/fetch_user_from_session_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/schema/query/addresses_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/schema/query/block_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/schema/query/node_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/schema/query/token_transfers_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/schema/query/transaction_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/schema/subscription/token_transfers_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/social_media_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/abi_encoded_value_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/address_coin_balance_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/address_contract_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/address_decompiled_contract_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/address_token_balance_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/address_transaction_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/address_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/api_docs_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/block_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/currency_helpers_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/error_helpers_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/error_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/internal_transaction_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/layout_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/render_helpers_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/search_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/smart_contract_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/tab_helpers_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/tokens/helpers_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/tokens/holder_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/tokens/instance/overview_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/tokens/overview_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/tokens/read_contract_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/tokens/smart_contract_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/tokens/transfer_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs create mode 100644 apps/block_scout_web/test/block_scout_web/views/wei_helpers_test.exs create mode 100644 apps/block_scout_web/test/phoenix/param/explorer/chain/block_test.exs create mode 100644 apps/block_scout_web/test/support/channel_case.ex create mode 100644 apps/block_scout_web/test/support/conn_case.ex create mode 100644 apps/block_scout_web/test/support/feature_case.ex create mode 100644 apps/block_scout_web/test/support/fixture/smart_contract/compiler_tests.json create mode 100644 apps/block_scout_web/test/support/fixture/smart_contract/contract_with_lib.json create mode 100644 apps/block_scout_web/test/support/fixture/smart_contract/solc_bin.json create mode 100644 apps/block_scout_web/test/support/subscription_case.ex create mode 100644 apps/block_scout_web/test/test_helper.exs create mode 100644 apps/ethereum_jsonrpc/.gitignore create mode 100644 apps/ethereum_jsonrpc/README.md create mode 100644 apps/ethereum_jsonrpc/config/config.exs create mode 100644 apps/ethereum_jsonrpc/config/dev.exs create mode 100644 apps/ethereum_jsonrpc/config/prod.exs create mode 100644 apps/ethereum_jsonrpc/config/runtime/test.exs create mode 100644 apps/ethereum_jsonrpc/config/test.exs create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/application.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/arbitrum.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/besu.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/besu/trace.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/besu/traces.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block/by_hash.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block/by_nephew.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block/by_number.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block/by_tag.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/decode_error.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/erigon.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_balance.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_balances.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_beneficiaries.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_beneficiary.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_code.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/fetched_codes.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/ganache.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/call.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/calls.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth/tracer.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http/httpoison.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/ipc.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/log.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/logs.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind/trace.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind/trace/action.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind/trace/result.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/nethermind/traces.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/pending_transaction.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipts.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/request_coordinator.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/rolling_window.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/subscription.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/trace_replay_block_transactions.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/tracer.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transactions.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transport.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/uncle.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/uncles.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/variant.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/registration.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/web_socket_client.ex create mode 100644 apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/web_socket_client/options.ex create mode 100644 apps/ethereum_jsonrpc/lib/rsk.ex create mode 100644 apps/ethereum_jsonrpc/mix.exs create mode 100644 apps/ethereum_jsonrpc/priv/js/ethereum_jsonrpc/geth/debug_traceTransaction/tracer.js create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/block_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/blocks_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/contract_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/encoder_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/fetched_beneficiaries_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth/call_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth/calls_tests.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/geth_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/http/mox_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/log_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/mox_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/nethermind/trace/action_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/nethermind/trace/result_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/nethermind/trace_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/nethermind_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/receipt_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/receipts_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/request_coordinator_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/rolling_window_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/transaction_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/transactions_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/uncle_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/uncles_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket/web_socket_client_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket_test.exs create mode 100644 apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs create mode 100644 apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case.ex create mode 100644 apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/geth/http_websocket.ex create mode 100644 apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/geth/mox.ex create mode 100644 apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/nethermind/http_websocket.ex create mode 100644 apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/nethermind/mox.ex create mode 100644 apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/http/case.ex create mode 100644 apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/web_socket/case.ex create mode 100644 apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/web_socket/case/geth.ex create mode 100644 apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/web_socket/case/mox.ex create mode 100644 apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/web_socket/case/nethermind.ex create mode 100644 apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/web_socket/cowboy/websocket_handler.ex create mode 100644 apps/ethereum_jsonrpc/test/test_helper.exs create mode 100644 apps/explorer/.gitignore create mode 100644 apps/explorer/.sobelow-conf create mode 100644 apps/explorer/README.md create mode 100644 apps/explorer/benchmarks/explorer/chain/recent_collated_transactions.benchee create mode 100644 apps/explorer/benchmarks/explorer/chain/recent_collated_transactions.exs create mode 100644 apps/explorer/config/config.exs create mode 100644 apps/explorer/config/dev.exs create mode 100644 apps/explorer/config/dev/arbitrum.exs create mode 100644 apps/explorer/config/dev/besu.exs create mode 100644 apps/explorer/config/dev/erigon.exs create mode 100644 apps/explorer/config/dev/ganache.exs create mode 100644 apps/explorer/config/dev/geth.exs create mode 100644 apps/explorer/config/dev/nethermind.exs create mode 100644 apps/explorer/config/dev/rsk.exs create mode 100644 apps/explorer/config/prod.exs create mode 100644 apps/explorer/config/prod/arbitrum.exs create mode 100644 apps/explorer/config/prod/besu.exs create mode 100644 apps/explorer/config/prod/erigon.exs create mode 100644 apps/explorer/config/prod/ganache.exs create mode 100644 apps/explorer/config/prod/geth.exs create mode 100644 apps/explorer/config/prod/nethermind.exs create mode 100644 apps/explorer/config/prod/rsk.exs create mode 100644 apps/explorer/config/runtime/test.exs create mode 100644 apps/explorer/config/test.exs create mode 100644 apps/explorer/config/test/arbitrum.exs create mode 100644 apps/explorer/config/test/besu.exs create mode 100644 apps/explorer/config/test/erigon.exs create mode 100644 apps/explorer/config/test/ganache.exs create mode 100644 apps/explorer/config/test/geth.exs create mode 100644 apps/explorer/config/test/nethermind.exs create mode 100644 apps/explorer/config/test/rsk.exs create mode 100644 apps/explorer/coveralls.json create mode 100644 apps/explorer/lib/encrypt.ex create mode 100644 apps/explorer/lib/explorer.ex create mode 100644 apps/explorer/lib/explorer/account.ex create mode 100644 apps/explorer/lib/explorer/account/api/key.ex create mode 100644 apps/explorer/lib/explorer/account/api/plan.ex create mode 100644 apps/explorer/lib/explorer/account/custom_abi.ex create mode 100644 apps/explorer/lib/explorer/account/identity.ex create mode 100644 apps/explorer/lib/explorer/account/notifier/email.ex create mode 100644 apps/explorer/lib/explorer/account/notifier/forbidden_address.ex create mode 100644 apps/explorer/lib/explorer/account/notifier/notify.ex create mode 100644 apps/explorer/lib/explorer/account/notifier/summary.ex create mode 100644 apps/explorer/lib/explorer/account/notify.ex create mode 100644 apps/explorer/lib/explorer/account/public_tags_request.ex create mode 100644 apps/explorer/lib/explorer/account/tag_address.ex create mode 100644 apps/explorer/lib/explorer/account/tag_transaction.ex create mode 100644 apps/explorer/lib/explorer/account/watchlist.ex create mode 100644 apps/explorer/lib/explorer/account/watchlist_address.ex create mode 100644 apps/explorer/lib/explorer/account/watchlist_notification.ex create mode 100644 apps/explorer/lib/explorer/accounts/accounts.ex create mode 100644 apps/explorer/lib/explorer/accounts/user.ex create mode 100644 apps/explorer/lib/explorer/accounts/user/authenticate.ex create mode 100644 apps/explorer/lib/explorer/accounts/user/registration.ex create mode 100644 apps/explorer/lib/explorer/accounts/user_contact.ex create mode 100644 apps/explorer/lib/explorer/admin.ex create mode 100644 apps/explorer/lib/explorer/admin/administrator.ex create mode 100644 apps/explorer/lib/explorer/admin/recovery.ex create mode 100644 apps/explorer/lib/explorer/admin/role.ex create mode 100644 apps/explorer/lib/explorer/application.ex create mode 100644 apps/explorer/lib/explorer/chain.ex create mode 100644 apps/explorer/lib/explorer/chain/address.ex create mode 100644 apps/explorer/lib/explorer/chain/address/coin_balance.ex create mode 100644 apps/explorer/lib/explorer/chain/address/coin_balance_daily.ex create mode 100644 apps/explorer/lib/explorer/chain/address/current_token_balance.ex create mode 100644 apps/explorer/lib/explorer/chain/address/name.ex create mode 100644 apps/explorer/lib/explorer/chain/address/token.ex create mode 100644 apps/explorer/lib/explorer/chain/address/token_balance.ex create mode 100644 apps/explorer/lib/explorer/chain/address_internal_transaction_csv_exporter.ex create mode 100644 apps/explorer/lib/explorer/chain/address_log_csv_exporter.ex create mode 100644 apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex create mode 100644 apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex create mode 100644 apps/explorer/lib/explorer/chain/block.ex create mode 100644 apps/explorer/lib/explorer/chain/block/emission_reward.ex create mode 100644 apps/explorer/lib/explorer/chain/block/range.ex create mode 100644 apps/explorer/lib/explorer/chain/block/reward.ex create mode 100644 apps/explorer/lib/explorer/chain/block/reward/address_type.ex create mode 100644 apps/explorer/lib/explorer/chain/block/second_degree_relation.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/accounts.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/address_sum.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/address_sum_minus_burnt.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/block.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/block_number.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/blocks.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/contracts_counter.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/gas_price_oracle.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/gas_usage.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/min_missing_block.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/net_version.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/new_contracts_counter.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/new_verified_contracts_counter.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/token_exchange_rate.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/transaction.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/transactions.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/uncles.ex create mode 100644 apps/explorer/lib/explorer/chain/cache/verified_contracts_counter.ex create mode 100644 apps/explorer/lib/explorer/chain/contract_method.ex create mode 100644 apps/explorer/lib/explorer/chain/currency_helpers.ex create mode 100644 apps/explorer/lib/explorer/chain/data.ex create mode 100644 apps/explorer/lib/explorer/chain/decompiled_smart_contract.ex create mode 100644 apps/explorer/lib/explorer/chain/events/db_sender.ex create mode 100644 apps/explorer/lib/explorer/chain/events/listener.ex create mode 100644 apps/explorer/lib/explorer/chain/events/publisher.ex create mode 100644 apps/explorer/lib/explorer/chain/events/simple_sender.ex create mode 100644 apps/explorer/lib/explorer/chain/events/subscriber.ex create mode 100644 apps/explorer/lib/explorer/chain/gas.ex create mode 100644 apps/explorer/lib/explorer/chain/hash.ex create mode 100644 apps/explorer/lib/explorer/chain/hash/address.ex create mode 100644 apps/explorer/lib/explorer/chain/hash/full.ex create mode 100644 apps/explorer/lib/explorer/chain/hash/nonce.ex create mode 100644 apps/explorer/lib/explorer/chain/import.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/address/coin_balances.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/address/coin_balances_daily.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/address/current_token_balances.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/address/token_balances.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/addresses.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/block/rewards.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/block/second_degree_relations.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/blocks.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/logs.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/token_transfers.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/tokens.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/transaction/forks.ex create mode 100644 apps/explorer/lib/explorer/chain/import/runner/transactions.ex create mode 100644 apps/explorer/lib/explorer/chain/import/stage.ex create mode 100644 apps/explorer/lib/explorer/chain/import/stage/address_referencing.ex create mode 100644 apps/explorer/lib/explorer/chain/import/stage/addresses.ex create mode 100644 apps/explorer/lib/explorer/chain/import/stage/block_following.ex create mode 100644 apps/explorer/lib/explorer/chain/import/stage/block_pending.ex create mode 100644 apps/explorer/lib/explorer/chain/import/stage/block_referencing.ex create mode 100644 apps/explorer/lib/explorer/chain/internal_transaction.ex create mode 100644 apps/explorer/lib/explorer/chain/internal_transaction/action.ex create mode 100644 apps/explorer/lib/explorer/chain/internal_transaction/call_type.ex create mode 100644 apps/explorer/lib/explorer/chain/internal_transaction/result.ex create mode 100644 apps/explorer/lib/explorer/chain/internal_transaction/type.ex create mode 100644 apps/explorer/lib/explorer/chain/log.ex create mode 100644 apps/explorer/lib/explorer/chain/map_cache.ex create mode 100644 apps/explorer/lib/explorer/chain/method_identifier.ex create mode 100644 apps/explorer/lib/explorer/chain/ordered_cache.ex create mode 100644 apps/explorer/lib/explorer/chain/pending_block_operation.ex create mode 100644 apps/explorer/lib/explorer/chain/smart_contract.ex create mode 100644 apps/explorer/lib/explorer/chain/smart_contract/external_library.ex create mode 100644 apps/explorer/lib/explorer/chain/smart_contract/verification_status.ex create mode 100644 apps/explorer/lib/explorer/chain/smart_contract_additional_sources.ex create mode 100644 apps/explorer/lib/explorer/chain/supply.ex create mode 100644 apps/explorer/lib/explorer/chain/supply/exchange_rate.ex create mode 100644 apps/explorer/lib/explorer/chain/supply/proof_of_authority.ex create mode 100644 apps/explorer/lib/explorer/chain/supply/rsk.ex create mode 100644 apps/explorer/lib/explorer/chain/token.ex create mode 100644 apps/explorer/lib/explorer/chain/token/instance.ex create mode 100644 apps/explorer/lib/explorer/chain/token_transfer.ex create mode 100644 apps/explorer/lib/explorer/chain/transaction.ex create mode 100644 apps/explorer/lib/explorer/chain/transaction/fork.ex create mode 100644 apps/explorer/lib/explorer/chain/transaction/history/historian.ex create mode 100644 apps/explorer/lib/explorer/chain/transaction/history/transaction_stats.ex create mode 100644 apps/explorer/lib/explorer/chain/transaction/status.ex create mode 100644 apps/explorer/lib/explorer/chain/wei.ex create mode 100644 apps/explorer/lib/explorer/chain_spec/genesis_data.ex create mode 100644 apps/explorer/lib/explorer/chain_spec/geth/importer.ex create mode 100644 apps/explorer/lib/explorer/chain_spec/parity/importer.ex create mode 100644 apps/explorer/lib/explorer/chain_spec/poa/importer.ex create mode 100644 apps/explorer/lib/explorer/counters/address_gas_usage_counter.ex create mode 100644 apps/explorer/lib/explorer/counters/address_token_transfers_counter.ex create mode 100644 apps/explorer/lib/explorer/counters/address_tokens_usd_sum.ex create mode 100644 apps/explorer/lib/explorer/counters/address_transactions_counter.ex create mode 100644 apps/explorer/lib/explorer/counters/addresses_counter.ex create mode 100644 apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex create mode 100644 apps/explorer/lib/explorer/counters/average_block_time.ex create mode 100644 apps/explorer/lib/explorer/counters/average_block_time_duration_format.ex create mode 100644 apps/explorer/lib/explorer/counters/block_burned_fee_counter.ex create mode 100644 apps/explorer/lib/explorer/counters/block_priority_fee_counter.ex create mode 100644 apps/explorer/lib/explorer/counters/helper.ex create mode 100644 apps/explorer/lib/explorer/counters/last_fetched_counter.ex create mode 100644 apps/explorer/lib/explorer/counters/token_holders_counter.ex create mode 100644 apps/explorer/lib/explorer/counters/token_transfers_counter.ex create mode 100644 apps/explorer/lib/explorer/custom_contracts_helpers.ex create mode 100644 apps/explorer/lib/explorer/encrypted/address_hash.ex create mode 100644 apps/explorer/lib/explorer/encrypted/binary.ex create mode 100644 apps/explorer/lib/explorer/encrypted/transaction_hash.ex create mode 100644 apps/explorer/lib/explorer/encrypted/types/address_hash.ex create mode 100644 apps/explorer/lib/explorer/encrypted/types/transaction_hash.ex create mode 100644 apps/explorer/lib/explorer/env_var_translator.ex create mode 100644 apps/explorer/lib/explorer/eth_rpc.ex create mode 100644 apps/explorer/lib/explorer/etherscan.ex create mode 100644 apps/explorer/lib/explorer/etherscan/addresses.ex create mode 100644 apps/explorer/lib/explorer/etherscan/blocks.ex create mode 100644 apps/explorer/lib/explorer/etherscan/contracts.ex create mode 100644 apps/explorer/lib/explorer/etherscan/logs.ex create mode 100644 apps/explorer/lib/explorer/etherscan/rpc.ex create mode 100644 apps/explorer/lib/explorer/exchange_rates/exchange_rates.ex create mode 100644 apps/explorer/lib/explorer/exchange_rates/source.ex create mode 100644 apps/explorer/lib/explorer/exchange_rates/source/coin_gecko.ex create mode 100644 apps/explorer/lib/explorer/exchange_rates/source/coin_market_cap.ex create mode 100644 apps/explorer/lib/explorer/exchange_rates/token.ex create mode 100644 apps/explorer/lib/explorer/graphql.ex create mode 100644 apps/explorer/lib/explorer/history/historian.ex create mode 100644 apps/explorer/lib/explorer/history/process.ex create mode 100644 apps/explorer/lib/explorer/known_tokens/known_tokens.ex create mode 100644 apps/explorer/lib/explorer/known_tokens/source.ex create mode 100644 apps/explorer/lib/explorer/known_tokens/source/my_ether_wallet.ex create mode 100644 apps/explorer/lib/explorer/logger.ex create mode 100644 apps/explorer/lib/explorer/mailer.ex create mode 100644 apps/explorer/lib/explorer/market/history/cataloger.ex create mode 100644 apps/explorer/lib/explorer/market/history/source.ex create mode 100644 apps/explorer/lib/explorer/market/history/source/crypto_compare.ex create mode 100644 apps/explorer/lib/explorer/market/market.ex create mode 100644 apps/explorer/lib/explorer/market/market_history.ex create mode 100644 apps/explorer/lib/explorer/market/market_history_cache.ex create mode 100644 apps/explorer/lib/explorer/paging_options.ex create mode 100644 apps/explorer/lib/explorer/prometheus/instrumenter.ex create mode 100644 apps/explorer/lib/explorer/repo.ex create mode 100644 apps/explorer/lib/explorer/repo/config_helper.ex create mode 100644 apps/explorer/lib/explorer/repo/prometheus_logger.ex create mode 100644 apps/explorer/lib/explorer/schema.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/compiler_version.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/helper.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/reader.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/rust_verifier_interface.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/solc_downloader.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/vyper/code_compiler.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/vyper_downloader.ex create mode 100644 apps/explorer/lib/explorer/smart_contract/writer.ex create mode 100644 apps/explorer/lib/explorer/tags/address_tag.ex create mode 100644 apps/explorer/lib/explorer/tags/address_tag_cataloger.ex create mode 100644 apps/explorer/lib/explorer/tags/address_to_tag.ex create mode 100644 apps/explorer/lib/explorer/third_party_integrations/airtable.ex create mode 100644 apps/explorer/lib/explorer/third_party_integrations/sourcify.ex create mode 100644 apps/explorer/lib/explorer/token/balance_reader.ex create mode 100644 apps/explorer/lib/explorer/token/instance_metadata_retriever.ex create mode 100644 apps/explorer/lib/explorer/token/instance_owner_reader.ex create mode 100644 apps/explorer/lib/explorer/token/metadata_retriever.ex create mode 100644 apps/explorer/lib/explorer/tracer.ex create mode 100644 apps/explorer/lib/explorer/utility/event_notification.ex create mode 100644 apps/explorer/lib/explorer/validator/metadata_importer.ex create mode 100644 apps/explorer/lib/explorer/validator/metadata_processor.ex create mode 100644 apps/explorer/lib/explorer/validator/metadata_retriever.ex create mode 100644 apps/explorer/lib/explorer/vault.ex create mode 100644 apps/explorer/lib/release_tasks.ex create mode 100644 apps/explorer/mix.exs create mode 100644 apps/explorer/package-lock.json create mode 100644 apps/explorer/package.json create mode 100644 apps/explorer/priv/account/migrations/20211031164954_create_account_identities.exs create mode 100644 apps/explorer/priv/account/migrations/20211105114502_create_account_watchlists.exs create mode 100644 apps/explorer/priv/account/migrations/20211105130907_create_account_watchlist_addresses.exs create mode 100644 apps/explorer/priv/account/migrations/20211127212336_create_account_watchlist_notifications.exs create mode 100644 apps/explorer/priv/account/migrations/20211205220414_add_email_and_name_to_account_identity.exs create mode 100644 apps/explorer/priv/account/migrations/20220212222222_create_account_tag_addresses.exs create mode 100644 apps/explorer/priv/account/migrations/20220313133333_create_account_tag_transactions.exs create mode 100644 apps/explorer/priv/account/migrations/20220324213333_add_subject_to_watchlist_notifications.exs create mode 100644 apps/explorer/priv/account/migrations/20220407134152_add_api_keys_and_plans_tables.exs create mode 100644 apps/explorer/priv/account/migrations/20220510094118_add_custom_abis_table.exs create mode 100644 apps/explorer/priv/account/migrations/20220606194836_add_account_public_tags_requests.exs create mode 100644 apps/explorer/priv/account/migrations/20220620182600_add_account_identity_fields.exs create mode 100644 apps/explorer/priv/account/migrations/20220624142547_add_unique_constraints.exs create mode 100644 apps/explorer/priv/account/migrations/20220705195240_migrate_public_tags_addresses_to_array.exs create mode 100644 apps/explorer/priv/account/migrations/20220706114430_encrypt_account_data.exs create mode 100644 apps/explorer/priv/account/migrations/20220706153506_remove_unencrypted_fields.exs create mode 100644 apps/explorer/priv/account/migrations/20220706211444_set_new_indexes.exs create mode 100644 apps/explorer/priv/account/migrations/20220905195203_remove_guardian_tokens.exs create mode 100755 apps/explorer/priv/compile_solc.js create mode 100644 apps/explorer/priv/compile_solc_standard_json_input.js create mode 100644 apps/explorer/priv/contracts_abi/poa/metadata.json create mode 100644 apps/explorer/priv/contracts_abi/poa/validators.json create mode 100644 apps/explorer/priv/contracts_abi/posdao/BlockRewardAuRa.json create mode 100644 apps/explorer/priv/contracts_abi/posdao/README.md create mode 100644 apps/explorer/priv/contracts_abi/posdao/StakingAuRa.json create mode 100644 apps/explorer/priv/contracts_abi/posdao/Token.json create mode 100644 apps/explorer/priv/contracts_abi/posdao/ValidatorSetAuRa.json create mode 100644 apps/explorer/priv/repo/migrations/.gitkeep create mode 100644 apps/explorer/priv/repo/migrations/20180117221921_create_address.exs create mode 100644 apps/explorer/priv/repo/migrations/20180117221922_create_blocks.exs create mode 100644 apps/explorer/priv/repo/migrations/20180117221923_create_transactions.exs create mode 100644 apps/explorer/priv/repo/migrations/20180212222309_create_logs.exs create mode 100644 apps/explorer/priv/repo/migrations/20180221001948_create_internal_transactions.exs create mode 100644 apps/explorer/priv/repo/migrations/20180424203101_create_market_history.exs create mode 100644 apps/explorer/priv/repo/migrations/20180508183700_create_users.exs create mode 100644 apps/explorer/priv/repo/migrations/20180508191045_create_user_contacts.exs create mode 100644 apps/explorer/priv/repo/migrations/20180518221256_create_smart_contract_verified.exs create mode 100644 apps/explorer/priv/repo/migrations/20180522154252_create_btree_gist_extension.exs create mode 100644 apps/explorer/priv/repo/migrations/20180522154253_create_block_rewards.exs create mode 100644 apps/explorer/priv/repo/migrations/20180606135149_create_tokens.exs create mode 100644 apps/explorer/priv/repo/migrations/20180606135150_create_token_transfers.exs create mode 100644 apps/explorer/priv/repo/migrations/20180626143840_add_inserted_at_index_to_blocks.exs create mode 100644 apps/explorer/priv/repo/migrations/20180717204948_create_address_coin_balances.exs create mode 100644 apps/explorer/priv/repo/migrations/20180817021704_create_address_token_balances.exs create mode 100644 apps/explorer/priv/repo/migrations/20180821142139_create_address_names.exs create mode 100644 apps/explorer/priv/repo/migrations/20180917182319_create_block_second_degree_relations.exs create mode 100644 apps/explorer/priv/repo/migrations/20180918200001_create_transaction_fork.exs create mode 100644 apps/explorer/priv/repo/migrations/20180919175123_alter_token_decimals_to_bigint.exs create mode 100644 apps/explorer/priv/repo/migrations/20181008195723_create_administrators.exs create mode 100644 apps/explorer/priv/repo/migrations/20181011193212_add_fields_to_internal_transactions.exs create mode 100644 apps/explorer/priv/repo/migrations/20181015173318_add_case_insensitive_extension.exs create mode 100644 apps/explorer/priv/repo/migrations/20181015173319_modify_users_username.exs create mode 100644 apps/explorer/priv/repo/migrations/20181016163236_modify_user_contacts_email.exs create mode 100644 apps/explorer/priv/repo/migrations/20181017141409_add_index_to_internal_transaction_table.exs create mode 100644 apps/explorer/priv/repo/migrations/20181024141113_internal_transactions_composite_primary_key.exs create mode 100644 apps/explorer/priv/repo/migrations/20181024164623_logs_composite_primary_key.exs create mode 100644 apps/explorer/priv/repo/migrations/20181024172010_token_transfers_composite_primary_key.exs create mode 100644 apps/explorer/priv/repo/migrations/20181026180921_create_address_current_token_balances.exs create mode 100644 apps/explorer/priv/repo/migrations/20181029174420_update_tokens_table_decimals_from_bigint_to_numeric.exs create mode 100644 apps/explorer/priv/repo/migrations/20181106152300_add_nonce_to_addresses.exs create mode 100644 apps/explorer/priv/repo/migrations/20181107164103_eip6.exs create mode 100644 apps/explorer/priv/repo/migrations/20181108205650_additional_internal_transaction_constraints.exs create mode 100644 apps/explorer/priv/repo/migrations/20181121170616_add_block_number_to_token_transfers.exs create mode 100644 apps/explorer/priv/repo/migrations/20181126203826_add_index_to_addresses.exs create mode 100644 apps/explorer/priv/repo/migrations/20181206200140_rename_block_rewards_to_emission_rewards.exs create mode 100644 apps/explorer/priv/repo/migrations/20181206200312_create_new_block_rewards.exs create mode 100644 apps/explorer/priv/repo/migrations/20181212115448_add_indexes_to_token_transfers.exs create mode 100644 apps/explorer/priv/repo/migrations/20181213111656_add_metadata_field_to_address_names.exs create mode 100644 apps/explorer/priv/repo/migrations/20181221143000_create_blocks_miner_hash_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20181221145054_add_contract_methods.exs create mode 100644 apps/explorer/priv/repo/migrations/20190102141900_index_current_token_holders.exs create mode 100644 apps/explorer/priv/repo/migrations/20190114204640_add_tokens_holder_count.exs create mode 100644 apps/explorer/priv/repo/migrations/20190116082843_add_created_contract_indexed_at_to_transactions.exs create mode 100644 apps/explorer/priv/repo/migrations/20190118040301_create_tokens_primary_key.exs create mode 100644 apps/explorer/priv/repo/migrations/20190118152240_block_second_degree_relations_composite_primary_key.exs create mode 100644 apps/explorer/priv/repo/migrations/20190122125815_change_transaction_error_constraint.exs create mode 100644 apps/explorer/priv/repo/migrations/20190124082812_add_index_on_transaction_nonce_and_from_address_hash.exs create mode 100644 apps/explorer/priv/repo/migrations/20190208113202_add_unique_index_to_rewards.exs create mode 100644 apps/explorer/priv/repo/migrations/20190208143201_add_index_on_address_hash_block_number_and_token_contract_address_hash_for_current_token_balance.exs create mode 100644 apps/explorer/priv/repo/migrations/20190213180502_add_earliest_processing_start_to_transactions.exs create mode 100644 apps/explorer/priv/repo/migrations/20190214135850_add_index_on_block_number_to_address_token_balances.exs create mode 100644 apps/explorer/priv/repo/migrations/20190215080049_add_index_on_fetched_coin_balance_to_addresses.exs create mode 100644 apps/explorer/priv/repo/migrations/20190215093358_add_compound_index_address_token_balances.exs create mode 100644 apps/explorer/priv/repo/migrations/20190215105501_add_constructor_arguments_to_smart_contracts.exs create mode 100644 apps/explorer/priv/repo/migrations/20190219082636_add_indexes_for_block_reward_query.exs create mode 100644 apps/explorer/priv/repo/migrations/20190228102650_add_index_created_contract_address_hash.exs create mode 100644 apps/explorer/priv/repo/migrations/20190228152333_change_constructor_arguments_to_text.exs create mode 100644 apps/explorer/priv/repo/migrations/20190228220746_add_internal_transactions_indexed_at_to_blocks.exs create mode 100644 apps/explorer/priv/repo/migrations/20190301095620_remove_duplicated_indexes.exs create mode 100644 apps/explorer/priv/repo/migrations/20190301120328_add_index_to_consensus.exs create mode 100644 apps/explorer/priv/repo/migrations/20190305095926_add_index_to_value_fetched_at.exs create mode 100644 apps/explorer/priv/repo/migrations/20190313085740_add_index_symobl_in_tokens.exs create mode 100644 apps/explorer/priv/repo/migrations/20190313103912_change_transactions_v_column_type.exs create mode 100644 apps/explorer/priv/repo/migrations/20190314084907_add_index_to_to_address_hash.exs create mode 100644 apps/explorer/priv/repo/migrations/20190318151809_add_inserted_at_index_to_accounts.exs create mode 100644 apps/explorer/priv/repo/migrations/20190319081821_create_decompiled_smart_contracts.exs create mode 100644 apps/explorer/priv/repo/migrations/20190321185644_add_old_value_for_current_token_balances.exs create mode 100644 apps/explorer/priv/repo/migrations/20190325081658_remove_unique_address_hash_decompiled_contracts.exs create mode 100644 apps/explorer/priv/repo/migrations/20190403080447_add_full_text_search_tokens.exs create mode 100644 apps/explorer/priv/repo/migrations/20190421143300_add_index_to_bsdr.exs create mode 100644 apps/explorer/priv/repo/migrations/20190424170833_change_block_size_to_nullable.exs create mode 100644 apps/explorer/priv/repo/migrations/20190508152922_add_old_block_hash_for_transactions.exs create mode 100644 apps/explorer/priv/repo/migrations/20190513134025_add_refetch_needed_to_block.exs create mode 100644 apps/explorer/priv/repo/migrations/20190516140202_add_address_hash_index_to_decompiled_smart_contracts.exs create mode 100644 apps/explorer/priv/repo/migrations/20190516160535_add_decompiled_and_verified_flag_to_addresses.exs create mode 100644 apps/explorer/priv/repo/migrations/20190521104412_create_staking_pools.exs create mode 100644 apps/explorer/priv/repo/migrations/20190523112839_create_staking_pools_delegators.exs create mode 100644 apps/explorer/priv/repo/migrations/20190613065856_add_tx_hash_inserted_at_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20190619154943_reduce_transaction_status_constraint.exs create mode 100644 apps/explorer/priv/repo/migrations/20190625085852_add_additional_contract_fields.exs create mode 100644 apps/explorer/priv/repo/migrations/20190709043832_create_transaction_stats.exs create mode 100644 apps/explorer/priv/repo/migrations/20190709103104_add_external_libraries_to_smart_contracts.exs create mode 100644 apps/explorer/priv/repo/migrations/20190807111216_remove_duplicate_indexes.exs create mode 100644 apps/explorer/priv/repo/migrations/20190807113117_create_suggested_indexes.exs create mode 100644 apps/explorer/priv/repo/migrations/20190827120224_add_index_on_token_transfer_token_id.exs create mode 100644 apps/explorer/priv/repo/migrations/20190905083522_create_token_instances.exs create mode 100644 apps/explorer/priv/repo/migrations/20190910170703_create_indexes_for_block_number_in_token_transfers_and_transactions.exs create mode 100644 apps/explorer/priv/repo/migrations/20191007082500_add_indexes_for_token_instances_query.exs create mode 100644 apps/explorer/priv/repo/migrations/20191009121635_add_token_transfer_sorting_indexes.exs create mode 100644 apps/explorer/priv/repo/migrations/20191010075740_add_error_to_token_instances.exs create mode 100644 apps/explorer/priv/repo/migrations/20191018120546_create_pending_block_operations.exs create mode 100644 apps/explorer/priv/repo/migrations/20191018140054_add_pending_internal_txs_operation.exs create mode 100644 apps/explorer/priv/repo/migrations/20191121064805_add_block_hash_and_block_index_to_logs.exs create mode 100644 apps/explorer/priv/repo/migrations/20191122062035_add_block_hash_to_token_transfers.exs create mode 100644 apps/explorer/priv/repo/migrations/20191128124415_remove_duplicate_indexes_token_entities.exs create mode 100644 apps/explorer/priv/repo/migrations/20191203112646_internal_transactions_add_to_address_hash_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20191208135613_block_rewards_block_hash_partial_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20191218120138_logs_block_number_index_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20191220113006_pending_block_operations_block_hash_partial_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20200214152058_add_token_id_to_token_balances.exs create mode 100644 apps/explorer/priv/repo/migrations/20200410115841_create_index_address_current_token_balances_token_contract_address_hash_value.exs create mode 100644 apps/explorer/priv/repo/migrations/20200410141202_create_index_token_transfers_token_contract_address_hash_block_number.exs create mode 100644 apps/explorer/priv/repo/migrations/20200421102450_pending_txs.exs create mode 100644 apps/explorer/priv/repo/migrations/20200424070607_drop_block_rewards_address_hash_address_type_block_hash_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20200518075748_create_index_blocks_miner_hash_number_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20200521090250_recreate_staking_tables.exs create mode 100644 apps/explorer/priv/repo/migrations/20200521170002_create_token_transfers_token_contract_address_hash_token_id_block_number_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20200525115811_address_coin_balances_daily.exs create mode 100644 apps/explorer/priv/repo/migrations/20200527144742_add_counters_table.exs create mode 100644 apps/explorer/priv/repo/migrations/20200608075122_alter_transactions_add_error_reason.exs create mode 100644 apps/explorer/priv/repo/migrations/20200806125649_token_add_bridged_column.exs create mode 100644 apps/explorer/priv/repo/migrations/20200807064700_bridged_tokens_table.exs create mode 100644 apps/explorer/priv/repo/migrations/20200812143050_add_addresses_contract_code_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20200904075501_add_bridged_token_custom_metadata.exs create mode 100644 apps/explorer/priv/repo/migrations/20200929075625_add_bridged_token_type.exs create mode 100644 apps/explorer/priv/repo/migrations/20201026093652_transactions_stat_add_gas_usage_column.exs create mode 100644 apps/explorer/priv/repo/migrations/20201214203532_support_sourcify.exs create mode 100644 apps/explorer/priv/repo/migrations/20210219080523_add_tags.exs create mode 100644 apps/explorer/priv/repo/migrations/20210226154732_add_exchange_rate_column_to_bridged_tokens.exs create mode 100644 apps/explorer/priv/repo/migrations/20210309104122_add_bridged_token_custom_cap.exs create mode 100644 apps/explorer/priv/repo/migrations/20210331074008_add_pool_name_description.exs create mode 100644 apps/explorer/priv/repo/migrations/20210422115740_add_token_id_to_current_token_balances.exs create mode 100644 apps/explorer/priv/repo/migrations/20210423084253_address_current_token_balances_add_token_id_to_unique_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20210423091652_address_token_balances_add_token_id_to_unique_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20210423094801_address_token_balances_change_unfetched_token_balances_unique_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20210423115108_extend_token_transfers_for_erc1155.exs create mode 100644 apps/explorer/priv/repo/migrations/20210524165427_min_missing_block_number.exs create mode 100644 apps/explorer/priv/repo/migrations/20210527093756_transaction_stats_add_total_fee_column.exs create mode 100644 apps/explorer/priv/repo/migrations/20210616120552_smart_contracts_add_is_vyper_flag.exs create mode 100644 apps/explorer/priv/repo/migrations/20210701084814_support_partial_match.exs create mode 100644 apps/explorer/priv/repo/migrations/20210811140837_add_1559_support.exs create mode 100644 apps/explorer/priv/repo/migrations/20210823144531_tokens_add_metadata_fetch_flag.exs create mode 100644 apps/explorer/priv/repo/migrations/20210916194004_add_file_path_for_sourcify_contracts.exs create mode 100644 apps/explorer/priv/repo/migrations/20211006121008_add_block_is_empty_flag.exs create mode 100644 apps/explorer/priv/repo/migrations/20211013190346_remove_duplicates_of_current_token_balances.exs create mode 100644 apps/explorer/priv/repo/migrations/20211017135545_migrate_optimization_runs_to_int8.exs create mode 100644 apps/explorer/priv/repo/migrations/20211018072347_add_is_empty_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20211018073652_add_token_balances_contract_address_hash_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20211018164843_transactions_block_number_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20211018170533_add_address_token_balances_address_hash_token_contract_address_hash_block_number_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20211018170638_add_logs_address_hash_transaction_hash_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20211029085117_drop_block_rewards_block_hash_partial_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20211115164817_add_check_for_bytecode_actuality.exs create mode 100644 apps/explorer/priv/repo/migrations/20211203115010_add_contract_verification_status_table.exs create mode 100644 apps/explorer/priv/repo/migrations/20211204184037_address_add_gas_used.exs create mode 100644 apps/explorer/priv/repo/migrations/20211206071033_modify_address_gas_used_bigint.exs create mode 100644 apps/explorer/priv/repo/migrations/20211210184136_add_display_name_to_address_tag.exs create mode 100644 apps/explorer/priv/repo/migrations/20211217201759_add_has_error_in_iternal_txs_field_to_transaction.exs create mode 100644 apps/explorer/priv/repo/migrations/20220111085751_address_add_counters.exs create mode 100644 apps/explorer/priv/repo/migrations/20220303083252_smart_contracts_contract_code_md5.exs create mode 100644 apps/explorer/priv/repo/migrations/20220306091504_add_implementation_name.exs create mode 100644 apps/explorer/priv/repo/migrations/20220622114402_remove_staking_tables.exs create mode 100644 apps/explorer/priv/repo/migrations/20220622140604_remove_bridged_tokens.exs create mode 100644 apps/explorer/priv/repo/migrations/20220706101103_address_coin_balances_daily_add_primary_key.exs create mode 100644 apps/explorer/priv/repo/migrations/20220706102257_address_coin_balances_add_primary_key.exs create mode 100644 apps/explorer/priv/repo/migrations/20220706102504_transactions_forks_add_primary_key.exs create mode 100644 apps/explorer/priv/repo/migrations/20220706102746_block_rewards_add_primary_key.exs create mode 100644 apps/explorer/priv/repo/migrations/20220706105925_emission_rewards_add_primary_key.exs create mode 100644 apps/explorer/priv/repo/migrations/20220706111510_address_names_add_primary_key.exs create mode 100644 apps/explorer/priv/repo/migrations/20220804114005_create_event_notifications.exs create mode 100644 apps/explorer/priv/repo/migrations/20220902083436_extend_token_name_type.exs create mode 100644 apps/explorer/priv/repo/migrations/20220902103213_create_index_token_transfers_token_ids.exs create mode 100644 apps/explorer/priv/repo/migrations/20220902103527_create_indexes_token_instances_token_contract_address_hash_token_id.exs create mode 100644 apps/explorer/priv/repo/migrations/20220919105140_add_method_id_index.exs create mode 100644 apps/explorer/priv/repo/migrations/20220926122620_extend_token_symbol_type.exs create mode 100644 apps/explorer/priv/repo/migrations/scripts/20181107164103_eip6.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/20181108205650_additional_internal_transaction_constraints.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/20181108205650_large_additional_internal_transaction_constraints.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/20181121170616_token_transfers_update_block_number_in_batches.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/20181126182700_migrate_address_nonce.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/20190326202921_lose_consensus_for_invalid_blocks.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/20191018140054_send_block_hash_block_index_dups_to_carnitine.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/insert_address_current_token_balances_in_batches.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/internal_transaction_update_in_batches.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/transaction_update_in_batches.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/update_address_current_token_balances_in_batches.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/update_new_tokens_holder_count_in_batches.sql create mode 100644 apps/explorer/priv/repo/migrations/scripts/update_replaced_transaction.sql create mode 100644 apps/explorer/priv/repo/seeds.exs create mode 100644 apps/explorer/priv/repo_api/migrations/.gitkeep create mode 100644 apps/explorer/test/explorer/account/notify/email_test.exs create mode 100644 apps/explorer/test/explorer/account/notify/notify_test.exs create mode 100644 apps/explorer/test/explorer/account/notify/summary_test.exs create mode 100644 apps/explorer/test/explorer/accounts/accounts_test.exs create mode 100644 apps/explorer/test/explorer/accounts/user_contact_test.exs create mode 100644 apps/explorer/test/explorer/accounts/user_test.exs create mode 100644 apps/explorer/test/explorer/admin/administrator/role_test.exs create mode 100644 apps/explorer/test/explorer/admin/recovery_test.exs create mode 100644 apps/explorer/test/explorer/admin_test.exs create mode 100644 apps/explorer/test/explorer/chain/address/coin_balance_test.exs create mode 100644 apps/explorer/test/explorer/chain/address/current_token_balance_test.exs create mode 100644 apps/explorer/test/explorer/chain/address/token_balance_test.exs create mode 100644 apps/explorer/test/explorer/chain/address/token_test.exs create mode 100644 apps/explorer/test/explorer/chain/address_internal_transaction_csv_exporter_test.exs create mode 100644 apps/explorer/test/explorer/chain/address_log_csv_exporter_test.exs create mode 100644 apps/explorer/test/explorer/chain/address_test.exs create mode 100644 apps/explorer/test/explorer/chain/address_token_transfer_csv_exporter_test.exs create mode 100644 apps/explorer/test/explorer/chain/address_transaction_csv_exporter_test.exs create mode 100644 apps/explorer/test/explorer/chain/block/range_test.exs create mode 100644 apps/explorer/test/explorer/chain/block/second_degree_relation_test.exs create mode 100644 apps/explorer/test/explorer/chain/block_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/accounts_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/address_sum_minus_burnt.exs create mode 100644 apps/explorer/test/explorer/chain/cache/address_sum_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/block_number_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/block_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/blocks_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/contracts_counter_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/gas_price_oracle_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/new_contracts_counter_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/new_verified_contracts_counter_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/transaction_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/transactions_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/uncles_test.exs create mode 100644 apps/explorer/test/explorer/chain/cache/verified_contracts_counter_test.exs create mode 100644 apps/explorer/test/explorer/chain/data_test.exs create mode 100644 apps/explorer/test/explorer/chain/decompiled_smart_contract_test.exs create mode 100644 apps/explorer/test/explorer/chain/events/publisher_test.exs create mode 100644 apps/explorer/test/explorer/chain/events/subscriber_test.exs create mode 100644 apps/explorer/test/explorer/chain/hash/address_test.exs create mode 100644 apps/explorer/test/explorer/chain/hash/full_test.exs create mode 100644 apps/explorer/test/explorer/chain/hash/nonce_test.exs create mode 100644 apps/explorer/test/explorer/chain/hash_test.exs create mode 100644 apps/explorer/test/explorer/chain/import/runner/address/current_token_balances_test.exs create mode 100644 apps/explorer/test/explorer/chain/import/runner/address/token_balances_test.exs create mode 100644 apps/explorer/test/explorer/chain/import/runner/addresses_test.exs create mode 100644 apps/explorer/test/explorer/chain/import/runner/blocks_test.exs create mode 100644 apps/explorer/test/explorer/chain/import/runner/internal_transactions_test.exs create mode 100644 apps/explorer/test/explorer/chain/import/runner/tokens_test.exs create mode 100644 apps/explorer/test/explorer/chain/import/runner/transactions_test.exs create mode 100644 apps/explorer/test/explorer/chain/import_test.exs create mode 100644 apps/explorer/test/explorer/chain/internal_transaction/call_type_test.exs create mode 100644 apps/explorer/test/explorer/chain/internal_transaction/type_test.exs create mode 100644 apps/explorer/test/explorer/chain/internal_transaction_test.exs create mode 100644 apps/explorer/test/explorer/chain/log_test.exs create mode 100644 apps/explorer/test/explorer/chain/supply/proof_of_authority_test.exs create mode 100644 apps/explorer/test/explorer/chain/supply/rsk_test.exs create mode 100644 apps/explorer/test/explorer/chain/token_test.exs create mode 100644 apps/explorer/test/explorer/chain/token_transfer_test.exs create mode 100644 apps/explorer/test/explorer/chain/transaction/fork_test.exs create mode 100644 apps/explorer/test/explorer/chain/transaction/history/historian_test.exs create mode 100644 apps/explorer/test/explorer/chain/transaction/history/transaction_stats_test.exs create mode 100644 apps/explorer/test/explorer/chain/transaction/status_test.exs create mode 100644 apps/explorer/test/explorer/chain/transaction_test.exs create mode 100644 apps/explorer/test/explorer/chain/wei_test.exs create mode 100644 apps/explorer/test/explorer/chain_spec/geth/importer_test.exs create mode 100644 apps/explorer/test/explorer/chain_spec/parity/importer_test.exs create mode 100644 apps/explorer/test/explorer/chain_test.exs create mode 100644 apps/explorer/test/explorer/counters/addresses_counter_test.exs create mode 100644 apps/explorer/test/explorer/counters/addresses_tokens_usd_sum_counter_test.exs create mode 100644 apps/explorer/test/explorer/counters/addresses_with_balance_counter_test.exs create mode 100644 apps/explorer/test/explorer/counters/average_block_time_test.exs create mode 100644 apps/explorer/test/explorer/etherscan/logs_test.exs create mode 100644 apps/explorer/test/explorer/etherscan_test.exs create mode 100644 apps/explorer/test/explorer/exchange_rates/exchange_rates_test.exs create mode 100644 apps/explorer/test/explorer/exchange_rates/source/coin_gecko_test.exs create mode 100644 apps/explorer/test/explorer/graphql_test.exs create mode 100644 apps/explorer/test/explorer/history/process_test.exs create mode 100644 apps/explorer/test/explorer/known_tokens/known_tokens_test.exs create mode 100644 apps/explorer/test/explorer/market/history/cataloger_test.exs create mode 100644 apps/explorer/test/explorer/market/history/source/crypto_compare_test.exs create mode 100644 apps/explorer/test/explorer/market/market_history_cache_test.exs create mode 100644 apps/explorer/test/explorer/market/market_test.exs create mode 100644 apps/explorer/test/explorer/repo/config_helper_test.exs create mode 100644 apps/explorer/test/explorer/repo_test.exs create mode 100644 apps/explorer/test/explorer/smart_contract/compiler_version_test.exs create mode 100644 apps/explorer/test/explorer/smart_contract/helper_test.exs create mode 100644 apps/explorer/test/explorer/smart_contract/reader_test.exs create mode 100644 apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs create mode 100644 apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs create mode 100644 apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs create mode 100644 apps/explorer/test/explorer/smart_contract/writer_test.exs create mode 100644 apps/explorer/test/explorer/token/balance_reader_test.exs create mode 100644 apps/explorer/test/explorer/token/instance_metadata_retriever_test.exs create mode 100644 apps/explorer/test/explorer/token/metadata_retriever_test.exs create mode 100644 apps/explorer/test/explorer/validator/metadata_importer_test.exs create mode 100644 apps/explorer/test/explorer/validator/metadata_retriever_test.exs create mode 100644 apps/explorer/test/string/chars/explorer/chain/address_test.exs create mode 100644 apps/explorer/test/string/chars/explorer/chain/data_test.exs create mode 100644 apps/explorer/test/support/chain/import/runner_case.ex create mode 100644 apps/explorer/test/support/data_case.ex create mode 100644 apps/explorer/test/support/factory.ex create mode 100644 apps/explorer/test/support/fakes/no_op_source.ex create mode 100644 apps/explorer/test/support/fakes/one_coin_source.ex create mode 100644 apps/explorer/test/support/fixture/chain_spec/classic.json create mode 100644 apps/explorer/test/support/fixture/chain_spec/foundation.json create mode 100644 apps/explorer/test/support/fixture/chain_spec/qdai_genesis.json create mode 100644 apps/explorer/test/support/fixture/exchange_rates/coin_gecko.json create mode 100644 apps/explorer/test/support/fixture/smart_contract/ERC677.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/compiler_tests.json create mode 100644 apps/explorer/test/support/fixture/smart_contract/contract_from_factory.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/contract_with_lib.json create mode 100644 apps/explorer/test/support/fixture/smart_contract/contract_with_lib.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/home_bridge.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/issue_3082.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/issue_4758.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/issue_5114.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/issue_5127.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/issue_5431.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/issue_with_constructor_args.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/large_smart_contract.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/solc_bin.json create mode 100644 apps/explorer/test/support/fixture/smart_contract/solidity_0.5.9_smart_contract.sol create mode 100644 apps/explorer/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.json create mode 100644 apps/explorer/test/support/fixture/smart_contract/solidity_5.11_new_whisper_metadata.sol create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/.gitkeep create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/block_importer_download_block_1_downloads_the_block.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/block_importer_import_1_duplicate_block.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/block_importer_import_1_pending.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/block_importer_import_1_saves_the_block.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/ethereumex_extensions_trace_transaction_1.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/import_block_perform_1_duplicate.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/import_block_perform_1_earliest.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/import_block_perform_1_integer.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/import_block_perform_1_latest.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/import_block_perform_1_string.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/import_block_perform_later_1_latest.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/import_internal_transaction_perform_1.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/import_receipt_perform_1.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/import_skipped_blocks_perform_1.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/import_transaction_perform_1.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/internal_transaction_importer_import_1.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/internal_transaction_importer_import_1_from_core-trace.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/internal_transaction_importer_import_1_with_contract_creation.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_binds_internal_transactions.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_creates_a_from_address.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_creates_a_to_address.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_creates_a_to_address_from_creates.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_download_transaction.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_download_transaction_with_a_bad_hash.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_import_1_failed.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_import_1_out_of_gas.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_import_1_pending.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_import_1_receipt.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_import_saves_the_transaction.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_saves_the_association.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_txn_without_block.json create mode 100644 apps/explorer/test/support/fixture/vcr_cassettes/transaction_importer_updates_the_association.json create mode 100644 apps/explorer/test/test_helper.exs create mode 100644 apps/indexer/.gitignore create mode 100644 apps/indexer/README.md create mode 100644 apps/indexer/config/config.exs create mode 100644 apps/indexer/config/dev.exs create mode 100644 apps/indexer/config/dev/arbitrum.exs create mode 100644 apps/indexer/config/dev/besu.exs create mode 100644 apps/indexer/config/dev/erigon.exs create mode 100644 apps/indexer/config/dev/ganache.exs create mode 100644 apps/indexer/config/dev/geth.exs create mode 100644 apps/indexer/config/dev/nethermind.exs create mode 100644 apps/indexer/config/dev/rsk.exs create mode 100644 apps/indexer/config/prod.exs create mode 100644 apps/indexer/config/prod/arbitrum.exs create mode 100644 apps/indexer/config/prod/besu.exs create mode 100644 apps/indexer/config/prod/erigon.exs create mode 100644 apps/indexer/config/prod/ganache.exs create mode 100644 apps/indexer/config/prod/geth.exs create mode 100644 apps/indexer/config/prod/nethermind.exs create mode 100644 apps/indexer/config/prod/rsk.exs create mode 100644 apps/indexer/config/runtime/test.exs create mode 100644 apps/indexer/config/test.exs create mode 100644 apps/indexer/config/test/arbitrum.exs create mode 100644 apps/indexer/config/test/besu.exs create mode 100644 apps/indexer/config/test/erigon.exs create mode 100644 apps/indexer/config/test/ganache.exs create mode 100644 apps/indexer/config/test/geth.exs create mode 100644 apps/indexer/config/test/nethermind.exs create mode 100644 apps/indexer/config/test/rsk.exs create mode 100644 apps/indexer/lib/indexer.ex create mode 100644 apps/indexer/lib/indexer/application.ex create mode 100644 apps/indexer/lib/indexer/block/catchup/bound_interval_supervisor.ex create mode 100644 apps/indexer/lib/indexer/block/catchup/fetcher.ex create mode 100644 apps/indexer/lib/indexer/block/catchup/sequence.ex create mode 100644 apps/indexer/lib/indexer/block/catchup/supervisor.ex create mode 100644 apps/indexer/lib/indexer/block/fetcher.ex create mode 100644 apps/indexer/lib/indexer/block/fetcher/receipts.ex create mode 100644 apps/indexer/lib/indexer/block/realtime/fetcher.ex create mode 100644 apps/indexer/lib/indexer/block/realtime/supervisor.ex create mode 100644 apps/indexer/lib/indexer/bound_interval.ex create mode 100644 apps/indexer/lib/indexer/bound_queue.ex create mode 100644 apps/indexer/lib/indexer/buffered_task.ex create mode 100644 apps/indexer/lib/indexer/fetcher.ex create mode 100644 apps/indexer/lib/indexer/fetcher/block_reward.ex create mode 100644 apps/indexer/lib/indexer/fetcher/coin_balance.ex create mode 100644 apps/indexer/lib/indexer/fetcher/coin_balance_on_demand.ex create mode 100644 apps/indexer/lib/indexer/fetcher/contract_code.ex create mode 100644 apps/indexer/lib/indexer/fetcher/empty_blocks_sanitizer.ex create mode 100644 apps/indexer/lib/indexer/fetcher/internal_transaction.ex create mode 100644 apps/indexer/lib/indexer/fetcher/pending_transaction.ex create mode 100644 apps/indexer/lib/indexer/fetcher/replaced_transaction.ex create mode 100644 apps/indexer/lib/indexer/fetcher/token.ex create mode 100644 apps/indexer/lib/indexer/fetcher/token_balance.ex create mode 100644 apps/indexer/lib/indexer/fetcher/token_balance_on_demand.ex create mode 100644 apps/indexer/lib/indexer/fetcher/token_instance.ex create mode 100644 apps/indexer/lib/indexer/fetcher/token_total_supply_on_demand.ex create mode 100644 apps/indexer/lib/indexer/fetcher/token_updater.ex create mode 100644 apps/indexer/lib/indexer/fetcher/uncle_block.ex create mode 100644 apps/indexer/lib/indexer/logger.ex create mode 100644 apps/indexer/lib/indexer/memory/monitor.ex create mode 100644 apps/indexer/lib/indexer/memory/shrinkable.ex create mode 100644 apps/indexer/lib/indexer/pending_ops_cleaner.ex create mode 100644 apps/indexer/lib/indexer/pending_transactions_sanitizer.ex create mode 100644 apps/indexer/lib/indexer/prometheus/instrumenter.ex create mode 100644 apps/indexer/lib/indexer/prometheus/pending_block_operations_collector.ex create mode 100644 apps/indexer/lib/indexer/supervisor.ex create mode 100644 apps/indexer/lib/indexer/temporary/blocks_transactions_mismatch.ex create mode 100644 apps/indexer/lib/indexer/temporary/uncataloged_token_transfers.ex create mode 100644 apps/indexer/lib/indexer/temporary/uncles_without_index.ex create mode 100644 apps/indexer/lib/indexer/token_balances.ex create mode 100644 apps/indexer/lib/indexer/tracer.ex create mode 100644 apps/indexer/lib/indexer/transform/address_coin_balances.ex create mode 100644 apps/indexer/lib/indexer/transform/address_coin_balances_daily.ex create mode 100644 apps/indexer/lib/indexer/transform/address_token_balances.ex create mode 100644 apps/indexer/lib/indexer/transform/addresses.ex create mode 100644 apps/indexer/lib/indexer/transform/blocks.ex create mode 100644 apps/indexer/lib/indexer/transform/blocks/base.ex create mode 100644 apps/indexer/lib/indexer/transform/blocks/clique.ex create mode 100644 apps/indexer/lib/indexer/transform/mint_transfers.ex create mode 100644 apps/indexer/lib/indexer/transform/token_transfers.ex create mode 100644 apps/indexer/mix.exs create mode 100644 apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs create mode 100644 apps/indexer/test/indexer/block/catchup/fetcher_test.exs create mode 100644 apps/indexer/test/indexer/block/catchup/sequence_test.exs create mode 100644 apps/indexer/test/indexer/block/fetcher/receipts_test.exs create mode 100644 apps/indexer/test/indexer/block/fetcher_test.exs create mode 100644 apps/indexer/test/indexer/block/realtime/fetcher_test.exs create mode 100644 apps/indexer/test/indexer/buffered_task_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/block_reward_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/coin_balance_on_demand_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/coin_balance_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/contract_code_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/internal_transaction_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/pending_transaction_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/replaced_transaction_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/token_balance_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/token_instance_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/token_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/token_updater_test.exs create mode 100644 apps/indexer/test/indexer/fetcher/uncle_block_test.exs create mode 100644 apps/indexer/test/indexer/pending_ops_cleaner_test.exs create mode 100644 apps/indexer/test/indexer/temporary/uncataloged_token_transfers_test.exs create mode 100644 apps/indexer/test/indexer/token_balances_test.exs create mode 100644 apps/indexer/test/indexer/transform/address_coin_balances_test.exs create mode 100644 apps/indexer/test/indexer/transform/address_token_balances_test.exs create mode 100644 apps/indexer/test/indexer/transform/addresses.exs create mode 100644 apps/indexer/test/indexer/transform/blocks/base_test.exs create mode 100644 apps/indexer/test/indexer/transform/blocks/clique_test.exs create mode 100644 apps/indexer/test/indexer/transform/blocks_test.exs create mode 100644 apps/indexer/test/indexer/transform/mint_transfers_test.exs create mode 100644 apps/indexer/test/indexer/transform/token_transfers_test.exs create mode 100644 apps/indexer/test/indexer_test.exs create mode 100644 apps/indexer/test/support/indexer/block/catchup_supervisor_case.ex create mode 100644 apps/indexer/test/support/indexer/fetcher/block_reward_supervisor_case.ex create mode 100644 apps/indexer/test/support/indexer/fetcher/coin_balance_supervisor_case.ex create mode 100644 apps/indexer/test/support/indexer/fetcher/contract_code_supervisor_case.ex create mode 100644 apps/indexer/test/support/indexer/fetcher/internal_transaction_supervisor_case.ex create mode 100644 apps/indexer/test/support/indexer/fetcher/pending_transaction_supervisor_case.ex create mode 100644 apps/indexer/test/support/indexer/fetcher/replaced_transaction_supervisor_case.ex create mode 100644 apps/indexer/test/support/indexer/fetcher/token_balance_supervisor_case.ex create mode 100644 apps/indexer/test/support/indexer/fetcher/token_supervisor_case.ex create mode 100644 apps/indexer/test/support/indexer/fetcher/token_updater_supervisor_case.ex create mode 100644 apps/indexer/test/support/indexer/fetcher/uncle_block_supervisor_case.ex create mode 100644 apps/indexer/test/test_helper.exs create mode 100644 appspec.yml create mode 100755 bin/deploy create mode 100755 bin/deployment/build create mode 100755 bin/deployment/health_check create mode 100755 bin/deployment/migrate create mode 100755 bin/deployment/start create mode 100755 bin/deployment/stop create mode 100755 bin/install_chrome_headless.sh create mode 100755 bin/test create mode 100644 blockscout.png create mode 100644 config/config.exs create mode 100644 config/dev.exs create mode 100644 config/prod.exs create mode 100644 config/runtime.exs create mode 100644 config/runtime/dev.exs create mode 100644 config/runtime/prod.exs create mode 100644 config/runtime/test.exs create mode 100644 config/test.exs create mode 100644 coveralls.json create mode 100644 docker-compose/README.md create mode 100644 docker-compose/docker-compose-no-build-erigon.yml create mode 100644 docker-compose/docker-compose-no-build-ganache.yml create mode 100644 docker-compose/docker-compose-no-build-geth.yml create mode 100644 docker-compose/docker-compose-no-build-hardhat-network.yml create mode 100644 docker-compose/docker-compose-no-build-nethermind.yml create mode 100644 docker-compose/docker-compose-no-build-no-db-container.yml create mode 100644 docker-compose/docker-compose-no-rust-verification.yml create mode 100644 docker-compose/docker-compose.yml create mode 100644 docker-compose/envs/common-blockscout.env create mode 100644 docker-compose/envs/common-smart-contract-verifier.env create mode 100644 docker/Dockerfile create mode 100644 docker/Makefile create mode 100644 docker/README.md create mode 100644 mix.exs create mode 100644 mix.lock create mode 100644 prometheus.yml create mode 100644 rel/commands/migrate.sh create mode 100644 rel/commands/seed.sh create mode 100644 rel/config.exs create mode 100644 rel/plugins/.gitignore create mode 100644 rel/vm.args diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..d688fc7 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,627 @@ +version: 2 +jobs: + build: + docker: + # Ensure .tool-versions matches + - image: circleci/elixir:1.10.3-node-browsers + environment: + MIX_ENV: test + # match POSTGRES_PASSWORD for postgres image below + PGPASSWORD: postgres + # match POSTGRES_USER for postgres image below + PGUSER: postgres + + working_directory: ~/app + + steps: + - run: sudo apt-get update; sudo apt-get -y install autoconf build-essential libgmp3-dev libtool + + - checkout + - run: + command: ./bin/install_chrome_headless.sh + no_output_timeout: 2400 + + - run: mix local.hex --force + - run: mix local.rebar --force + + - run: + name: "ELIXIR_VERSION.lock" + command: echo "${ELIXIR_VERSION}" > ELIXIR_VERSION.lock + - run: + name: "OTP_VERSION.lock" + command: echo "${OTP_VERSION}" > OTP_VERSION.lock + + - restore_cache: + keys: + - v8-mix-compile-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}-{{ checksum "mix.lock" }} + - v8-mix-compile-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}-{{ checksum "mix.exs" }} + - v8-mix-compile-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }} + + - run: mix deps.get + + - restore_cache: + keys: + - v8-npm-install-{{ .Branch }}-{{ checksum "apps/block_scout_web/assets/package-lock.json" }} + - v8-npm-install-{{ .Branch }} + - v8-npm-install + + - run: + command: npm install + working_directory: "apps/explorer" + + - save_cache: + key: v3-npm-install-{{ .Branch }}-{{ checksum "apps/explorer/package-lock.json" }} + paths: "apps/explorer/node_modules" + - save_cache: + key: v3-npm-install-{{ .Branch }} + paths: "apps/explorer/node_modules" + - save_cache: + key: v3-npm-install + paths: "apps/explorer/node_modules" + + - run: + command: npm install + working_directory: "apps/block_scout_web/assets" + + - save_cache: + key: v8-npm-install-{{ .Branch }}-{{ checksum "apps/block_scout_web/assets/package-lock.json" }} + paths: "apps/block_scout_web/assets/node_modules" + - save_cache: + key: v8-npm-install-{{ .Branch }} + paths: "apps/block_scout_web/assets/node_modules" + - save_cache: + key: v8-npm-install + paths: "apps/block_scout_web/assets/node_modules" + + - run: mix compile + + # Ensure NIF is compiled for libsecp256k1 + - run: + command: make + working_directory: "deps/libsecp256k1" + + # `deps` needs to be cached with `_build` because `_build` will symlink into `deps` + + - save_cache: + key: v8-mix-compile-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}-{{ checksum "mix.lock" }} + paths: + - deps + - _build + - save_cache: + key: v8-mix-compile-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}-{{ checksum "mix.exs" }} + paths: + - deps + - _build + - save_cache: + key: v8-mix-compile-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }} + paths: + - deps + - _build + + - run: + name: Build assets + command: node node_modules/webpack/bin/webpack.js --mode development + working_directory: "apps/block_scout_web/assets" + + - persist_to_workspace: + root: . + paths: + - .circleci + - .credo.exs + - .dialyzer-ignore + - .formatter.exs + - .git + - .gitignore + - ELIXIR_VERSION.lock + - Gemfile + - Gemfile.lock + - OTP_VERSION.lock + - _build + - apps + - bin + - config + - deps + - doc + - mix.exs + - mix.lock + - appspec.yml + - rel + check_formatted: + docker: + # Ensure .tool-versions matches + - image: circleci/elixir:1.10.3 + environment: + MIX_ENV: test + + working_directory: ~/app + + steps: + - attach_workspace: + at: . + + - run: mix format --check-formatted + credo: + docker: + # Ensure .tool-versions matches + - image: circleci/elixir:1.10.3 + environment: + MIX_ENV: test + + working_directory: ~/app + + steps: + - attach_workspace: + at: . + + - run: mix local.hex --force + + - run: mix credo + deploy_aws: + docker: + # Ensure .tool-versions matches + - image: circleci/python:2.7-stretch + + working_directory: ~/app + + steps: + - attach_workspace: + at: . + + - add_ssh_keys: + fingerprints: + - "c4:fd:a8:f8:48:a8:09:e5:3e:be:30:62:4d:6f:6f:36" + + - run: + name: Deploy to AWS + command: bin/deploy + dialyzer: + docker: + # Ensure .tool-versions matches + - image: circleci/elixir:1.10.3 + environment: + MIX_ENV: test + + working_directory: ~/app + + steps: + - attach_workspace: + at: . + + - run: mix local.hex --force + + - restore_cache: + keys: + - v8-mix-dialyzer-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}-{{ checksum "mix.lock" }} + - v8-mix-dialyzer-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}-{{ checksum "mix.exs" }} + - v8-mix-dialyzer-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }} + + - run: + name: Unpack PLT cache + command: | + mkdir -p _build/test + cp plts/dialyxir*.plt _build/test/ || true + mkdir -p ~/.mix + cp plts/dialyxir*.plt ~/.mix/ || true + + - run: mix dialyzer --plt + + - run: + name: Pack PLT cache + command: | + mkdir -p plts + cp _build/test/dialyxir*.plt plts/ + cp ~/.mix/dialyxir*.plt plts/ + + - save_cache: + key: v8-mix-dialyzer-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}-{{ checksum "mix.lock" }} + paths: + - plts + - save_cache: + key: v8-mix-dialyzer-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}-{{ checksum "mix.exs" }} + paths: + - plts + - save_cache: + key: v8-mix-dialyzer-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }} + paths: + - plts + + - run: mix dialyzer --halt-exit-status + eslint: + docker: + # Ensure .tool-versions matches + - image: circleci/node:12.18.2-browsers-legacy + + working_directory: ~/app + + steps: + - attach_workspace: + at: . + + - run: + name: ESLint + command: ./node_modules/.bin/eslint --format=junit --output-file="test/eslint/junit.xml" js/** + working_directory: apps/block_scout_web/assets + + - store_test_results: + path: apps/block_scout_web/assets/test + gettext: + docker: + # Ensure .tool-versions matches + - image: circleci/elixir:1.10.3 + environment: + MIX_ENV: test + + working_directory: ~/app + + steps: + - attach_workspace: + at: . + + - run: mix local.hex --force + + - run: + name: Check for missed translations + command: | + mix gettext.extract --merge | tee stdout.txt + ! grep "Wrote " stdout.txt + working_directory: "apps/block_scout_web" + + - store_artifacts: + path: apps/block_scout_web/priv/gettext + jest: + docker: + # Ensure .tool-versions matches + - image: circleci/node:12.18.2-browsers-legacy + + working_directory: ~/app + + steps: + - attach_workspace: + at: . + + - run: + name: Jest + command: ./node_modules/.bin/jest + working_directory: apps/block_scout_web/assets + release: + docker: + # Ensure .tool-versions matches + - image: circleci/elixir:1.10.3 + environment: + MIX_ENV: prod + + working_directory: ~/app + + steps: + - attach_workspace: + at: . + + - run: mix local.hex --force + - run: mix local.rebar --force + - run: MIX_ENV=prod mix release + - run: + name: Collecting artifacts + command: | + find -name 'blockscout.tar.gz' -exec sh -c 'mkdir -p ci_artifact && cp "$@" ci_artifact/ci_artifact_blockscout.tar.gz' _ {} + + when: always + + - store_artifacts: + name: Uploading CI artifacts + path: ci_artifact/ci_artifact_blockscout.tar.gz + destination: ci_artifact_blockscout.tar.gz + sobelow: + docker: + # Ensure .tool-versions matches + - image: circleci/elixir:1.10.3 + environment: + MIX_ENV: test + + working_directory: ~/app + + steps: + - attach_workspace: + at: . + + - run: mix local.hex --force + + - run: + name: Scan explorer for vulnerabilities + command: mix sobelow --config + working_directory: "apps/explorer" + + - run: + name: Scan block_scout_web for vulnerabilities + command: mix sobelow --config + working_directory: "apps/block_scout_web" + # test_geth_http_websocket: + # docker: + # # Ensure .tool-versions matches + # - image: circleci/elixir:1.10.3-node-browsers + # environment: + # MIX_ENV: test + # # match POSTGRES_PASSWORD for postgres image below + # PGPASSWORD: postgres + # # match POSTGRES_USER for postgres image below + # PGUSER: postgres + # ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Geth.HTTPWebSocket" + # ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Geth" + # - image: circleci/postgres:10.10-alpine + # environment: + # # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database + # POSTGRES_DB: explorer_test + # # match PGPASSWORD for elixir image above + # POSTGRES_PASSWORD: postgres + # # match PGUSER for elixir image above + # POSTGRES_USER: postgres + + # working_directory: ~/app + + # steps: + # - attach_workspace: + # at: . + + # - run: + # command: ./bin/install_chrome_headless.sh + # no_output_timeout: 2400 + + # - run: mix local.hex --force + # - run: mix local.rebar --force + + # - run: + # name: Wait for DB + # command: dockerize -wait tcp://localhost:5432 -timeout 1m + + # - run: + # name: mix test --exclude no_geth + # command: | + # # Don't submit coverage report for forks, but let the build succeed + # if [[ -z "$COVERALLS_REPO_TOKEN" ]]; then + # mix coveralls.html --exclude no_geth --parallel --umbrella + # else + # mix coveralls.circle --exclude no_geth --parallel --umbrella || + # # if mix failed, then coveralls_merge won't run, so signal done here and return original exit status + # (retval=$? && curl -k https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN -d "payload[build_num]=$CIRCLE_WORKFLOW_WORKSPACE_ID&payload[status]=done" && return $retval) + # fi + + # - store_artifacts: + # path: cover/excoveralls.html + # - store_test_results: + # path: _build/test/junit + # test_geth_mox: + # docker: + # # Ensure .tool-versions matches + # - image: circleci/elixir:1.10.3-node-browsers + # environment: + # MIX_ENV: test + # # match POSTGRES_PASSWORD for postgres image below + # PGPASSWORD: postgres + # # match POSTGRES_USER for postgres image below + # PGUSER: postgres + # ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Geth.Mox" + # ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Mox" + # - image: circleci/postgres:10.10-alpine + # environment: + # # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database + # POSTGRES_DB: explorer_test + # # match PGPASSWORD for elixir image above + # POSTGRES_PASSWORD: postgres + # # match PGUSER for elixir image above + # POSTGRES_USER: postgres + + # working_directory: ~/app + + # steps: + # - attach_workspace: + # at: . + + # - run: + # command: ./bin/install_chrome_headless.sh + # no_output_timeout: 2400 + + # - run: mix local.hex --force + # - run: mix local.rebar --force + + # - run: + # name: Wait for DB + # command: dockerize -wait tcp://localhost:5432 -timeout 1m + + # - run: + # name: mix test --exclude no_geth + # command: | + # # Don't submit coverage report for forks, but let the build succeed + # if [[ -z "$COVERALLS_REPO_TOKEN" ]]; then + # mix coveralls.html --exclude no_geth --parallel --umbrella + # else + # mix coveralls.circle --exclude no_geth --parallel --umbrella || + # # if mix failed, then coveralls_merge won't run, so signal done here and return original exit status + # (retval=$? && curl -k https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN -d "payload[build_num]=$CIRCLE_WORKFLOW_WORKSPACE_ID&payload[status]=done" && return $retval) + # fi + + # - store_artifacts: + # path: cover/excoveralls.html + # - store_test_results: + # path: _build/test/junit + # test_nethermind_http_websocket: + # docker: + # # Ensure .tool-versions matches + # - image: circleci/elixir:1.10.3-node-browsers + # environment: + # MIX_ENV: test + # # match POSTGRES_PASSWORD for postgres image below + # PGPASSWORD: postgres + # # match POSTGRES_USER for postgres image below + # PGUSER: postgres + # ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Nethermind.HTTPWebSocket" + # ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Nethermind" + # - image: circleci/postgres:10.10-alpine + # environment: + # # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database + # POSTGRES_DB: explorer_test + # # match PGPASSWORD for elixir image above + # POSTGRES_PASSWORD: postgres + # # match PGUSER for elixir image above + # POSTGRES_USER: postgres + + # working_directory: ~/app + + # steps: + # - attach_workspace: + # at: . + + # - run: + # command: ./bin/install_chrome_headless.sh + # no_output_timeout: 2400 + + # - run: mix local.hex --force + # - run: mix local.rebar --force + + # - run: + # name: Wait for DB + # command: dockerize -wait tcp://localhost:5432 -timeout 1m + + # - run: + # name: mix test --exclude no_nethermind + # command: | + # # Don't submit coverage report for forks, but let the build succeed + # if [[ -z "$COVERALLS_REPO_TOKEN" ]]; then + # mix coveralls.html --exclude no_nethermind --parallel --umbrella + # else + # mix coveralls.circle --exclude no_nethermind --parallel --umbrella || + # # if mix failed, then coveralls_merge won't run, so signal done here and return original exit status + # (retval=$? && curl -k https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN -d "payload[build_num]=$CIRCLE_WORKFLOW_WORKSPACE_ID&payload[status]=done" && return $retval) + # fi + + # - store_artifacts: + # path: cover/excoveralls.html + # - store_test_results: + # path: _build/test/junit + test_nethermind_mox: + docker: + # Ensure .tool-versions matches + - image: circleci/elixir:1.10.3-node-browsers + environment: + MIX_ENV: test + # match POSTGRES_PASSWORD for postgres image below + PGPASSWORD: postgres + # match POSTGRES_USER for postgres image below + PGUSER: postgres + ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Nethermind.Mox" + ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Mox" + - image: circleci/postgres:10.10-alpine + environment: + # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database + POSTGRES_DB: explorer_test + # match PGPASSWORD for elixir image above + POSTGRES_PASSWORD: postgres + # match PGUSER for elixir image above + POSTGRES_USER: postgres + + working_directory: ~/app + + steps: + - attach_workspace: + at: . + + - run: + command: ./bin/install_chrome_headless.sh + no_output_timeout: 2400 + + - run: mix local.hex --force + - run: mix local.rebar --force + + - run: + name: Wait for DB + command: dockerize -wait tcp://localhost:5432 -timeout 1m + + - run: + name: mix test --exclude no_nethermind + command: | + # Don't submit coverage report for forks, but let the build succeed + if [[ -z "$COVERALLS_REPO_TOKEN" ]]; then + mix coveralls.html --exclude no_nethermind --parallel --umbrella + else + mix coveralls.circle --exclude no_nethermind --parallel --umbrella || + # if mix failed, then coveralls_merge won't run, so signal done here and return original exit status + (retval=$? && curl -k https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN -d "payload[build_num]=$CIRCLE_WORKFLOW_WORKSPACE_ID&payload[status]=done" && return $retval) + fi + + - store_artifacts: + path: cover/excoveralls.html + - store_test_results: + path: _build/test/junit + coveralls_merge: + docker: + # Ensure .tool-versions matches + - image: circleci/elixir:1.10.3 + environment: + MIX_ENV: test + + steps: + - run: + name: Tell coveralls.io build is done + command: curl -k https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN -d "payload[build_num]=$CIRCLE_WORKFLOW_WORKSPACE_ID&payload[status]=done" +workflows: + version: 2 + primary: + jobs: + - build + - check_formatted: + requires: + - build + # This unfortunately will only fire if all the tests pass because of how `requires` works + - coveralls_merge: + requires: + # - test_nethermind_http_websocket + - test_nethermind_mox + # - test_geth_http_websocket + # - test_geth_mox + - credo: + requires: + - build + - deploy_aws: + filters: + branches: + only: + - production + - staging + - /deploy-[A-Za-z0-9]+$/ + requires: + - check_formatted + - credo + - eslint + - jest + - sobelow + # - test_nethermind_http_websocket + - test_nethermind_mox + # - test_geth_http_websocket + # - test_geth_mox + - dialyzer: + requires: + - build + - eslint: + requires: + - build + - gettext: + requires: + - build + - jest: + requires: + - build + - release: + requires: + - build + - sobelow: + requires: + - build + # - test_nethermind_http_websocket: + # requires: + # - build + - test_nethermind_mox: + requires: + - build + # - test_geth_http_websocket: + # requires: + # - build + # - test_geth_mox: + # requires: + # - build diff --git a/.credo.exs b/.credo.exs new file mode 100644 index 0000000..ba4d7fe --- /dev/null +++ b/.credo.exs @@ -0,0 +1,150 @@ +# This file contains the configuration for Credo and you are probably reading +# this after creating it with `mix credo.gen.config`. +# +# If you find anything wrong or unclear in this file, please report an +# issue on GitHub: https://github.com/rrrene/credo/issues +# +%{ + # + # You can have as many configs as you like in the `configs:` field. + configs: [ + %{ + # + # Run any exec using `mix credo -C `. If no exec name is given + # "default" is used. + # + name: "default", + # + # These are the files included in the analysis: + files: %{ + # + # You can give explicit globs or simply directories. + # In the latter case `**/*.{ex,exs}` will be used. + # + included: ["lib/", "src/", "web/", "apps/*/lib/**/*.{ex,exs}"], + excluded: [ + ~r"/_build/", + ~r"/deps/", + ~r"/node_modules/", + ~r"/apps/block_scout_web/lib/block_scout_web.ex" + ] + }, + # + # If you create your own checks, you must specify the source files for + # them here, so they can be loaded by Credo before running the analysis. + # + requires: [], + # + # If you want to enforce a style guide and need a more traditional linting + # experience, you can change `strict` to `true` below: + # + strict: true, + # + # If you want to use uncolored output by default, you can change `color` + # to `false` below: + # + color: true, + # + # You can customize the parameters of any check by adding a second element + # to the tuple. + # + # To disable a check put `false` as second element: + # + # {Credo.Check.Design.DuplicatedCode, false} + # + checks: [ + # outdated by formatter in Elixir 1.6. See https://github.com/rrrene/credo/issues/505 + {Credo.Check.Consistency.LineEndings, false}, + {Credo.Check.Consistency.SpaceAroundOperators, false}, + {Credo.Check.Consistency.SpaceInParentheses, false}, + {Credo.Check.Consistency.TabsOrSpaces, false}, + {Credo.Check.Readability.LargeNumbers, false}, + {Credo.Check.Readability.MaxLineLength, false}, + {Credo.Check.Readability.ParenthesesInCondition, false}, + {Credo.Check.Readability.RedundantBlankLines, false}, + {Credo.Check.Readability.Semicolons, false}, + {Credo.Check.Readability.SpaceAfterCommas, false}, + {Credo.Check.Readability.TrailingBlankLine, false}, + {Credo.Check.Readability.TrailingWhiteSpace, false}, + + # outdated by lazy Logger in Elixir 1.7. See https://elixir-lang.org/blog/2018/07/25/elixir-v1-7-0-released/ + {Credo.Check.Warning.LazyLogging, false}, + + # not handled by formatter + {Credo.Check.Consistency.ExceptionNames}, + {Credo.Check.Consistency.ParameterPatternMatching}, + + # You can customize the priority of any check + # Priority values are: `low, normal, high, higher` + # + {Credo.Check.Design.AliasUsage, + excluded_namespaces: ~w(Block Blocks Import Runner Socket SpandexDatadog Task), + excluded_lastnames: + ~w(Address DateTime Exporter Fetcher Full Instrumenter Logger Monitor Name Number Repo Spec Time Unit), + priority: :low}, + + # For some checks, you can also set other parameters + # + # If you don't want the `setup` and `test` macro calls in ExUnit tests + # or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just + # set the `excluded_macros` parameter to `[:schema, :setup, :test]`. + # + {Credo.Check.Design.DuplicatedCode, excluded_macros: [], mass_threshold: 80}, + + # You can also customize the exit_status of each check. + # If you don't want TODO comments to cause `mix credo` to fail, just + # set this value to 0 (zero). + # + {Credo.Check.Design.TagTODO, exit_status: 0}, + {Credo.Check.Design.TagFIXME}, + {Credo.Check.Readability.FunctionNames}, + {Credo.Check.Readability.ModuleAttributeNames}, + {Credo.Check.Readability.ModuleDoc}, + {Credo.Check.Readability.ModuleNames}, + {Credo.Check.Readability.ParenthesesOnZeroArityDefs}, + {Credo.Check.Readability.PredicateFunctionNames}, + {Credo.Check.Readability.PreferImplicitTry}, + {Credo.Check.Readability.StringSigils}, + {Credo.Check.Readability.VariableNames}, + {Credo.Check.Refactor.DoubleBooleanNegation}, + {Credo.Check.Refactor.CondStatements}, + {Credo.Check.Refactor.CyclomaticComplexity}, + {Credo.Check.Refactor.FunctionArity}, + {Credo.Check.Refactor.LongQuoteBlocks}, + {Credo.Check.Refactor.MatchInCondition}, + {Credo.Check.Refactor.NegatedConditionsInUnless}, + {Credo.Check.Refactor.NegatedConditionsWithElse}, + {Credo.Check.Refactor.Nesting}, + {Credo.Check.Refactor.PipeChainStart}, + {Credo.Check.Refactor.UnlessWithElse}, + {Credo.Check.Warning.BoolOperationOnSameValues}, + {Credo.Check.Warning.ExpensiveEmptyEnumCheck}, + {Credo.Check.Warning.IExPry}, + {Credo.Check.Warning.IoInspect}, + {Credo.Check.Warning.OperationOnSameValues}, + {Credo.Check.Warning.OperationWithConstantResult}, + {Credo.Check.Warning.UnusedEnumOperation}, + {Credo.Check.Warning.UnusedFileOperation}, + {Credo.Check.Warning.UnusedKeywordOperation}, + {Credo.Check.Warning.UnusedListOperation}, + {Credo.Check.Warning.UnusedPathOperation}, + {Credo.Check.Warning.UnusedRegexOperation}, + {Credo.Check.Warning.UnusedStringOperation}, + {Credo.Check.Warning.UnusedTupleOperation}, + {Credo.Check.Warning.RaiseInsideRescue, false}, + + # Controversial and experimental checks (opt-in, just remove `, false`) + # + # TODO reenable before merging optimized-indexer branch + {Credo.Check.Refactor.ABCSize, false}, + {Credo.Check.Refactor.AppendSingleItem}, + {Credo.Check.Refactor.VariableRebinding}, + {Credo.Check.Warning.MapGetUnsafePass}, + {Credo.Check.Consistency.MultiAliasImportRequireUse} + + # Custom checks can be created using `mix credo.gen.check`. + # + ] + } + ] +} diff --git a/.dialyzer-ignore b/.dialyzer-ignore new file mode 100644 index 0000000..654d998 --- /dev/null +++ b/.dialyzer-ignore @@ -0,0 +1,35 @@ +:0: Unknown function 'Elixir.ExUnit.Callbacks':'__merge__'/3 +:0: Unknown function 'Elixir.ExUnit.CaseTemplate':'__proxy__'/2 +:0: Unknown type 'Elixir.Map':t/0 +:0: Unknown type 'Elixir.Hash':t/0 +:0: Unknown type 'Elixir.Address':t/0 +apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex:400: Function timestamp_to_datetime/1 has no local return +lib/ethereum_jsonrpc/rolling_window.ex:173 +lib/explorer/repo/prometheus_logger.ex:8 +lib/explorer/smart_contract/solidity/publisher_worker.ex:1 +lib/explorer/smart_contract/vyper/publisher_worker.ex:1 +lib/explorer/smart_contract/solidity/publisher_worker.ex:6 +lib/explorer/smart_contract/vyper/publisher_worker.ex:6 +apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: Function microseconds_time/1 has no local return +apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: The call 'Elixir.System':convert_time_unit(__@1::any(),'native','microseconds') breaks the contract (integer(),time_unit() | 'native',time_unit() | 'native') -> integer() +lib/block_scout_web/router.ex:1 +lib/block_scout_web/schema/types.ex:31 +lib/phoenix/router.ex:324 +lib/phoenix/router.ex:402 +lib/block_scout_web/views/layout_view.ex:145: The call 'Elixir.Poison.Parser':'parse!' +lib/block_scout_web/views/layout_view.ex:237: The call 'Elixir.Poison.Parser':'parse!' +lib/explorer/smart_contract/reader.ex:435 +lib/indexer/fetcher/token_total_supply_on_demand.ex:16 +lib/explorer/exchange_rates/source.ex:116 +lib/explorer/exchange_rates/source.ex:119 +lib/explorer/smart_contract/solidity/verifier.ex:310 +lib/block_scout_web/templates/address_contract/index.html.eex:158 +lib/block_scout_web/templates/address_contract/index.html.eex:195 +lib/explorer/third_party_integrations/sourcify.ex:120 +lib/explorer/third_party_integrations/sourcify.ex:123 +lib/block_scout_web/views/transaction_view.ex:137 +lib/block_scout_web/views/transaction_view.ex:152 +lib/block_scout_web/views/transaction_view.ex:197 +lib/indexer/buffered_task.ex:402 +lib/indexer/buffered_task.ex:451 +lib/indexer/memory/monitor.ex:160 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b52edd4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +_build +deps +apps/block_scout_web/assets/node_modules +apps/explorer/node_modules +test +.git +.circleci +logs +apps/*/test diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..69ae0d2 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,11 @@ +[ + inputs: [ + ".credo.exs", + ".formatter.exs", + "apps/*/mix.exs", + "apps/*/{benchmarks,config,lib,priv,test}/**/*.{ex,exs}", + "mix.exs", + "{config}/**/*.{ex,exs}" + ], + line_length: 120 +] diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b6a9ec4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: + - package-ecosystem: "mix" + directory: "/" + open-pull-requests-limit: 20 + schedule: + interval: "daily" + + - package-ecosystem: "npm" + directory: "/apps/block_scout_web/assets" + open-pull-requests-limit: 10 + schedule: + interval: "daily" + + - package-ecosystem: "npm" + directory: "/apps/explorer" + open-pull-requests-limit: 10 + schedule: + interval: "daily" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..9b8a5cf --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,72 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '45 11 * * 5' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml new file mode 100644 index 0000000..738095b --- /dev/null +++ b/.github/workflows/config.yml @@ -0,0 +1,584 @@ +name: Blockscout + +on: + push: + branches: + - master + pull_request: + branches: + - master + +env: + MIX_ENV: test + OTP_VERSION: '24.3.4.1' + ELIXIR_VERSION: '1.13.4' + ACCOUNT_AUTH0_DOMAIN: 'blockscoutcom.us.auth0.com' + ACCOUNT_AUTH0_LOGOUT_URL: 'https://blockscoutcom.us.auth0.com/v2/logout' + ACCOUNT_AUTH0_LOGOUT_RETURN_URL: 'https://blockscout.com/auth/logout' + +jobs: + build-and-cache: + name: Build and Cache deps + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + + - name: "ELIXIR_VERSION.lock" + run: echo "${ELIXIR_VERSION}" > ELIXIR_VERSION.lock + + - name: "OTP_VERSION.lock" + run: echo "${OTP_VERSION}" > OTP_VERSION.lock + + - name: Restore Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps- + + - name: Conditionally build Mix deps cache + if: steps.deps-cache.outputs.cache-hit != 'true' + run: | + mix local.hex --force + mix local.rebar --force + mix deps.get + mix deps.compile + cd deps/libsecp256k1 + make + + - name: Restore Explorer NPM Cache + uses: actions/cache@v2 + id: explorer-npm-cache + with: + path: apps/explorer/node_modules + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm-${{ hashFiles('apps/explorer/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm- + + - name: Conditionally build Explorer NPM Cache + if: steps.explorer-npm-cache.outputs.cache-hit != 'true' + run: npm install + working-directory: apps/explorer + + - name: Restore Blockscout Web NPM Cache + uses: actions/cache@v2 + id: blockscoutweb-npm-cache + with: + path: apps/block_scout_web/assets/node_modules + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm-${{ hashFiles('apps/block_scout_web/assets/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm- + + - name: Conditionally build Blockscout Web NPM Cache + if: steps.blockscoutweb-npm-cache.outputs.cache-hit != 'true' + run: npm install + working-directory: apps/block_scout_web/assets + + credo: + name: Credo + runs-on: ubuntu-latest + needs: build-and-cache + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + + - name: Restore Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" + + - run: mix credo + + check_formatted: + name: Code formatting checks + runs-on: ubuntu-latest + needs: build-and-cache + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + + - name: Restore Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" + + - run: mix format --check-formatted + dialyzer: + name: Dialyzer static analysis + runs-on: ubuntu-latest + needs: build-and-cache + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + + - name: Restore Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" + + - name: Restore Dialyzer Cache + uses: actions/cache@v2 + id: dialyzer-cache + with: + path: priv/plts + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-mixlockhash_16-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-" + + - name: Conditionally build Dialyzer Cache + if: steps.dialyzer-cache.output.cache-hit != 'true' + run: | + mkdir -p priv/plts + mix dialyzer --plt + + - name: Run Dialyzer + run: mix dialyzer --halt-exit-status + + gettext: + name: Missing translation keys check + runs-on: ubuntu-latest + needs: build-and-cache + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + + - name: Restore Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" + + - run: | + mix gettext.extract --merge | tee stdout.txt + ! grep "Wrote " stdout.txt + working-directory: "apps/block_scout_web" + sobelow: + name: Sobelow security analysis + runs-on: ubuntu-latest + needs: build-and-cache + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + + - name: Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" + + - name: Scan explorer for vulnerabilities + run: mix sobelow --config + working-directory: "apps/explorer" + - name: Scan block_scout_web for vulnerabilities + run: mix sobelow --config + working-directory: "apps/block_scout_web" + eslint: + name: ESLint + runs-on: ubuntu-latest + needs: build-and-cache + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + + - name: Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" + + - name: Restore Explorer NPM Cache + uses: actions/cache@v2 + id: explorer-npm-cache + with: + path: apps/explorer/node_modules + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm-${{ hashFiles('apps/explorer/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm- + + - name: Restore Blockscout Web NPM Cache + uses: actions/cache@v2 + id: blockscoutweb-npm-cache + with: + path: apps/block_scout_web/assets/node_modules + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm-${{ hashFiles('apps/block_scout_web/assets/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm- + + - name: Build assets + run: node node_modules/webpack/bin/webpack.js --mode development + working-directory: "apps/block_scout_web/assets" + + - run: ./node_modules/.bin/eslint --format=junit --output-file="test/eslint/junit.xml" js/** + working-directory: apps/block_scout_web/assets + jest: + name: JS Tests + runs-on: ubuntu-latest + needs: build-and-cache + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + + - name: Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" + + - name: Restore Blockscout Web NPM Cache + uses: actions/cache@v2 + id: blockscoutweb-npm-cache + with: + path: apps/block_scout_web/assets/node_modules + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm-${{ hashFiles('apps/block_scout_web/assets/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm- + + - name: Build assets + run: node node_modules/webpack/bin/webpack.js --mode development + working-directory: "apps/block_scout_web/assets" + + - run: ./node_modules/.bin/jest + working-directory: apps/block_scout_web/assets + + test_nethermind_mox_ethereum_jsonrpc: + name: EthereumJSONRPC Tests + runs-on: ubuntu-latest + needs: build-and-cache + services: + postgres: + image: postgres + env: + # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database + POSTGRES_DB: explorer_test + # match PGPASSWORD for elixir image above + POSTGRES_PASSWORD: postgres + # match PGUSER for elixir image above + POSTGRES_USER: postgres + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + # Maps tcp port 5432 on service container to the host + - 5432:5432 + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + - run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + - run: echo 'export PATH=~/.cargo/bin/:$PATH' >> $GITHUB_ENV + + - name: Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" + + - run: ./bin/install_chrome_headless.sh + - name: mix test --exclude no_nethermind + run: | + cd apps/ethereum_jsonrpc + mix compile + mix test --no-start --exclude no_nethermind + env: + # match POSTGRES_PASSWORD for postgres image below + PGPASSWORD: postgres + # match POSTGRES_USER for postgres image below + PGUSER: postgres + ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Nethermind.Mox" + ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Mox" + test_nethermind_mox_explorer: + name: Explorer Tests + runs-on: ubuntu-latest + needs: build-and-cache + services: + postgres: + image: postgres + env: + # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database + POSTGRES_DB: explorer_test + # match PGPASSWORD for elixir image above + POSTGRES_PASSWORD: postgres + # match PGUSER for elixir image above + POSTGRES_USER: postgres + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + # Maps tcp port 5432 on service container to the host + - 5432:5432 + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + - run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + - run: echo 'export PATH=~/.cargo/bin/:$PATH' >> $GITHUB_ENV + + - name: Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" + + - name: Restore Explorer NPM Cache + uses: actions/cache@v2 + id: explorer-npm-cache + with: + path: apps/explorer/node_modules + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm-${{ hashFiles('apps/explorer/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm + + - run: ./bin/install_chrome_headless.sh + - name: mix test --exclude no_nethermind + run: | + mix ecto.create --quiet + mix ecto.migrate + cd apps/explorer + mix compile + mix test --no-start --exclude no_nethermind + env: + # match POSTGRES_PASSWORD for postgres image below + PGPASSWORD: postgres + # match POSTGRES_USER for postgres image below + PGUSER: postgres + ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Nethermind.Mox" + ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Mox" + test_nethermind_mox_indexer: + name: Indexer Tests + runs-on: ubuntu-latest + needs: build-and-cache + services: + postgres: + image: postgres + env: + # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database + POSTGRES_DB: explorer_test + # match PGPASSWORD for elixir image above + POSTGRES_PASSWORD: postgres + # match PGUSER for elixir image above + POSTGRES_USER: postgres + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + # Maps tcp port 5432 on service container to the host + - 5432:5432 + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + - run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + - run: echo 'export PATH=~/.cargo/bin/:$PATH' >> $GITHUB_ENV + + - name: Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" + + + - run: ./bin/install_chrome_headless.sh + + - name: mix test --exclude no_nethermind + run: | + mix ecto.create --quiet + mix ecto.migrate + cd apps/indexer + mix compile + mix test --no-start --exclude no_nethermind + env: + # match POSTGRES_PASSWORD for postgres image below + PGPASSWORD: postgres + # match POSTGRES_USER for postgres image below + PGUSER: postgres + ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Nethermind.Mox" + ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Mox" + + test_nethermind_mox_block_scout_web: + name: Blockscout Web Tests + runs-on: ubuntu-latest + needs: build-and-cache + services: + redis_db: + image: 'redis:alpine' + ports: + - 6379:6379 + + postgres: + image: postgres + env: + # Match apps/explorer/config/test.exs config :explorer, Explorer.Repo, database + POSTGRES_DB: explorer_test + # match PGPASSWORD for elixir image above + POSTGRES_PASSWORD: postgres + # match PGUSER for elixir image above + POSTGRES_USER: postgres + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + # Maps tcp port 5432 on service container to the host + - 5432:5432 + steps: + - uses: actions/checkout@v2 + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{ env.OTP_VERSION }} + elixir-version: ${{ env.ELIXIR_VERSION }} + - run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + - run: echo 'export PATH=~/.cargo/bin/:$PATH' >> $GITHUB_ENV + + - name: Mix Deps Cache + uses: actions/cache@v2 + id: deps-cache + with: + path: | + deps + _build + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_12-${{ hashFiles('mix.lock') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" + + + - name: Restore Explorer NPM Cache + uses: actions/cache@v2 + id: explorer-npm-cache + with: + path: apps/explorer/node_modules + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm-${{ hashFiles('apps/explorer/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-explorer-npm- + + - name: Restore Blockscout Web NPM Cache + uses: actions/cache@v2 + id: blockscoutweb-npm-cache + with: + path: apps/block_scout_web/assets/node_modules + key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm-${{ hashFiles('apps/block_scout_web/assets/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-blockscoutweb-npm- + + - name: Build assets + run: node node_modules/webpack/bin/webpack.js --mode development + working-directory: "apps/block_scout_web/assets" + + - run: ./bin/install_chrome_headless.sh + + - name: mix test --exclude no_nethermind + run: | + mix ecto.create --quiet + mix ecto.migrate + cd apps/block_scout_web + mix compile + mix test --no-start --exclude no_nethermind + env: + # match POSTGRES_PASSWORD for postgres image below + PGPASSWORD: postgres + # match POSTGRES_USER for postgres image below + PGUSER: postgres + ETHEREUM_JSONRPC_CASE: "EthereumJSONRPC.Case.Nethermind.Mox" + ETHEREUM_JSONRPC_WEB_SOCKET_CASE: "EthereumJSONRPC.WebSocket.Case.Mox" + CHAIN_ID: "77" + ADMIN_PANEL_ENABLED: "true" + ACCOUNT_ENABLED: "true" + ACCOUNT_REDIS_URL: "redis://localhost:6379" diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml new file mode 100644 index 0000000..987f911 --- /dev/null +++ b/.github/workflows/e2e-tests.yml @@ -0,0 +1,86 @@ +name: Run E2E tests k8s + +on: + # push: + pull_request: + workflow_dispatch: + +env: + K8S_LOCAL_PORT: ${{ secrets.K8S_LOCAL_PORT }} + K8S_HOST: ${{ secrets.K8S_HOST }} + BASTION_HOST: ${{ secrets.BASTION_HOST }} + K8S_PORT: ${{ secrets.K8S_PORT }} + USERNAME: ${{ secrets.USERNAME }} + BASTION_SSH_KEY: ${{secrets.BASTION_SSH_KEY}} + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + outputs: + release-version: ${{ steps.output-step.outputs.release-version }} + short-sha: ${{ steps.output-step.outputs.short-sha }} + steps: + - name: Check out the repo + uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 + with: + images: blockscout/blockscout + + - name: Add SHORT_SHA env property with commit short sha + run: echo "SHORT_SHA=`echo ${GITHUB_SHA} | cut -c1-8`" >> $GITHUB_ENV + + - name: Add outputs + run: | + echo "::set-output name=short-sha::${{ env.SHORT_SHA }}" + id: output-step + + - name: Build and push Docker image + uses: docker/build-push-action@v3 + with: + context: . + file: ./docker/Dockerfile + push: true + cache-from: type=registry,ref=blockscout/blockscout:pr-buildcache + cache-to: type=registry,ref=blockscout/blockscout:pr-buildcache,mode=max + tags: blockscout/blockscout:pr-${{ env.SHORT_SHA }} + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_READ_API=false + API_PATH= + NETWORK_PATH= + DISABLE_WEBAPP=false + DISABLE_WRITE_API=false + CACHE_ENABLE_TOTAL_GAS_USAGE_COUNTER= + WOBSERVER_ENABLED=false + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + DISABLE_BRIDGE_MARKET_CAP_UPDATER=false + CACHE_BRIDGE_MARKET_CAP_UPDATE_INTERVAL= + SOCKET_ROOT= + + deploy_and_tests: + needs: push_to_registry + uses: blockscout/blockscout-ci-cd/.github/workflows/e2e_new.yaml@master + with: + blockscoutImage: blockscout/blockscout:pr-${{ needs.push_to_registry.outputs.short-sha }} + blockscoutIngressHost: e2e-blockscout-$GITHUB_SHA_SHORT + frontendIngressHost: e2e-blockscout-$GITHUB_SHA_SHORT + gethIngressHost: e2e-geth-$GITHUB_SHA_SHORT + scVerifierIngressHost: e2e-sc-verifier-$GITHUB_SHA_SHORT + secrets: inherit diff --git a/.github/workflows/publish-docker-image-every-push.yml b/.github/workflows/publish-docker-image-every-push.yml new file mode 100644 index 0000000..ec503dd --- /dev/null +++ b/.github/workflows/publish-docker-image-every-push.yml @@ -0,0 +1,82 @@ +name: Publish Docker image on every push to master branch + +on: + push: + branches: + - master +env: + OTP_VERSION: '24.3.4.1' + ELIXIR_VERSION: '1.13.4' + NEXT_RELEASE_VERSION: 5.0.0 + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + outputs: + release-version: ${{ steps.output-step.outputs.release-version }} + short-sha: ${{ steps.output-step.outputs.short-sha }} + steps: + - name: Check out the repo + uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 + with: + images: blockscout/blockscout + + - name: Add SHORT_SHA env property with commit short sha + run: echo "SHORT_SHA=`echo ${GITHUB_SHA} | cut -c1-8`" >> $GITHUB_ENV + + - name: Add outputs + run: | + echo "::set-output name=release-version::${{ env.NEXT_RELEASE_VERSION }}" + echo "::set-output name=short-sha::${{ env.SHORT_SHA }}" + id: output-step + + - name: Build and push Docker image + uses: docker/build-push-action@v3 + with: + context: . + file: ./docker/Dockerfile + push: true + cache-from: type=registry,ref=blockscout/blockscout:buildcache + cache-to: type=registry,ref=blockscout/blockscout:buildcache,mode=max + tags: blockscout/blockscout:latest, blockscout/blockscout:${{ env.NEXT_RELEASE_VERSION }}-prerelease-${{ env.SHORT_SHA }} + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_READ_API=false + API_PATH= + NETWORK_PATH= + DISABLE_WEBAPP=false + DISABLE_WRITE_API=false + CACHE_ENABLE_TOTAL_GAS_USAGE_COUNTER= + WOBSERVER_ENABLED=false + ADMIN_PANEL_ENABLED=false + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + DISABLE_BRIDGE_MARKET_CAP_UPDATER=false + CACHE_BRIDGE_MARKET_CAP_UPDATE_INTERVAL= + SOCKET_ROOT= + tests: + needs: push_to_registry + uses: blockscout/blockscout-ci-cd/.github/workflows/e2e_new.yaml@master + with: + blockscoutImage: blockscout/blockscout:${{ needs.push_to_registry.outputs.release-version }}-prerelease-${{ needs.push_to_registry.outputs.short-sha }} + blockscoutIngressHost: e2e-blockscout-$GITHUB_SHA_SHORT + frontendIngressHost: e2e-blockscout-$GITHUB_SHA_SHORT + gethIngressHost: e2e-geth-$GITHUB_SHA_SHORT + scVerifierIngressHost: e2e-sc-verifier-$GITHUB_SHA_SHORT + secrets: inherit diff --git a/.github/workflows/publish-docker-image-release.yml b/.github/workflows/publish-docker-image-release.yml new file mode 100644 index 0000000..0586187 --- /dev/null +++ b/.github/workflows/publish-docker-image-release.yml @@ -0,0 +1,111 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Publish Docker image + +on: + release: + types: [published] + +env: + OTP_VERSION: '24.3.4.1' + ELIXIR_VERSION: '1.13.4' + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + env: + RELEASE_VERSION: 4.1.8 + steps: + - name: Check out the repo + uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 + with: + images: blockscout/blockscout + + - name: Build & Push Docker image + uses: docker/build-push-action@v3 + with: + context: . + file: ./docker/Dockerfile + push: true + cache-from: type=registry,ref=blockscout/blockscout:buildcache + cache-to: type=registry,ref=blockscout/blockscout:buildcache,mode=max + tags: blockscout/blockscout:latest, blockscout/blockscout:${{ env.RELEASE_VERSION }} + platforms: | + linux/arm64 + linux/amd64 + build-args: | + CACHE_EXCHANGE_RATES_PERIOD= + DISABLE_READ_API=false + API_PATH=/ + NETWORK_PATH=/ + DISABLE_WEBAPP=false + DISABLE_WRITE_API=false + CACHE_ENABLE_TOTAL_GAS_USAGE_COUNTER= + WOBSERVER_ENABLED=false + ADMIN_PANEL_ENABLED= + CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL= + SOCKET_ROOT= + + merge-master-after-release: + name: Merge 'master' to specific branch after release + runs-on: ubuntu-latest + env: + BRANCHES: | + production-core-stg + production-eth-stg + production-harmony-mainnet-shard-0-stg + production-lukso-stg + production-optimism-goerli-stg + production-optimism-bedrock-goerli-stg + production-optimism-mainnet-stg + production-optimism-stg + production-rsk-stg + production-sokol-stg + production-xdai-stg + steps: + - uses: actions/checkout@v2 + - name: Set Git config + run: | + git config --local user.email "actions@github.com" + git config --local user.name "Github Actions" + - name: Merge master back after release + run: | + git fetch --unshallow + touch errors.txt + for branch in $BRANCHES; + do + git reset --merge + git checkout master + git fetch origin + echo $branch + git ls-remote --exit-code --heads origin $branch || { echo $branch >> errors.txt; continue; } + echo "Merge 'master' to $branch" + git checkout $branch + git pull || { echo $branch >> errors.txt; continue; } + git merge --no-ff master -m "Auto-merge master back to $branch" || { echo $branch >> errors.txt; continue; } + git push || { echo $branch >> errors.txt; continue; } + git checkout master; + done + [ -s errors.txt ] && echo "There are problems with merging 'master' to branches:" || echo "Errors file is empty" + cat errors.txt + [ ! -s errors.txt ] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0bc441a --- /dev/null +++ b/.gitignore @@ -0,0 +1,57 @@ +# App artifacts +/_build +/apps/*/cover +/apps/*/logs +/cover +/db +/deps +/doc +/*.ez +/logs + +# mix dialyzer artifacts +/priv/plts + +# Generated on crash by the VM +erl_crash.dump + +# Generated on crash by NPM +npm-debug.log + +# Static artifacts +/apps/**/node_modules + +# Since we are building assets from assets/, +# we ignore priv/static. You may want to comment +# this depending on your deployment strategy. +/apps/*/priv/static/ + +# Files matching config/*.secret.exs pattern contain sensitive +# data and you should not commit them into version control. +# +# Alternatively, you may comment the line below and commit the +# secrets files as long as you replace their contents by environment +# variables. +/apps/*/config/*.secret.exs + +# Wallaby screenshots +screenshots/ + +# Sobelow +.sobelow + +# osx +.DS_Store + +# mix phx.gen.cert self-signed certs for dev +/apps/block_scout_web/priv/cert + +/docker-compose/postgres-data +/docker-compose/redis-data +/docker-compose/tmp +/docker-compose/logs + +.idea/ +*.iml + +.vscode diff --git a/.pairs b/.pairs new file mode 100644 index 0000000..e40bd6e --- /dev/null +++ b/.pairs @@ -0,0 +1,13 @@ +pairs: + cj: CJ Bryan; cj + dr: Doc Ritezel; doc + mo: Matt Olenick; matto + db: Derek Barnes; dgb + rdwb: Desmond Bowe; des + +email: + prefix: pair + domain: ministryofvelocity.com + no_solo_prefix: true + +global: true diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..7b8898c --- /dev/null +++ b/.tool-versions @@ -0,0 +1,3 @@ +elixir 1.14.1 +erlang 25.1.1 +nodejs 16.16.0 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..383e5d7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,2046 @@ +## Current + +### Features + +- [#6407](https://github.com/blockscout/blockscout/pull/6407) - Indexed ratio for int txs fetching stage +- [#6324](https://github.com/blockscout/blockscout/pull/6324) - Add verified contracts list page +- [#6379](https://github.com/blockscout/blockscout/pull/6379) - API v2 for frontend +- [#6351](https://github.com/blockscout/blockscout/pull/6351) - Enable forum link env var +- [#6316](https://github.com/blockscout/blockscout/pull/6316) - Copy public tags functionality to master +- [#6196](https://github.com/blockscout/blockscout/pull/6196) - INDEXER_CATCHUP_BLOCKS_BATCH_SIZE and INDEXER_CATCHUP_BLOCKS_CONCURRENCY env varaibles +- [#6187](https://github.com/blockscout/blockscout/pull/6187) - Filter by created time of verified contracts in listcontracts API endpoint +- [#6092](https://github.com/blockscout/blockscout/pull/6092) - Blockscout Account functionality +- [#6073](https://github.com/blockscout/blockscout/pull/6073) - Add vyper support for rust verifier microservice integration +- [#6111](https://github.com/blockscout/blockscout/pull/6111) - Add Prometheus metrics to indexer +- [#6168](https://github.com/blockscout/blockscout/pull/6168) - Token instance fetcher checks instance owner and updates current token balance +- [#6209](https://github.com/blockscout/blockscout/pull/6209) - Add metrics for block import stages, runners, steps +- [#6257](https://github.com/blockscout/blockscout/pull/6257), [#6276](https://github.com/blockscout/blockscout/pull/6276) - DISABLE_TOKEN_INSTANCE_FETCHER env variable + +### Fixes + +- [#6390](https://github.com/blockscout/blockscout/pull/6390) - Fix transactions responses in API v2 +- [#6357](https://github.com/blockscout/blockscout/pull/6357), [#6409](https://github.com/blockscout/blockscout/pull/6409) - Fix definitions of NETWORK_PATH, API_PATH, SOCKET_ROOT: process trailing slash +- [#6338](https://github.com/blockscout/blockscout/pull/6338) - Fix token search with space +- [#6329](https://github.com/blockscout/blockscout/pull/6329) - Prevent logger from truncating response from rust verifier service in case of an error +- [#6309](https://github.com/blockscout/blockscout/pull/6309) - Fix read contract bug and change address tx count +- [#6303](https://github.com/blockscout/blockscout/pull/6303) - Fix some UI bugs +- [#6243](https://github.com/blockscout/blockscout/pull/6243) - Fix freezes on `/blocks` page +- [#6162](https://github.com/blockscout/blockscout/pull/6162) - Extend token symbol type varchar(255) -> text +- [#6158](https://github.com/blockscout/blockscout/pull/6158) - Add missing clause for merge_twin_vyper_contract_with_changeset function +- [#6090](https://github.com/blockscout/blockscout/pull/6090) - Fix metadata fetching for ERC-1155 tokens instances +- [#6091](https://github.com/blockscout/blockscout/pull/6091) - Improve fetching media type for NFT +- [#6094](https://github.com/blockscout/blockscout/pull/6094) - Fix inconsistent behaviour of `getsourcecode` method +- [#6105](https://github.com/blockscout/blockscout/pull/6105) - Fix some token transfers broadcasting +- [#6106](https://github.com/blockscout/blockscout/pull/6106) - Fix 500 response on `/coin-balance` for empty address +- [#6118](https://github.com/blockscout/blockscout/pull/6118) - Fix unfetched token balances +- [#6163](https://github.com/blockscout/blockscout/pull/6163) - Fix rate limit logs +- [#6223](https://github.com/blockscout/blockscout/pull/6223) - Fix coin_id test +- [#6336](https://github.com/blockscout/blockscout/pull/6336) - Fix sending request on each key in token search +- [#6327](https://github.com/blockscout/blockscout/pull/6327) - Fix and refactor address logs page and search + +### Chore + +- [#6387](https://github.com/blockscout/blockscout/pull/6387) - Fix errors in docker-build and e2e-tests workflows +- [#6325](https://github.com/blockscout/blockscout/pull/6325) - Set http_only attribute of account authorization cookie to false +- [#6343](https://github.com/blockscout/blockscout/pull/6343) - Docker-compose persistent logs +- [#6240](https://github.com/blockscout/blockscout/pull/6240) - Elixir 1.14 support +- [#6204](https://github.com/blockscout/blockscout/pull/6204) - Refactor contract libs render, CONTRACT_VERIFICATION_MAX_LIBRARIES, refactor parsing integer env vars in config +- [#6195](https://github.com/blockscout/blockscout/pull/6195) - Docker compose configs improvements: Redis container name and persistent storage +- [#6192](https://github.com/blockscout/blockscout/pull/6192), [#6207](https://github.com/blockscout/blockscout/pull/6207) - Hide Indexing Internal Transactions message, if INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=true +- [#6183](https://github.com/blockscout/blockscout/pull/6183) - Transparent coin name definition +- [#6155](https://github.com/blockscout/blockscout/pull/6155), [#6189](https://github.com/blockscout/blockscout/pull/6189) - Refactor Ethereum JSON RPC variants +- [#6125](https://github.com/blockscout/blockscout/pull/6125) - Rename obsolete "parity" EthereumJSONRPC.Variant to "nethermind" +- [#6124](https://github.com/blockscout/blockscout/pull/6124) - Docker compose: add config for Erigon +- [#6061](https://github.com/blockscout/blockscout/pull/6061) - Discord badge and updated permalink + +### Dependencies version bumps + +- [#6053](https://github.com/blockscout/blockscout/pull/6053) - Bump jest-environment-jsdom from 29.0.1 to 29.0.2 in /apps/block_scout_web/assets +- [#6055](https://github.com/blockscout/blockscout/pull/6055) - Bump @babel/core from 7.18.13 to 7.19.0 in /apps/block_scout_web/assets +- [#6054](https://github.com/blockscout/blockscout/pull/6054) - Bump jest from 29.0.1 to 29.0.2 in /apps/block_scout_web/assets +- [#6056](https://github.com/blockscout/blockscout/pull/6056) - Bump @babel/preset-env from 7.18.10 to 7.19.0 in /apps/block_scout_web/assets +- [#6064](https://github.com/blockscout/blockscout/pull/6064) - Bump sweetalert2 from 11.4.29 to 11.4.31 in /apps/block_scout_web/assets +- [#6075](https://github.com/blockscout/blockscout/pull/6075) - Bump sweetalert2 from 11.4.31 to 11.4.32 in /apps/block_scout_web/assets +- [#6082](https://github.com/blockscout/blockscout/pull/6082) - Bump core-js from 3.25.0 to 3.25.1 in /apps/block_scout_web/assets +- [#6083](https://github.com/blockscout/blockscout/pull/6083) - Bump sass from 1.54.8 to 1.54.9 in /apps/block_scout_web/assets +- [#6095](https://github.com/blockscout/blockscout/pull/6095) - Bump jest-environment-jsdom from 29.0.2 to 29.0.3 in /apps/block_scout_web/assets +- [#6096](https://github.com/blockscout/blockscout/pull/6096) - Bump exvcr from 0.13.3 to 0.13.4 +- [#6101](https://github.com/blockscout/blockscout/pull/6101) - Bump ueberauth from 0.10.1 to 0.10.2 +- [#6102](https://github.com/blockscout/blockscout/pull/6102) - Bump eslint from 8.23.0 to 8.23.1 in /apps/block_scout_web/assets +- [#6098](https://github.com/blockscout/blockscout/pull/6098) - Bump ex_json_schema from 0.9.1 to 0.9.2 +- [#6097](https://github.com/blockscout/blockscout/pull/6097) - Bump autoprefixer from 10.4.8 to 10.4.9 in /apps/block_scout_web/assets +- [#6099](https://github.com/blockscout/blockscout/pull/6099) - Bump jest from 29.0.2 to 29.0.3 in /apps/block_scout_web/assets +- [#6103](https://github.com/blockscout/blockscout/pull/6103) - Bump css-minimizer-webpack-plugin from 4.0.0 to 4.1.0 in /apps/block_scout_web/assets +- [#6108](https://github.com/blockscout/blockscout/pull/6108) - Bump autoprefixer from 10.4.9 to 10.4.10 in /apps/block_scout_web/assets +- [#6116](https://github.com/blockscout/blockscout/pull/6116) - Bump autoprefixer from 10.4.10 to 10.4.11 in /apps/block_scout_web/assets +- [#6114](https://github.com/blockscout/blockscout/pull/6114) - Bump @babel/core from 7.19.0 to 7.19.1 in /apps/block_scout_web/assets +- [#6113](https://github.com/blockscout/blockscout/pull/6113) - Bump ueberauth from 0.10.2 to 0.10.3 +- [#6112](https://github.com/blockscout/blockscout/pull/6112) - Bump @babel/preset-env from 7.19.0 to 7.19.1 in /apps/block_scout_web/assets +- [#6115](https://github.com/blockscout/blockscout/pull/6115) - Bump web3 from 1.7.5 to 1.8.0 in /apps/block_scout_web/assets +- [#6117](https://github.com/blockscout/blockscout/pull/6117) - Bump sweetalert2 from 11.4.32 to 11.4.33 in /apps/block_scout_web/assets +- [#6119](https://github.com/blockscout/blockscout/pull/6119) - Bump scss-tokenizer from 0.3.0 to 0.4.3 in /apps/block_scout_web/assets +- [#6138](https://github.com/blockscout/blockscout/pull/6138) - Bump core-js from 3.25.1 to 3.25.2 in /apps/block_scout_web/assets +- [#6147](https://github.com/blockscout/blockscout/pull/6147) - Bump autoprefixer from 10.4.11 to 10.4.12 in /apps/block_scout_web/assets +- [#6151](https://github.com/blockscout/blockscout/pull/6151) - Bump sass from 1.54.9 to 1.55.0 in /apps/block_scout_web/assets +- [#6173](https://github.com/blockscout/blockscout/pull/6173) - Bump core-js from 3.25.2 to 3.25.3 in /apps/block_scout_web/assets +- [#6174](https://github.com/blockscout/blockscout/pull/6174) - Bump sweetalert2 from 11.4.33 to 11.4.34 in /apps/block_scout_web/assets +- [#6175](https://github.com/blockscout/blockscout/pull/6175) - Bump luxon from 3.0.3 to 3.0.4 in /apps/block_scout_web/assets +- [#6176](https://github.com/blockscout/blockscout/pull/6176) - Bump @babel/preset-env from 7.19.1 to 7.19.3 in /apps/block_scout_web/assets +- [#6177](https://github.com/blockscout/blockscout/pull/6177) - Bump @babel/core from 7.19.1 to 7.19.3 in /apps/block_scout_web/assets +- [#6178](https://github.com/blockscout/blockscout/pull/6178) - Bump eslint from 8.23.1 to 8.24.0 in /apps/block_scout_web/assets +- [#6184](https://github.com/blockscout/blockscout/pull/6184) - Bump jest from 29.0.3 to 29.1.1 in /apps/block_scout_web/assets +- [#6186](https://github.com/blockscout/blockscout/pull/6186) - Bump jest-environment-jsdom from 29.0.3 to 29.1.1 in /apps/block_scout_web/assets +- [#6185](https://github.com/blockscout/blockscout/pull/6185) - Bump sweetalert2 from 11.4.34 to 11.4.35 in /apps/block_scout_web/assets +- [#6146](https://github.com/blockscout/blockscout/pull/6146) - Bump websocket_client from 1.3.0 to 1.5.0 +- [#6191](https://github.com/blockscout/blockscout/pull/6191) - Bump css-minimizer-webpack-plugin from 4.1.0 to 4.2.0 in /apps/block_scout_web/assets +- [#6199](https://github.com/blockscout/blockscout/pull/6199) - Bump redix from 1.1.5 to 1.2.0 +- [#6213](https://github.com/blockscout/blockscout/pull/6213) - Bump sweetalert2 from 11.4.35 to 11.4.37 in /apps/block_scout_web/assets +- [#6214](https://github.com/blockscout/blockscout/pull/6214) - Bump jest-environment-jsdom from 29.1.1 to 29.1.2 in /apps/block_scout_web/assets +- [#6215](https://github.com/blockscout/blockscout/pull/6215) - Bump postcss from 8.4.16 to 8.4.17 in /apps/block_scout_web/assets +- [#6216](https://github.com/blockscout/blockscout/pull/6216) - Bump core-js from 3.25.3 to 3.25.5 in /apps/block_scout_web/assets +- [#6217](https://github.com/blockscout/blockscout/pull/6217) - Bump jest from 29.1.1 to 29.1.2 in /apps/block_scout_web/assets +- [#6229](https://github.com/blockscout/blockscout/pull/6229) - Bump sweetalert2 from 11.4.37 to 11.4.38 in /apps/block_scout_web/assets +- [#6232](https://github.com/blockscout/blockscout/pull/6232) - Bump css-minimizer-webpack-plugin from 4.2.0 to 4.2.1 in /apps/block_scout_web/assets +- [#6230](https://github.com/blockscout/blockscout/pull/6230) - Bump sass-loader from 13.0.2 to 13.1.0 in /apps/block_scout_web/assets +- [#6251](https://github.com/blockscout/blockscout/pull/6251) - Bump sweetalert2 from 11.4.38 to 11.5.1 in /apps/block_scout_web/assets +- [#6246](https://github.com/blockscout/blockscout/pull/6246) - Bump @babel/preset-env from 7.19.3 to 7.19.4 in /apps/block_scout_web/assets +- [#6247](https://github.com/blockscout/blockscout/pull/6247) - Bump ex_abi from 0.5.14 to 0.5.15 +- [#6248](https://github.com/blockscout/blockscout/pull/6248) - Bump eslint from 8.24.0 to 8.25.0 in /apps/block_scout_web/assets +- [#6255](https://github.com/blockscout/blockscout/pull/6255) - Bump postcss from 8.4.17 to 8.4.18 in /apps/block_scout_web/assets +- [#6256](https://github.com/blockscout/blockscout/pull/6256) - Bump css-minimizer-webpack-plugin from 4.2.1 to 4.2.2 in /apps/block_scout_web/assets +- [#6258](https://github.com/blockscout/blockscout/pull/6258) - Bump jest from 29.1.2 to 29.2.0 in /apps/block_scout_web/assets +- [#6259](https://github.com/blockscout/blockscout/pull/6259) - Bump jest-environment-jsdom from 29.1.2 to 29.2.0 in /apps/block_scout_web/assets +- [#6253](https://github.com/blockscout/blockscout/pull/6253) - Bump eslint-plugin-promise from 6.0.1 to 6.1.0 in /apps/block_scout_web/assets +- [#6279](https://github.com/blockscout/blockscout/pull/6279) - Bump util from 0.12.4 to 0.12.5 in /apps/block_scout_web/assets +- [#6280](https://github.com/blockscout/blockscout/pull/6280) - Bump ex_rlp from 0.5.4 to 0.5.5 +- [#6281](https://github.com/blockscout/blockscout/pull/6281) - Bump ex_abi from 0.5.15 to 0.5.16 +- [#6283](https://github.com/blockscout/blockscout/pull/6283) - Bump spandex_datadog from 1.2.0 to 1.3.0 +- [#6282](https://github.com/blockscout/blockscout/pull/6282) - Bump sweetalert2 from 11.5.1 to 11.5.2 in /apps/block_scout_web/assets +- [#6284](https://github.com/blockscout/blockscout/pull/6284) - Bump spandex_phoenix from 1.0.6 to 1.1.0 +- [#6298](https://github.com/blockscout/blockscout/pull/6298) - Bump jest-environment-jsdom from 29.2.0 to 29.2.1 in /apps/block_scout_web/assets +- [#6297](https://github.com/blockscout/blockscout/pull/6297) - Bump jest from 29.2.0 to 29.2.1 in /apps/block_scout_web/assets +- [#6254](https://github.com/blockscout/blockscout/pull/6254) - Bump ex_doc from 0.28.5 to 0.28.6 +- [#6314](https://github.com/blockscout/blockscout/pull/6314) - Bump @babel/core from 7.19.3 to 7.19.6 in /apps/block_scout_web/assets +- [#6313](https://github.com/blockscout/blockscout/pull/6313) - Bump ex_doc from 0.28.6 to 0.29.0 +- [#6305](https://github.com/blockscout/blockscout/pull/6305) - Bump sweetalert2 from 11.5.2 to 11.6.0 in /apps/block_scout_web/assets +- [#6312](https://github.com/blockscout/blockscout/pull/6312) - Bump eslint-plugin-promise from 6.1.0 to 6.1.1 in /apps/block_scout_web/assets +- [#6318](https://github.com/blockscout/blockscout/pull/6318) - Bump spandex from 3.1.0 to 3.2.0 + + +## 4.1.8-beta + +### Features + +- [#5968](https://github.com/blockscout/blockscout/pull/5968) - Add call type in the response of txlistinternal API method +- [#5860](https://github.com/blockscout/blockscout/pull/5860) - Integrate rust verifier micro-service ([blockscout-rs/verifier](https://github.com/blockscout/blockscout-rs/tree/main/verification)) +- [#6001](https://github.com/blockscout/blockscout/pull/6001) - Add ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES env var that filters requests and query node only if the block quantity is "latest" +- [#5944](https://github.com/blockscout/blockscout/pull/5944) - Add tab with state changes to transaction page + +### Fixes + +- [#6038](https://github.com/blockscout/blockscout/pull/6038) - Extend token name from string to text type +- [#6037](https://github.com/blockscout/blockscout/pull/6037) - Fix order of results in txlistinternal API endpoint +- [#6036](https://github.com/blockscout/blockscout/pull/6036) - Fix address checksum on transaction page +- [#6032](https://github.com/blockscout/blockscout/pull/6032) - Sort by address.hash column in accountlist API endpoint +- [#6017](https://github.com/blockscout/blockscout/pull/6017), [#6028](https://github.com/blockscout/blockscout/pull/6028) - Move "contract interaction" and "Add chain to MM" env vars to runtime +- [#6012](https://github.com/blockscout/blockscout/pull/6012) - Fix display of estimated addresses counter on the main page +- [#5978](https://github.com/blockscout/blockscout/pull/5978) - Allow timestamp param in the log of eth_getTransactionReceipt method +- [#5977](https://github.com/blockscout/blockscout/pull/5977) - Fix address overview.html.eex in case of nil implementation address hash +- [#5975](https://github.com/blockscout/blockscout/pull/5975) - Fix CSV export of internal transactions +- [#5957](https://github.com/blockscout/blockscout/pull/5957) - Server-side reCAPTCHA check for CSV export +- [#5954](https://github.com/blockscout/blockscout/pull/5954) - Fix ace editor appearance +- [#5942](https://github.com/blockscout/blockscout/pull/5942), [#5945](https://github.com/blockscout/blockscout/pull/5945) - Fix nightly solidity versions filtering UX +- [#5904](https://github.com/blockscout/blockscout/pull/5904) - Enhance health API endpoint: better parsing HEALTHY_BLOCKS_PERIOD and use it in the response +- [#5903](https://github.com/blockscout/blockscout/pull/5903) - Disable compile env validation +- [#5887](https://github.com/blockscout/blockscout/pull/5887) - Added missing environment variables to Makefile container params +- [#5850](https://github.com/blockscout/blockscout/pull/5850) - Fix too large postgres notifications +- [#5809](https://github.com/blockscout/blockscout/pull/5809) - Fix 404 on `/metadata` page +- [#5807](https://github.com/blockscout/blockscout/pull/5807) - Update Makefile migrate command due to release build +- [#5786](https://github.com/blockscout/blockscout/pull/5786) - Replace `current_path` with `Controller.current_full_path` in two controllers +- [#5948](https://github.com/blockscout/blockscout/pull/5948) - Fix unexpected messages in `CoinBalanceOnDemand` +- [#6013](https://github.com/blockscout/blockscout/pull/6013) - Fix ERC-1155 tokens fetching +- [#6043](https://github.com/blockscout/blockscout/pull/6043) - Fix token instance fetching +- [#6093](https://github.com/blockscout/blockscout/pull/6093) - Fix Indexer.Fetcher.TokenInstance for ERC-1155 tokens + +### Chore + +- [#5921](https://github.com/blockscout/blockscout/pull/5921) - Bump briefly from 25942fb to 1dd66ee +- [#6033](https://github.com/blockscout/blockscout/pull/6033) - Bump sass from 1.54.7 to 1.54.8 in /apps/block_scout_web/assets +- [#6046](https://github.com/blockscout/blockscout/pull/6046) - Bump credo from 1.6.6 to 1.6.7 +- [#6045](https://github.com/blockscout/blockscout/pull/6045) - Re-use _btn_copy.html for raw trace page +- [#6035](https://github.com/blockscout/blockscout/pull/6035) - Hide copy btn if no raw trace +- [#6034](https://github.com/blockscout/blockscout/pull/6034) - Suppress empty sections in supported chain dropdown +- [#5939](https://github.com/blockscout/blockscout/pull/5939) - Bump sweetalert2 from 11.4.26 to 11.4.27 in /apps/block_scout_web/assets +- [#5938](https://github.com/blockscout/blockscout/pull/5938) - Bump xss from 1.0.13 to 1.0.14 in /apps/block_scout_web/assets +- [#5743](https://github.com/blockscout/blockscout/pull/5743) - Fixing tracer not found #5729 +- [#5952](https://github.com/blockscout/blockscout/pull/5952) - Bump sweetalert2 from 11.4.27 to 11.4.28 in /apps/block_scout_web/assets +- [#5955](https://github.com/blockscout/blockscout/pull/5955) - Bump ex_doc from 0.28.4 to 0.28.5 +- [#5956](https://github.com/blockscout/blockscout/pull/5956) - Bump bcrypt_elixir from 2.3.1 to 3.0.1 +- [#5964](https://github.com/blockscout/blockscout/pull/5964) - Bump sweetalert2 from 11.4.28 to 11.4.29 in /apps/block_scout_web/assets +- [#5966](https://github.com/blockscout/blockscout/pull/5966) - Bump sass from 1.54.4 to 1.54.5 in /apps/block_scout_web/assets +- [#5967](https://github.com/blockscout/blockscout/pull/5967) - Bump @babel/core from 7.18.10 to 7.18.13 in /apps/block_scout_web/assets +- [#5973](https://github.com/blockscout/blockscout/pull/5973) - Bump prometheus from 4.9.0 to 4.9.1 +- [#5974](https://github.com/blockscout/blockscout/pull/5974) - Bump cldr_utils from 2.19.0 to 2.19.1 +- [#5884](https://github.com/blockscout/blockscout/pull/5884) - Bump nimble_csv from 1.1.0 to 1.2.0 +- [#5984](https://github.com/blockscout/blockscout/pull/5984) - Bump jest from 28.1.3 to 29.0.0 in /apps/block_scout_web/assets +- [#5983](https://github.com/blockscout/blockscout/pull/5983) - Bump core-js from 3.24.1 to 3.25.0 in /apps/block_scout_web/assets +- [#5981](https://github.com/blockscout/blockscout/pull/5981) - Bump eslint-plugin-promise from 6.0.0 to 6.0.1 in /apps/block_scout_web/assets +- [#5982](https://github.com/blockscout/blockscout/pull/5982) - Bump jest-environment-jsdom from 28.1.3 to 29.0.0 in /apps/block_scout_web/assets +- [#5987](https://github.com/blockscout/blockscout/pull/5987) - Bump jest from 29.0.0 to 29.0.1 in /apps/block_scout_web/assets +- [#5988](https://github.com/blockscout/blockscout/pull/5988) - Bump jest-environment-jsdom from 29.0.0 to 29.0.1 in /apps/block_scout_web/assets +- [#5989](https://github.com/blockscout/blockscout/pull/5989) - Bump jquery from 3.6.0 to 3.6.1 in /apps/block_scout_web/assets +- [#5990](https://github.com/blockscout/blockscout/pull/5990) - Bump web3modal from 1.9.8 to 1.9.9 in /apps/block_scout_web/assets +- [#6004](https://github.com/blockscout/blockscout/pull/6004) - Bump luxon from 3.0.1 to 3.0.3 in /apps/block_scout_web/assets +- [#6005](https://github.com/blockscout/blockscout/pull/6005) - Bump ex_cldr from 2.33.1 to 2.33.2 +- [#6006](https://github.com/blockscout/blockscout/pull/6006) - Bump eslint from 8.22.0 to 8.23.0 in /apps/block_scout_web/assets +- [#6015](https://github.com/blockscout/blockscout/pull/6015) - Bump @fortawesome/fontawesome-free from 6.1.2 to 6.2.0 in /apps/block_scout_web/assets +- [#6021](https://github.com/blockscout/blockscout/pull/6021) - Bump sass from 1.54.5 to 1.54.7 in /apps/block_scout_web/assets +- [#6018](https://github.com/blockscout/blockscout/pull/6018) - Update chromedriver version +- [#5836](https://github.com/blockscout/blockscout/pull/5836) - Bump comeonin from 4.1.2 to 5.3.3 +- [#5869](https://github.com/blockscout/blockscout/pull/5869) - Bump reduce-reducers from 0.4.3 to 1.0.4 in /apps/block_scout_web/assets +- [#5919](https://github.com/blockscout/blockscout/pull/5919) - Bump floki from 0.32.1 to 0.33.1 +- [#5930](https://github.com/blockscout/blockscout/pull/5930) - Bump eslint from 8.21.0 to 8.22.0 in /apps/block_scout_web/assets +- [#5845](https://github.com/blockscout/blockscout/pull/5845) - Bump autoprefixer from 10.4.2 to 10.4.8 in /apps/block_scout_web/assets +- [#5877](https://github.com/blockscout/blockscout/pull/5877) - Bump eslint from 8.17.0 to 8.21.0 in /apps/block_scout_web/assets +- [#5875](https://github.com/blockscout/blockscout/pull/5875) - Bump sass from 1.49.8 to 1.54.3 in /apps/block_scout_web/assets +- [#5873](https://github.com/blockscout/blockscout/pull/5873) - Bump highlight.js from 11.4.0 to 11.6.0 in /apps/block_scout_web/assets +- [#5870](https://github.com/blockscout/blockscout/pull/5870) - Bump spandex_ecto from 0.6.2 to 0.7.0 +- [#5867](https://github.com/blockscout/blockscout/pull/5867) - Bump @babel/preset-env from 7.16.11 to 7.18.10 in /apps/block_scout_web/assets +- [#5876](https://github.com/blockscout/blockscout/pull/5876) - Bump bignumber.js from 9.0.2 to 9.1.0 in /apps/block_scout_web/assets +- [#5871](https://github.com/blockscout/blockscout/pull/5871) - Bump redux from 4.1.2 to 4.2.0 in /apps/block_scout_web/assets +- [#5868](https://github.com/blockscout/blockscout/pull/5868) - Bump ex_rlp from 0.5.3 to 0.5.4 +- [#5874](https://github.com/blockscout/blockscout/pull/5874) - Bump core-js from 3.20.3 to 3.24.1 in /apps/block_scout_web/assets +- [#5882](https://github.com/blockscout/blockscout/pull/5882) - Bump math from 0.3.1 to 0.7.0 +- [#5878](https://github.com/blockscout/blockscout/pull/5878) - Bump css-minimizer-webpack-plugin from 3.4.1 to 4.0.0 in /apps/block_scout_web/assets +- [#5883](https://github.com/blockscout/blockscout/pull/5883) - Bump postgrex from 0.15.10 to 0.15.13 +- [#5885](https://github.com/blockscout/blockscout/pull/5885) - Bump hammer from 6.0.0 to 6.1.0 +- [#5893](https://github.com/blockscout/blockscout/pull/5893) - Bump prometheus from 4.8.1 to 4.9.0 +- [#5892](https://github.com/blockscout/blockscout/pull/5892) - Bump babel-loader from 8.2.3 to 8.2.5 in /apps/block_scout_web/assets +- [#5890](https://github.com/blockscout/blockscout/pull/5890) - Bump sweetalert2 from 11.3.10 to 11.4.26 in /apps/block_scout_web/assets +- [#5889](https://github.com/blockscout/blockscout/pull/5889) - Bump sass from 1.54.3 to 1.54.4 in /apps/block_scout_web/assets +- [#5894](https://github.com/blockscout/blockscout/pull/5894) - Bump jest from 27.4.7 to 28.1.3 in /apps/block_scout_web/assets +- [#5865](https://github.com/blockscout/blockscout/pull/5865) - Bump timex from 3.7.1 to 3.7.9 +- [#5872](https://github.com/blockscout/blockscout/pull/5872) - Bump benchee from 0.13.2 to 0.99.0 +- [#5895](https://github.com/blockscout/blockscout/pull/5895) - Bump wallaby from 0.29.1 to 0.30.1 +- [#5905](https://github.com/blockscout/blockscout/pull/5905) - Bump absinthe from 1.6.5 to 1.6.8 +- [#5881](https://github.com/blockscout/blockscout/pull/5881) - Bump dataloader from 1.0.9 to 1.0.10 +- [#5909](https://github.com/blockscout/blockscout/pull/5909) - Bump junit_formatter from 3.3.0 to 3.3.1 +- [#5912](https://github.com/blockscout/blockscout/pull/5912) - Bump credo from 1.6.4 to 1.6.6 +- [#5911](https://github.com/blockscout/blockscout/pull/5911) - Bump absinthe_relay from 1.5.1 to 1.5.2 +- [#5915](https://github.com/blockscout/blockscout/pull/5915) - Bump flow from 0.15.0 to 1.2.0 +- [#5916](https://github.com/blockscout/blockscout/pull/5916) - Bump dialyxir from 1.1.0 to 1.2.0 +- [#5910](https://github.com/blockscout/blockscout/pull/5910) - Bump benchee from 0.99.0 to 1.1.0 +- [#5917](https://github.com/blockscout/blockscout/pull/5917) - Bump bypass from 1.0.0 to 2.1.0 +- [#5920](https://github.com/blockscout/blockscout/pull/5920) - Bump spandex_datadog from 1.1.0 to 1.2.0 +- [#5918](https://github.com/blockscout/blockscout/pull/5918) - Bump logger_file_backend from 0.0.12 to 0.0.13 +- [#5863](https://github.com/blockscout/blockscout/pull/5863) - Update Poison hex package +- [#5861](https://github.com/blockscout/blockscout/pull/5861) - Add cache for docker build +- [#5859](https://github.com/blockscout/blockscout/pull/5859) - Update ex_cldr hex packages +- [#5858](https://github.com/blockscout/blockscout/pull/5858) - Update CHANGELOG; revert update of css-loader; rename fontawesome icons selectors +- [#5811](https://github.com/blockscout/blockscout/pull/5811) - Bump chartjs-adapter-luxon from 1.1.0 to 1.2.0 in /apps/block_scout_web/assets +- [#5814](https://github.com/blockscout/blockscout/pull/5814) - Bump webpack from 5.69.1 to 5.74.0 in /apps/block_scout_web/assets +- [#5812](https://github.com/blockscout/blockscout/pull/5812) - Bump mini-css-extract-plugin from 2.5.3 to 2.6.1 in /apps/block_scout_web/assets +- [#5819](https://github.com/blockscout/blockscout/pull/5819) - Bump xss from 1.0.10 to 1.0.13 in /apps/block_scout_web/assets +- [#5818](https://github.com/blockscout/blockscout/pull/5818) - Bump @fortawesome/fontawesome-free from 6.0.0-beta3 to 6.1.2 in /apps/block_scout_web/assets +- [#5821](https://github.com/blockscout/blockscout/pull/5821) - Bump spandex from 3.0.3 to 3.1.0 +- [#5830](https://github.com/blockscout/blockscout/pull/5830) - Bump spandex_phoenix from 1.0.5 to 1.0.6 +- [#5825](https://github.com/blockscout/blockscout/pull/5825) - Bump postcss from 8.4.6 to 8.4.16 in /apps/block_scout_web/assets +- [#5816](https://github.com/blockscout/blockscout/pull/5816) - Bump webpack-cli from 4.9.2 to 4.10.0 in /apps/block_scout_web/assets +- [#5822](https://github.com/blockscout/blockscout/pull/5822) - Bump chart.js from 3.7.0 to 3.9.1 in /apps/block_scout_web/assets +- [#5829](https://github.com/blockscout/blockscout/pull/5829) - Bump mox from 0.5.2 to 1.0.2 +- [#5823](https://github.com/blockscout/blockscout/pull/5823) - Bump luxon from 2.4.0 to 3.0.1 in /apps/block_scout_web/assets +- [#5837](https://github.com/blockscout/blockscout/pull/5837) - Bump @walletconnect/web3-provider from 1.7.8 to 1.8.0 in /apps/block_scout_web/assets +- [#5840](https://github.com/blockscout/blockscout/pull/5840) - Bump web3modal from 1.9.5 to 1.9.8 in /apps/block_scout_web/assets +- [#5842](https://github.com/blockscout/blockscout/pull/5842) - Bump copy-webpack-plugin from 10.2.1 to 11.0.0 in /apps/block_scout_web/assets +- [#5835](https://github.com/blockscout/blockscout/pull/5835) - Bump tesla from 1.3.3 to 1.4.4 +- [#5841](https://github.com/blockscout/blockscout/pull/5841) - Bump sass-loader from 12.6.0 to 13.0.2 in /apps/block_scout_web/assets +- [#5844](https://github.com/blockscout/blockscout/pull/5844) - Bump postcss-loader from 6.2.1 to 7.0.1 in /apps/block_scout_web/assets +- [#5838](https://github.com/blockscout/blockscout/pull/5838) - Bump path-parser from 4.2.0 to 6.1.0 in /apps/block_scout_web/assets +- [#5843](https://github.com/blockscout/blockscout/pull/5843) - Bump @tarekraafat/autocomplete.js from 10.2.6 to 10.2.7 in /apps/block_scout_web/assets +- [#5834](https://github.com/blockscout/blockscout/pull/5834) - Bump clipboard from 2.0.9 to 2.0.11 in /apps/block_scout_web/assets +- [#5827](https://github.com/blockscout/blockscout/pull/5827) - Bump @babel/core from 7.16.12 to 7.18.10 in /apps/block_scout_web/assets +- [#5851](https://github.com/blockscout/blockscout/pull/5851) - Bump exvcr from 0.13.2 to 0.13.3 +- [#5824](https://github.com/blockscout/blockscout/pull/5824) - Bump ex_json_schema from 0.6.2 to 0.9.1 +- [#5849](https://github.com/blockscout/blockscout/pull/5849) - Bump gettext 0.18.2 -> 0.20.0 +- [#5806](https://github.com/blockscout/blockscout/pull/5806) - Update target Postgres version in Docker: 13 -> 14 + +## 4.1.7-beta + +### Features + +- [#5783](https://github.com/blockscout/blockscout/pull/5783) - Allow to setup multiple ranges of blocks to index + +### Fixes + +- [#5799](https://github.com/blockscout/blockscout/pull/5799) - Fix address_tokens_usd_sum function +- [#5798](https://github.com/blockscout/blockscout/pull/5798) - Copy explorer node_modules to result image +- [#5797](https://github.com/blockscout/blockscout/pull/5797) - Fix flickering token tooltip + +### Chore + +- [#5796](https://github.com/blockscout/blockscout/pull/5796) - Add job for e2e tests on every push to master + fix job "Merge 'master' to specific branch after release" + +## 4.1.6-beta + +### Features + +- [#5739](https://github.com/blockscout/blockscout/pull/5739) - Erigon archive node support +- [#5732](https://github.com/blockscout/blockscout/pull/5732) - Manage testnet label (right to the navbar logo) +- [#5699](https://github.com/blockscout/blockscout/pull/5699) - Switch to basic (non-pro) API endpoint for Coingecko requests, if API key is not provided +- [#5542](https://github.com/blockscout/blockscout/pull/5542) - Add `jq` in docker image +- [#5345](https://github.com/blockscout/blockscout/pull/5345) - Graphql: add user-selected ordering to transactions for address query + +### Fixes + +- [#5768](https://github.com/blockscout/blockscout/pull/5768) - Outstanding rows limit for missing blocks query (catchup fetcher) +- [#5737](https://github.com/blockscout/blockscout/pull/5737), [#5772](https://github.com/blockscout/blockscout/pull/5772) - Fix double requests; Fix token balances dropdown view +- [#5723](https://github.com/blockscout/blockscout/pull/5723) - Add nil clause for Data.to_string/1 +- [#5714](https://github.com/blockscout/blockscout/pull/5714) - Add clause for EthereumJSONRPC.Transaction.elixir_to_params/1 when gas_price is missing in the response +- [#5697](https://github.com/blockscout/blockscout/pull/5697) - Gas price oracle: ignore gas price rounding for values less than 0.01 +- [#5690](https://github.com/blockscout/blockscout/pull/5690) - Allow special characters for password in DB URL parser +- [#5778](https://github.com/blockscout/blockscout/pull/5778) - Allow hyphen in database name + +### Chore +- [#5787](https://github.com/blockscout/blockscout/pull/5787) - Add job for merging master to specific branch after release +- [#5788](https://github.com/blockscout/blockscout/pull/5788) - Update Docker image on every push to master branch +- [#5736](https://github.com/blockscout/blockscout/pull/5736) - Remove obsolete network selector +- [#5730](https://github.com/blockscout/blockscout/pull/5730) - Add primary keys for DB tables where they do not exist +- [#5703](https://github.com/blockscout/blockscout/pull/5703) - Remove bridged tokens functionality from Blockscout core +- [#5700](https://github.com/blockscout/blockscout/pull/5700) - Remove Staking dapp logic from Blockscout core +- [#5696](https://github.com/blockscout/blockscout/pull/5696) - Update .tool-versions +- [#5695](https://github.com/blockscout/blockscout/pull/5695) - Decimal hex package update 1.9 -> 2.0 +- [#5684](https://github.com/blockscout/blockscout/pull/5684) - Block import timings logs + +## 4.1.5-beta + +### Features + +- [#5667](https://github.com/blockscout/blockscout/pull/5667) - Address page: scroll to selected tab's data + +### Fixes + +- [#5680](https://github.com/blockscout/blockscout/pull/5680) - Fix broken token icons; Disable animation in lists; Fix doubled requests for some pages +- [#5671](https://github.com/blockscout/blockscout/pull/5671) - Fix double requests for token exchange rates; Disable fetching `btc_value` by default (add `EXCHANGE_RATES_FETCH_BTC_VALUE` env variable); Add `CACHE_EXCHANGE_RATES_PERIOD` env variable +- [#5676](https://github.com/blockscout/blockscout/pull/5676) - Fix wrong miner address shown for post EIP-1559 block for clique network + +### Chore + +- [#5679](https://github.com/blockscout/blockscout/pull/5679) - Optimize query in fetch_min_missing_block_cache function +- [#5674](https://github.com/blockscout/blockscout/pull/5674) - Disable token holder refreshing +- [#5661](https://github.com/blockscout/blockscout/pull/5661) - Fixes yaml syntax for boolean env variables in docker compose + +## 4.1.4-beta + +### Features + +- [#5656](https://github.com/blockscout/blockscout/pull/5656) - Gas price oracle +- [#5613](https://github.com/blockscout/blockscout/pull/5613) - Exchange rates CoinMarketCap source module +- [#5588](https://github.com/blockscout/blockscout/pull/5588) - Add broadcasting of coin balance +- [#5560](https://github.com/blockscout/blockscout/pull/5560) - Manual fetch benefeciaries +- [#5479](https://github.com/blockscout/blockscout/pull/5479) - Remake of solidity verifier module; Verification UX improvements +- [#5540](https://github.com/blockscout/blockscout/pull/5540) - Tx page: scroll to selected tab's data + +### Fixes + +- [#5647](https://github.com/blockscout/blockscout/pull/5647) - Add handling for invalid Sourcify response +- [#5635](https://github.com/blockscout/blockscout/pull/5635) - Set CoinGecko source in exchange_rates_source function fix in case of token_bridge +- [#5629](https://github.com/blockscout/blockscout/pull/5629) - Fix empty coin balance for empty address +- [#5612](https://github.com/blockscout/blockscout/pull/5612) - Fix token transfers order +- [#5626](https://github.com/blockscout/blockscout/pull/5626) - Fix vyper compiler versions order +- [#5603](https://github.com/blockscout/blockscout/pull/5603) - Fix failing verification attempts +- [#5598](https://github.com/blockscout/blockscout/pull/5598) - Fix token dropdown +- [#5592](https://github.com/blockscout/blockscout/pull/5592) - Burn fees for legacy transactions +- [#5568](https://github.com/blockscout/blockscout/pull/5568) - Add regexp for ipfs checking +- [#5567](https://github.com/blockscout/blockscout/pull/5567) - Sanitize token name and symbol before insert into DB, display in the application +- [#5564](https://github.com/blockscout/blockscout/pull/5564) - Add fallback clauses to `string_to_..._hash` functions +- [#5538](https://github.com/blockscout/blockscout/pull/5538) - Fix internal transaction's tile bug + +### Chore + +- [#5660](https://github.com/blockscout/blockscout/pull/5660) - Display txs count chart by default, disable price chart by default, add chart titles +- [#5659](https://github.com/blockscout/blockscout/pull/5659) - Use chartjs-adapter-luxon instead chartjs-adapter-moment for charts +- [#5651](https://github.com/blockscout/blockscout/pull/5651), [#5657](https://github.com/blockscout/blockscout/pull/5657) - Gnosis chain rebranded theme and generalization of chart legend colors definition +- [#5640](https://github.com/blockscout/blockscout/pull/5640) - Clean up and fix tests, reduce amount of warnings +- [#5625](https://github.com/blockscout/blockscout/pull/5625) - Get rid of some redirects to checksummed address url +- [#5623](https://github.com/blockscout/blockscout/pull/5623) - Allow hyphen in DB password +- [#5543](https://github.com/blockscout/blockscout/pull/5543) - Increase max_restarts to 1_000 (from 3 by default) for explorer, block_scout_web supervisors +- [#5536](https://github.com/blockscout/blockscout/pull/5536) - NPM audit fix + +## 4.1.3-beta + +### Features + +- [#5515](https://github.com/blockscout/blockscout/pull/5515) - Integrate ace editor to display contract sources +- [#5505](https://github.com/blockscout/blockscout/pull/5505) - Manage debug_traceTransaction JSON RPC method timeout +- [#5491](https://github.com/blockscout/blockscout/pull/5491) - Sequential blocks broadcast on the main page +- [#5312](https://github.com/blockscout/blockscout/pull/5312) - Add OpenZeppelin proxy storage slot +- [#5302](https://github.com/blockscout/blockscout/pull/5302) - Add specific tx receipt fields for the GoQuorum client +- [#5268](https://github.com/blockscout/blockscout/pull/5268), [#5313](https://github.com/blockscout/blockscout/pull/5313) - Contract names display improvement + +### Fixes + +- [#5528](https://github.com/blockscout/blockscout/pull/5528) - Token balances fetcher retry +- [#5524](https://github.com/blockscout/blockscout/pull/5524) - ContractState module resistance to unresponsive archive node +- [#5513](https://github.com/blockscout/blockscout/pull/5513) - Do not fill pending blocks ops with block numbers below TRACE_FIRST_BLOCK +- [#5508](https://github.com/blockscout/blockscout/pull/5508) - Hide indexing banner if we fetched internal transactions from TRACE_FIRST_BLOCK +- [#5504](https://github.com/blockscout/blockscout/pull/5504) - Extend TRACE_FIRST_BLOCK env var to geth variant +- [#5488](https://github.com/blockscout/blockscout/pull/5488) - Split long contract output to multiple lines +- [#5487](https://github.com/blockscout/blockscout/pull/5487) - Fix array displaying in decoded constructor args +- [#5482](https://github.com/blockscout/blockscout/pull/5482) - Fix for querying of the contract read functions +- [#5455](https://github.com/blockscout/blockscout/pull/5455) - Fix unverified_smart_contract function: add md5 of bytecode to the changeset +- [#5454](https://github.com/blockscout/blockscout/pull/5454) - Docker: Fix the qemu-x86_64 signal 11 error on Apple Silicon +- [#5443](https://github.com/blockscout/blockscout/pull/5443) - Geth: display tx revert reason +- [#5420](https://github.com/blockscout/blockscout/pull/5420) - Deduplicate addresses and coin balances before inserting to the DB +- [#5416](https://github.com/blockscout/blockscout/pull/5416) - Fix getsourcecode for EOA addresses +- [#5413](https://github.com/blockscout/blockscout/pull/5413) - Fix params encoding for read contracts methods +- [#5411](https://github.com/blockscout/blockscout/pull/5411) - Fix character_not_in_repertoire error for tx revert reason +- [#5410](https://github.com/blockscout/blockscout/pull/5410) - Handle exited realtime fetcher +- [#5383](https://github.com/blockscout/blockscout/pull/5383) - Fix reload transactions button +- [#5381](https://github.com/blockscout/blockscout/pull/5381), [#5397](https://github.com/blockscout/blockscout/pull/5397) - Fix exchange rate broadcast error +- [#5375](https://github.com/blockscout/blockscout/pull/5375) - Fix pending transactions fetcher +- [#5374](https://github.com/blockscout/blockscout/pull/5374) - Return all ERC-1155's token instances in tokenList api endpoint +- [#5342](https://github.com/blockscout/blockscout/pull/5342) - Fix 500 error on NF token page with nil metadata +- [#5319](https://github.com/blockscout/blockscout/pull/5319), [#5357](https://github.com/blockscout/blockscout/pull/5357), [#5425](https://github.com/blockscout/blockscout/pull/5425) - Empty blocks sanitizer performance improvement +- [#5310](https://github.com/blockscout/blockscout/pull/5310) - Fix flash on reload in dark mode +- [#5306](https://github.com/blockscout/blockscout/pull/5306) - Fix indexer bug +- [#5300](https://github.com/blockscout/blockscout/pull/5300), [#5305](https://github.com/blockscout/blockscout/pull/5305) - Token instance page: general video improvements +- [#5136](https://github.com/blockscout/blockscout/pull/5136) - Improve contract verification +- [#5285](https://github.com/blockscout/blockscout/pull/5285) - Fix verified smart-contract bytecode twins feature +- [#5269](https://github.com/blockscout/blockscout/pull/5269) - Address Page: Fix implementation address align +- [#5264](https://github.com/blockscout/blockscout/pull/5264) - Fix bug with 500 response on `partial` sourcify status +- [#5263](https://github.com/blockscout/blockscout/pull/5263) - Fix bug with name absence for contract +- [#5259](https://github.com/blockscout/blockscout/pull/5259) - Fix `coin-balances/by-day` bug +- [#5239](https://github.com/blockscout/blockscout/pull/5239) - Add accounting for block rewards in `getblockreward` api method + +### Chore + +- [#5506](https://github.com/blockscout/blockscout/pull/5506) - Refactor config files +- [#5480](https://github.com/blockscout/blockscout/pull/5480) - Remove duplicate of balances_params_to_address_params function +- [#5473](https://github.com/blockscout/blockscout/pull/5473) - Refactor daily coin balances fetcher +- [#5458](https://github.com/blockscout/blockscout/pull/5458) - Decrease min safe polling period for realtime fetcher +- [#5456](https://github.com/blockscout/blockscout/pull/5456) - Ignore arbitrary block details fields for custom Ethereum clients +- [#5450](https://github.com/blockscout/blockscout/pull/5450) - Logging error in publishing of smart-contract +- [#5433](https://github.com/blockscout/blockscout/pull/5433) - Caching modules refactoring +- [#5419](https://github.com/blockscout/blockscout/pull/5419) - Add check if address exists for some api methods +- [#5408](https://github.com/blockscout/blockscout/pull/5408) - Update websocket_client hex package +- [#5407](https://github.com/blockscout/blockscout/pull/5407) - Update hackney, certifi, tzdata +- [#5369](https://github.com/blockscout/blockscout/pull/5369) - Manage indexer memory limit +- [#5368](https://github.com/blockscout/blockscout/pull/5368) - Refactoring from SourcifyFilePathBackfiller +- [#5367](https://github.com/blockscout/blockscout/pull/5367) - Resolve Prototype Pollution in minimist dependency +- [#5366](https://github.com/blockscout/blockscout/pull/5366) - Fix Vyper smart-contract verification form tooltips +- [#5348](https://github.com/blockscout/blockscout/pull/5348) - Block data for Avalanche: pass blockExtraData param +- [#5341](https://github.com/blockscout/blockscout/pull/5341) - Remove unused broadcasts +- [#5318](https://github.com/blockscout/blockscout/pull/5318) - Eliminate Jquery import from chart-loader.js +- [#5317](https://github.com/blockscout/blockscout/pull/5317) - NPM audit +- [#5303](https://github.com/blockscout/blockscout/pull/5303) - Besu: revertReason support in trace +- [#5301](https://github.com/blockscout/blockscout/pull/5301) - Allow specific block keys for sgb/ava +- [#5295](https://github.com/blockscout/blockscout/pull/5295) - CI pipeline: build and push Docker image to Docker Hub on every release +- [#5290](https://github.com/blockscout/blockscout/pull/5290) - Bump ex_doc from 0.25.2 to 0.28.2 +- [#5289](https://github.com/blockscout/blockscout/pull/5289) - Bump ex_abi from 1.5.9 to 1.5.11 +- [#5288](https://github.com/blockscout/blockscout/pull/5288) - Makefile: find exact container by name +- [#5287](https://github.com/blockscout/blockscout/pull/5287) - Docker: modify native token symbol +- [#5286](https://github.com/blockscout/blockscout/pull/5286) - Change namespace for one of the SmartContractViewTest test +- [#5260](https://github.com/blockscout/blockscout/pull/5260) - Makefile release task to prerelease and release task +- [#5082](https://github.com/blockscout/blockscout/pull/5082) - Elixir 1.12 -> 1.13 + +## 4.1.2-beta + +### Features + +- [#5232](https://github.com/blockscout/blockscout/pull/5232) - Contract Read Page: Add functions overloading support +- [#5220](https://github.com/blockscout/blockscout/pull/5220) - Add info about proxy contracts to api methods response +- [#5200](https://github.com/blockscout/blockscout/pull/5200) - Docker-compose configuration +- [#5105](https://github.com/blockscout/blockscout/pull/5105) - Redesign token page +- [#5016](https://github.com/blockscout/blockscout/pull/5016) - Add view for internal transactions error +- [#4690](https://github.com/blockscout/blockscout/pull/4690) - Improve pagination: introduce pagination with random access to pages; Integrate it to the Transactions List page + +### Fixes + +- [#5248](https://github.com/blockscout/blockscout/pull/5248) - Speedup query for getting verified smart-contract bytecode twin +- [#5241](https://github.com/blockscout/blockscout/pull/5241) - Fix DB hostname Regex pattern +- [#5216](https://github.com/blockscout/blockscout/pull/5216) - Add token-transfers-toggle.js to the `block_transaction/index.html.eex` +- [#5212](https://github.com/blockscout/blockscout/pull/5212) - Fix `gas_used` value bug +- [#5197](https://github.com/blockscout/blockscout/pull/5197) - Fix contract functions outputs +- [#5196](https://github.com/blockscout/blockscout/pull/5196) - Various Docker setup fixes +- [#5192](https://github.com/blockscout/blockscout/pull/5192) - Fix DATABASE_URL config parser +- [#5191](https://github.com/blockscout/blockscout/pull/5191) - Add empty view for new addresses +- [#5184](https://github.com/blockscout/blockscout/pull/5184) - eth_call method: remove from param from the request, if it is null +- [#5172](https://github.com/blockscout/blockscout/pull/5172), [#5182](https://github.com/blockscout/blockscout/pull/5182) - Reduced the size of js bundles +- [#5169](https://github.com/blockscout/blockscout/pull/5169) - Fix several UI bugs; Add tooltip to the prev/next block buttons +- [#5166](https://github.com/blockscout/blockscout/pull/5166), [#5198](https://github.com/blockscout/blockscout/pull/5198) - Fix contracts verification bugs +- [#5160](https://github.com/blockscout/blockscout/pull/5160) - Fix blocks validated hint +- [#5155](https://github.com/blockscout/blockscout/pull/5155) - Fix get_implementation_abi_from_proxy/2 implementation +- [#5154](https://github.com/blockscout/blockscout/pull/5154) - Fix token counters bug +- [#4862](https://github.com/blockscout/blockscout/pull/4862) - Fix internal transactions pagination + +### Chore + +- [#5230](https://github.com/blockscout/blockscout/pull/5230) - Contract verification forms refactoring +- [#5227](https://github.com/blockscout/blockscout/pull/5227) - Major update of css-loader npm package +- [#5226](https://github.com/blockscout/blockscout/pull/5226) - Update mini-css-extract-plugin, css-minimizer-webpack-plugin packages +- [#5224](https://github.com/blockscout/blockscout/pull/5224) - Webpack config refactoring +- [#5223](https://github.com/blockscout/blockscout/pull/5223) - Migrate fontawesome 5 -> 6 +- [#5202](https://github.com/blockscout/blockscout/pull/5202), [#5229](https://github.com/blockscout/blockscout/pull/5229) - Docker setup Makefile release/publish tasks +- [#5195](https://github.com/blockscout/blockscout/pull/5195) - Add Berlin, London to the list of default EVM versions +- [#5190](https://github.com/blockscout/blockscout/pull/5190) - Set 8545 as default port everywhere except Ganache JSON RPC variant +- [#5189](https://github.com/blockscout/blockscout/pull/5189) - ENV var to manage pending transactions fetcher switching off +- [#5171](https://github.com/blockscout/blockscout/pull/5171) - Replace lodash NPM package with tiny lodash modules +- [#5170](https://github.com/blockscout/blockscout/pull/5170) - Token price row name fix +- [#5153](https://github.com/blockscout/blockscout/pull/5153) - Discord link instead of Gitter +- [#5142](https://github.com/blockscout/blockscout/pull/5142) - Updated some outdated npm packages +- [#5140](https://github.com/blockscout/blockscout/pull/5140) - Babel minor and core-js major updates +- [#5139](https://github.com/blockscout/blockscout/pull/5139) - Eslint major update +- [#5138](https://github.com/blockscout/blockscout/pull/5138) - Webpack minor update +- [#5119](https://github.com/blockscout/blockscout/pull/5119) - Inventory controller refactoring +- [#5118](https://github.com/blockscout/blockscout/pull/5118) - Fix top navigation template + +## 4.1.1-beta + +### Features + +- [#5090](https://github.com/blockscout/blockscout/pull/5090) - Allotted rate limit by IP +- [#5080](https://github.com/blockscout/blockscout/pull/5080) - Allotted rate limit by a global API key + +### Fixes + +- [#5085](https://github.com/blockscout/blockscout/pull/5085) - Fix wallet style +- [#5088](https://github.com/blockscout/blockscout/pull/5088) - Store address transactions/token transfers in the DB +- [#5071](https://github.com/blockscout/blockscout/pull/5071) - Fix write page contract tuple input +- [#5066](https://github.com/blockscout/blockscout/pull/5066) - Fix read contract page bug +- [#5034](https://github.com/blockscout/blockscout/pull/5034) - Fix broken functions input at transaction page +- [#5025](https://github.com/blockscout/blockscout/pull/5025) - Add standard input JSON files validation +- [#5051](https://github.com/blockscout/blockscout/pull/5051) - Fix 500 response when ABI method was parsed as nil + +### Chore + +- [#5092](https://github.com/blockscout/blockscout/pull/5092) - Resolve vulnerable follow-redirects npm dep in ./apps/explorer +- [#5091](https://github.com/blockscout/blockscout/pull/5091) - Refactor search page template +- [#5081](https://github.com/blockscout/blockscout/pull/5081) - Add internal transactions fetcher disabled? config parameter +- [#5063](https://github.com/blockscout/blockscout/pull/5063) - Resolve moderate NPM vulnerabilities with npm audit tool +- [#5053](https://github.com/blockscout/blockscout/pull/5053) - Update ex_keccak lib + +## 4.1.0-beta + +### Features + +- [#5030](https://github.com/blockscout/blockscout/pull/5030) - API rate limiting +- [#4924](https://github.com/blockscout/blockscout/pull/4924) - Add daily bytecode verifcation to prevent metamorphic contracts vulnerablity +- [#4908](https://github.com/blockscout/blockscout/pull/4908) - Add verification via standard JSON input +- [#5004](https://github.com/blockscout/blockscout/pull/5004) - Add ability to set up a separate DB endpoint for the API endpoints +- [#4989](https://github.com/blockscout/blockscout/pull/4989), [#4991](https://github.com/blockscout/blockscout/pull/4991) - Bridged tokens list API endpoint +- [#4931](https://github.com/blockscout/blockscout/pull/4931) - Web3 modal with Wallet Connect for Write contract page and Staking Dapp + +### Fixes + +- [#5045](https://github.com/blockscout/blockscout/pull/5045) - Contracts interaction improvements +- [#5032](https://github.com/blockscout/blockscout/pull/5032) - Fix token transfer csv export +- [#5020](https://github.com/blockscout/blockscout/pull/5020) - Token instance image display imrovement +- [#5019](https://github.com/blockscout/blockscout/pull/5019) - Fix fetch_last_token_balance function termination +- [#5011](https://github.com/blockscout/blockscout/pull/5011) - Fix `0x0` implementation address +- [#5008](https://github.com/blockscout/blockscout/pull/5008) - Extend decimals cap in format_according_to_decimals up to 24 +- [#5005](https://github.com/blockscout/blockscout/pull/5005) - Fix falsy appearance `Connection Lost` warning on reload/switch page +- [#5003](https://github.com/blockscout/blockscout/pull/5003) - API router refactoring +- [#4992](https://github.com/blockscout/blockscout/pull/4992) - Fix `type` field in transactions after enabling 1559 +- [#4979](https://github.com/blockscout/blockscout/pull/4979), [#4993](https://github.com/blockscout/blockscout/pull/4993) - Store total gas_used in addresses table +- [#4977](https://github.com/blockscout/blockscout/pull/4977) - Export token transfers on address: include transfers on contract itself +- [#4976](https://github.com/blockscout/blockscout/pull/4976) - Handle :econnrefused in pending transactions fetcher +- [#4965](https://github.com/blockscout/blockscout/pull/4965) - Fix search field appearance on medium size screens +- [#4945](https://github.com/blockscout/blockscout/pull/4945) - Fix `Verify & Publish` button link +- [#4938](https://github.com/blockscout/blockscout/pull/4938) - Fix displaying of nested arrays for contracts read +- [#4888](https://github.com/blockscout/blockscout/pull/4888) - Fix fetch_top_tokens method: add nulls last for token holders desc order +- [#4867](https://github.com/blockscout/blockscout/pull/4867) - Fix bug in quering contracts method and improve contracts interactions + +### Chore + +- [#5047](https://github.com/blockscout/blockscout/pull/5047) - At contract write use wei precision +- [#5023](https://github.com/blockscout/blockscout/pull/5023) - Capability to leave an empty logo +- [#5018](https://github.com/blockscout/blockscout/pull/5018) - Resolve npm vulnerabilities via npm audix fix +- [#5014](https://github.com/blockscout/blockscout/pull/5014) - Separate FIRST_BLOCK and TRACE_FIRST_BLOCK option for blocks import and tracing methods +- [#4998](https://github.com/blockscout/blockscout/pull/4998) - API endpoints logger +- [#4983](https://github.com/blockscout/blockscout/pull/4983), [#5038](https://github.com/blockscout/blockscout/pull/5038) - Fix contract verification tests +- [#4861](https://github.com/blockscout/blockscout/pull/4861) - Add separate column for token icons + +## 4.0.0-beta + +### Features + +- [#4807](https://github.com/blockscout/blockscout/pull/4807) - Added support for BeaconProxy pattern +- [#4777](https://github.com/blockscout/blockscout/pull/4777), [#4791](https://github.com/blockscout/blockscout/pull/4791), [#4799](https://github.com/blockscout/blockscout/pull/4799), [#4847](https://github.com/blockscout/blockscout/pull/4847) - Added decoding revert reason +- [#4776](https://github.com/blockscout/blockscout/pull/4776) - Added view for unsuccessfully fetched values from read functions +- [#4761](https://github.com/blockscout/blockscout/pull/4761) - ERC-1155 support +- [#4739](https://github.com/blockscout/blockscout/pull/4739) - Improve logs and inputs decoding +- [#4747](https://github.com/blockscout/blockscout/pull/4747) - Advanced CSV export +- [#4745](https://github.com/blockscout/blockscout/pull/4745) - Vyper contracts verification +- [#4699](https://github.com/blockscout/blockscout/pull/4699), [#4793](https://github.com/blockscout/blockscout/pull/4793), [#4820](https://github.com/blockscout/blockscout/pull/4820), [#4827](https://github.com/blockscout/blockscout/pull/4827) - Address page facelifting +- [#4667](https://github.com/blockscout/blockscout/pull/4667) - Transaction Page: Add expand/collapse button for long contract method data +- [#4641](https://github.com/blockscout/blockscout/pull/4641), [#4733](https://github.com/blockscout/blockscout/pull/4733) - Improve Read Contract page logic +- [#4660](https://github.com/blockscout/blockscout/pull/4660) - Save Sourcify path instead of filename +- [#4656](https://github.com/blockscout/blockscout/pull/4656) - Open in Tenderly button +- [#4655](https://github.com/blockscout/blockscout/pull/4655), [#4676](https://github.com/blockscout/blockscout/pull/4676) - EIP-3091 support +- [#4621](https://github.com/blockscout/blockscout/pull/4621) - Add beacon contract address slot for proxy +- [#4625](https://github.com/blockscout/blockscout/pull/4625) - Contract address page: Add implementation link to the overview of proxy contracts +- [#4624](https://github.com/blockscout/blockscout/pull/4624) - Support HTML tags in alert message +- [#4608](https://github.com/blockscout/blockscout/pull/4608), [#4622](https://github.com/blockscout/blockscout/pull/4622) - Block Details page: Improved style of transactions button +- [#4596](https://github.com/blockscout/blockscout/pull/4596), [#4681](https://github.com/blockscout/blockscout/pull/4681), [#4693](https://github.com/blockscout/blockscout/pull/4693) - Display token icon for bridged with Mainnet tokens or identicons for other tokens +- [#4520](https://github.com/blockscout/blockscout/pull/4520) - Add support for EIP-1559 +- [#4593](https://github.com/blockscout/blockscout/pull/4593) - Add status in `Position` pane for txs have no block +- [#4579](https://github.com/blockscout/blockscout/pull/4579) - Write contract page: Resize inputs; Improve multiplier selector + +### Fixes + +- [#4857](https://github.com/blockscout/blockscout/pull/4857) - Fix `tx/raw-trace` Internal Server Error +- [#4854](https://github.com/blockscout/blockscout/pull/4854) - Fix infinite gas usage count loading +- [#4853](https://github.com/blockscout/blockscout/pull/4853) - Allow custom optimizations runs for contract verifications via API +- [#4840](https://github.com/blockscout/blockscout/pull/4840) - Replace Enum.dedup with Enum.uniq where actually uniq items are expected +- [#4835](https://github.com/blockscout/blockscout/pull/4835) - Fix view for broken token icons +- [#4830](https://github.com/blockscout/blockscout/pull/4830) - Speed up txs per day chart data collection +- [#4818](https://github.com/blockscout/blockscout/pull/4818) - Fix for extract_omni_bridged_token_metadata_wrapper method +- [#4812](https://github.com/blockscout/blockscout/pull/4812), [#4815](https://github.com/blockscout/blockscout/pull/4815) - Check if exists custom_cap property of extended token object before access it +- [#4810](https://github.com/blockscout/blockscout/pull/4810) - Show `nil` block.size as `N/A bytes` +- [#4806](https://github.com/blockscout/blockscout/pull/4806) - Get token type for token balance update if it is empty +- [#4802](https://github.com/blockscout/blockscout/pull/4802) - Fix floating tooltip on the main page +- [#4801](https://github.com/blockscout/blockscout/pull/4801) - Added clauses and tests for get_total_staked_and_ordered/1 +- [#4798](https://github.com/blockscout/blockscout/pull/4798) - Token instance View contract icon Safari fix +- [#4796](https://github.com/blockscout/blockscout/pull/4796) - Fix nil.timestamp issue +- [#4764](https://github.com/blockscout/blockscout/pull/4764) - Add cleaning of substrings of `require` messages from parsed constructor arguments +- [#4778](https://github.com/blockscout/blockscout/pull/4778) - Migrate :optimization_runs field type: `int4 -> int8` in `smart_contracts` table +- [#4768](https://github.com/blockscout/blockscout/pull/4768) - Block Details page: handle zero division +- [#4751](https://github.com/blockscout/blockscout/pull/4751) - Change text and link for `trade STAKE` button +- [#4746](https://github.com/blockscout/blockscout/pull/4746) - Fix comparison of decimal value +- [#4711](https://github.com/blockscout/blockscout/pull/4711) - Add trimming to the contract functions inputs +- [#4729](https://github.com/blockscout/blockscout/pull/4729) - Fix bugs with fees in cases of txs with `gas price = 0` +- [#4725](https://github.com/blockscout/blockscout/pull/4725) - Fix hardcoded coin name on transaction's and block's page +- [#4724](https://github.com/blockscout/blockscout/pull/4724), [#4842](https://github.com/blockscout/blockscout/pull/4841) - Sanitizer of "empty" blocks +- [#4717](https://github.com/blockscout/blockscout/pull/4717) - Contract verification fix: check only success creation tx +- [#4713](https://github.com/blockscout/blockscout/pull/4713) - Search input field: sanitize input +- [#4703](https://github.com/blockscout/blockscout/pull/4703) - Block Details page: Fix pagination on the Transactions tab +- [#4686](https://github.com/blockscout/blockscout/pull/4686) - Block page: check gas limit value before division +- [#4678](https://github.com/blockscout/blockscout/pull/4678) - Internal transactions indexer: fix issue of some pending transactions never become confirmed +- [#4668](https://github.com/blockscout/blockscout/pull/4668) - Fix css for dark theme +- [#4654](https://github.com/blockscout/blockscout/pull/4654) - AddressView: Change `@burn_address` to string `0x0000000000000000000000000000000000000000` +- [#4626](https://github.com/blockscout/blockscout/pull/4626) - Refine view of popup for reverted tx +- [#4640](https://github.com/blockscout/blockscout/pull/4640) - Token page: fixes in mobile view +- [#4612](https://github.com/blockscout/blockscout/pull/4612) - Hide error selector in the contract's functions list +- [#4615](https://github.com/blockscout/blockscout/pull/4615) - Fix broken style for `View more transfers` button +- [#4592](https://github.com/blockscout/blockscout/pull/4592) - Add `type` field for `receive` and `fallback` entities of a Smart Contract +- [#4601](https://github.com/blockscout/blockscout/pull/4601) - Fix endless Fetching tokens... message on empty addresses +- [#4591](https://github.com/blockscout/blockscout/pull/4591) - Add step and min value for txValue input field +- [#4589](https://github.com/blockscout/blockscout/pull/4589) - Fix solid outputs on contract read page +- [#4586](https://github.com/blockscout/blockscout/pull/4586) - Fix floating tooltips on the token transfer family blocks +- [#4587](https://github.com/blockscout/blockscout/pull/4587) - Enable navbar menu on Search results page +- [#4582](https://github.com/blockscout/blockscout/pull/4582) - Fix NaN input on write contract page + +### Chore + +- [#4876](https://github.com/blockscout/blockscout/pull/4876) - Add missing columns updates when INSERT ... ON CONFLICT DO UPDATE ... happens +- [#4872](https://github.com/blockscout/blockscout/pull/4872) - Set explicit ascending order by hash in acquire transactions query of internal transactions import +- [#4871](https://github.com/blockscout/blockscout/pull/4871) - Remove cumulative gas used update duplicate +- [#4860](https://github.com/blockscout/blockscout/pull/4860) - Node 16 support +- [#4828](https://github.com/blockscout/blockscout/pull/4828) - Logging for txs/day chart +- [#4823](https://github.com/blockscout/blockscout/pull/4823) - Various error handlers with unresponsive JSON RPC endpoint +- [#4821](https://github.com/blockscout/blockscout/pull/4821) - Block Details page: Remove crossing at the Burnt Fee line +- [#4819](https://github.com/blockscout/blockscout/pull/4819) - Add config for GasUsage Cache +- [#4781](https://github.com/blockscout/blockscout/pull/4781) - PGAnalyze index suggestions +- [#4735](https://github.com/blockscout/blockscout/pull/4735) - Code clean up: Remove clauses for outdated ganache bugs +- [#4726](https://github.com/blockscout/blockscout/pull/4726) - Update chart.js +- [#4707](https://github.com/blockscout/blockscout/pull/4707) - Top navigation: Move Accounts tab to Tokens +- [#4704](https://github.com/blockscout/blockscout/pull/4704) - Update to Erlang/OTP 24 +- [#4682](https://github.com/blockscout/blockscout/pull/4682) - Update all possible outdated mix dependencies +- [#4663](https://github.com/blockscout/blockscout/pull/4663) - Migrate to Elixir 1.12.x +- [#4661](https://github.com/blockscout/blockscout/pull/4661) - Update NPM packages to resolve vulnerabilities +- [#4649](https://github.com/blockscout/blockscout/pull/4649) - 1559 Transaction Page: Convert Burnt Fee to ether and add price in USD +- [#4646](https://github.com/blockscout/blockscout/pull/4646) - Transaction page: Rename burned to burnt +- [#4611](https://github.com/blockscout/blockscout/pull/4611) - Ability to hide miner in block views + +## 3.7.3-beta + +### Features + +- [#4569](https://github.com/blockscout/blockscout/pull/4569) - Smart-Contract: remove comment with the submission date +- [#4568](https://github.com/blockscout/blockscout/pull/4568) - TX page: Token transfer and minting section improvements +- [#4540](https://github.com/blockscout/blockscout/pull/4540) - Allign copy buttons for `Block Details` and `Transaction Details` pages +- [#4528](https://github.com/blockscout/blockscout/pull/4528) - Block Details page: rework view +- [#4531](https://github.com/blockscout/blockscout/pull/4531) - Add Arbitrum support +- [#4524](https://github.com/blockscout/blockscout/pull/4524) - Add index position of transaction in the block +- [#4489](https://github.com/blockscout/blockscout/pull/4489) - Search results page +- [#4475](https://github.com/blockscout/blockscout/pull/4475) - Tx page facelifting +- [#4452](https://github.com/blockscout/blockscout/pull/4452) - Add names for smart-conrtact's function response + +### Fixes + +- [#4553](https://github.com/blockscout/blockscout/pull/4553) - Indexer performance update: skip genesis block in requesting of trace_block API endpoint +- [#4544](https://github.com/blockscout/blockscout/pull/4544) - Indexer performance update: Add skip_metadata flag for token if indexer failed to get any of [name, symbol, decimals, totalSupply] +- [#4542](https://github.com/blockscout/blockscout/pull/4542) - Indexer performance update: Deduplicate tokens in the indexer token transfers transformer +- [#4535](https://github.com/blockscout/blockscout/pull/4535) - Indexer performance update:: Eliminate multiple updates of the same token while parsing mint/burn token transfers batch +- [#4527](https://github.com/blockscout/blockscout/pull/4527) - Indexer performance update: refactor coin balance daily fetcher +- [#4525](https://github.com/blockscout/blockscout/pull/4525) - Uncataloged token transfers query performance improvement +- [#4513](https://github.com/blockscout/blockscout/pull/4513) - Fix installation with custom default path: add NETWORK_PATH variable to the current_path +- [#4500](https://github.com/blockscout/blockscout/pull/4500) - `/tokens/{addressHash}/instance/{id}/token-transfers`: fix incorrect next page url +- [#4493](https://github.com/blockscout/blockscout/pull/4493) - Contract's code page: handle null contracts_creation_transaction +- [#4488](https://github.com/blockscout/blockscout/pull/4488) - Tx page: handle empty to_address +- [#4483](https://github.com/blockscout/blockscout/pull/4483) - Fix copy-paste typo in `token_transfers_counter.ex` +- [#4473](https://github.com/blockscout/blockscout/pull/4473), [#4481](https://github.com/blockscout/blockscout/pull/4481) - Search autocomplete: fix for address/block/tx hash +- [#4472](https://github.com/blockscout/blockscout/pull/4472) - Search autocomplete: fix Cannot read property toLowerCase of undefined +- [#4456](https://github.com/blockscout/blockscout/pull/4456) - URL encoding for NFT media files URLs +- [#4453](https://github.com/blockscout/blockscout/pull/4453) - Unescape characters for string output type in the contract response +- [#4401](https://github.com/blockscout/blockscout/pull/4401) - Fix displaying of token holders with the same amount + +### Chore + +- [#4550](https://github.com/blockscout/blockscout/pull/4550) - Update con_cache package to 1.0 +- [#4523](https://github.com/blockscout/blockscout/pull/4523) - Change order of transations in block's view +- [#4521](https://github.com/blockscout/blockscout/pull/4521) - Rewrite transaction page tooltips +- [#4516](https://github.com/blockscout/blockscout/pull/4516) - Add DB migrations step into Docker start script +- [#4497](https://github.com/blockscout/blockscout/pull/4497) - Handle error in fetch_validators_list method +- [#4444](https://github.com/blockscout/blockscout/pull/4444) - Main page performance cumulative update +- [#4439](https://github.com/blockscout/blockscout/pull/4439), - [#4465](https://github.com/blockscout/blockscout/pull/4465) - Fix revert response in contract's output + +## 3.7.2-beta + +### Features + +- [#4424](https://github.com/blockscout/blockscout/pull/4424) - Display search results categories +- [#4423](https://github.com/blockscout/blockscout/pull/4423) - Add creation time of contract in the results of the search +- [#4391](https://github.com/blockscout/blockscout/pull/4391) - Add batched transactions on the `address/{addressHash}/transactions` page +- [#4353](https://github.com/blockscout/blockscout/pull/4353) - Added live-reload on the token holders page + +### Fixes + +- [#4437](https://github.com/blockscout/blockscout/pull/4437) - Fix `PendingTransactionsSanitizer` for non-consensus blocks +- [#4430](https://github.com/blockscout/blockscout/pull/4430) - Fix current token balance on-demand fetcher +- [#4429](https://github.com/blockscout/blockscout/pull/4429), [#4431](https://github.com/blockscout/blockscout/pull/4431) - Fix 500 response on `/tokens/{addressHash}/token-holders?type=JSON` when total supply is zero +- [#4419](https://github.com/blockscout/blockscout/pull/4419) - Order contracts in the search by inserted_at in descending order +- [#4418](https://github.com/blockscout/blockscout/pull/4418) - Fix empty search results for the full-word search criteria +- [#4406](https://github.com/blockscout/blockscout/pull/4406) - Fix internal server error on the validator's txs page +- [#4360](https://github.com/blockscout/blockscout/pull/4360) - Fix false-pending transactions in reorg blocks +- [#4388](https://github.com/blockscout/blockscout/pull/4388) - Fix internal server error on contract page for insctances without sourcify envs +- [#4385](https://github.com/blockscout/blockscout/pull/4385) - Fix html template for transaction's input; Add copy text for tuples + +### Chore + +- [#4400](https://github.com/blockscout/blockscout/pull/4400) - Add "Token ID" label onto `tokens/.../instance/.../token-transfers` page +- [#4398](https://github.com/blockscout/blockscout/pull/4398) - Speed up the transactions loading on the front-end +- [#4384](https://github.com/blockscout/blockscout/pull/4384) - Fix Elixir version in `.tool-versions` +- [#4382](https://github.com/blockscout/blockscout/pull/4382) - Replace awesomplete with autocomplete.js +- [#4371](https://github.com/blockscout/blockscout/pull/4371) - Place search outside of burger in mobile view +- [#4355](https://github.com/blockscout/blockscout/pull/4355) - Do not redirect to 404 page with empty string in the search field + +## 3.7.1-beta + +### Features + +- [#4331](https://github.com/blockscout/blockscout/pull/4331) - Added support for partially verified contracts via [Sourcify](https://sourcify.dev) +- [#4323](https://github.com/blockscout/blockscout/pull/4323) - Renamed Contract Byte Code, add Contract Creation Code on contract's page +- [#4312](https://github.com/blockscout/blockscout/pull/4312) - Display pending transactions on address page +- [#4299](https://github.com/blockscout/blockscout/pull/4299) - Added [Sourcify](https://sourcify.dev) verification API endpoint +- [#4267](https://github.com/blockscout/blockscout/pull/4267) - Extend verification through [Sourcify](https://sourcify.dev) smart-contract verification: fetch smart contract metadata from Sourcify repo if it has been already verified there +- [#4241](https://github.com/blockscout/blockscout/pull/4241) - Reload transactions on the main page without reloading of the whole page +- [#4218](https://github.com/blockscout/blockscout/pull/4218) - Hide long arrays in smart-contracts +- [#4205](https://github.com/blockscout/blockscout/pull/4205) - Total transactions fees per day API endpoint +- [#4158](https://github.com/blockscout/blockscout/pull/4158) - Calculate total fee per day +- [#4067](https://github.com/blockscout/blockscout/pull/4067) - Display LP tokens USD value and custom metadata in tokens dropdown at address page + +### Fixes + +- [#4351](https://github.com/blockscout/blockscout/pull/4351) - Support effectiveGasPrice property in tx receipt (Geth specific) +- [#4346](https://github.com/blockscout/blockscout/pull/4346) - Fix internal server error on raw-trace transaction page +- [#4345](https://github.com/blockscout/blockscout/pull/4345) - Fix bug on validator's address transactions page(Support effectiveGasPrice property in receipt (geth specific)) +- [#4342](https://github.com/blockscout/blockscout/pull/4342) - Remove dropped/replaced txs from address transactions page +- [#4320](https://github.com/blockscout/blockscout/pull/4320) - Fix absence of imported smart-contracts' source code in `getsourcecode` API method +- [#4274](https://github.com/blockscout/blockscout/pull/4302) - Fix search token-autocomplete +- [#4316](https://github.com/blockscout/blockscout/pull/4316) - Fix `/decompiled-contracts` bug +- [#4310](https://github.com/blockscout/blockscout/pull/4310) - Fix logo URL redirection, set font-family defaults for chart.js +- [#4308](https://github.com/blockscout/blockscout/pull/4308) - Fix internal server error on contract verification options page +- [#4307](https://github.com/blockscout/blockscout/pull/4307) - Fix for composing IPFS URLs for NFTs images +- [#4306](https://github.com/blockscout/blockscout/pull/4306) - Check token instance images MIME types +- [#4295](https://github.com/blockscout/blockscout/pull/4295) - Mobile view fix: transaction tile tx hash overflow +- [#4294](https://github.com/blockscout/blockscout/pull/4294) - User wont be able to open verification pages for verified smart-contract +- [#4240](https://github.com/blockscout/blockscout/pull/4240) - `[]` is accepted in write contract page +- [#4236](https://github.com/blockscout/blockscout/pull/4236), [#4242](https://github.com/blockscout/blockscout/pull/4242) - Fix typo, constructor instead of contructor +- [#4167](https://github.com/blockscout/blockscout/pull/4167) - Deduplicate block numbers in acquire_blocks function +- [#4149](https://github.com/blockscout/blockscout/pull/4149) - Exclude smart_contract_additional_sources from JSON encoding in address schema +- [#4137](https://github.com/blockscout/blockscout/pull/4137) - Get token balance query improvement +- [#4129](https://github.com/blockscout/blockscout/pull/4129) - Speedup procedure of finding missing block numbers for catchup fetcher +- [#4038](https://github.com/blockscout/blockscout/pull/4038) - Add clause for abi_decode_address_output/1 when is_nil(address) +- [#3989](https://github.com/blockscout/blockscout/pull/3989), [4061](https://github.com/blockscout/blockscout/pull/4061) - Fixed bug that sometimes lead to incorrect ordering of token transfers +- [#3946](https://github.com/blockscout/blockscout/pull/3946) - Get NFT metadata from URIs with status_code 301 +- [#3888](https://github.com/blockscout/blockscout/pull/3888) - EIP-1967 contract proxy pattern detection fix + +### Chore + +- [#4315](https://github.com/blockscout/blockscout/pull/4315) - Replace node_modules/ with ~ in app.scss +- [#4314](https://github.com/blockscout/blockscout/pull/4314) - Set infinite timeout for fetch_min_missing_block_cache method DB query +- [#4300](https://github.com/blockscout/blockscout/pull/4300) - Remove clear_build.sh script +- [#4268](https://github.com/blockscout/blockscout/pull/4268) - Migration to Chart.js 3.0 +- [#4253](https://github.com/blockscout/blockscout/pull/4253) - Elixir 1.11.4, npm audit fix +- [#4231](https://github.com/blockscout/blockscout/pull/4231) - Transactions stats: get min/max blocks in one query +- [#4157](https://github.com/blockscout/blockscout/pull/4157) - Fix internal docs generation +- [#4127](https://github.com/blockscout/blockscout/pull/4127) - Update ex_keccak package +- [#4063](https://github.com/blockscout/blockscout/pull/4063) - Do not display 4bytes signature in the tx tile for contract creation +- [#3934](https://github.com/blockscout/blockscout/pull/3934) - Update nimble_csv package +- [#3902](https://github.com/blockscout/blockscout/pull/3902) - Increase number of left symbols in short address view +- [#3894](https://github.com/blockscout/blockscout/pull/3894) - Refactoring: replace inline style display: none with d-none class +- [#3893](https://github.com/blockscout/blockscout/pull/3893) - Add left/right paddings in tx tile +- [#3870](https://github.com/blockscout/blockscout/pull/3870) - Manage token balance on-demand fetcher threshold via env var + +## 3.7.0-beta + +### Features + +- [#3858](https://github.com/blockscout/blockscout/pull/3858) - Integration with Sourcify +- [#3834](https://github.com/blockscout/blockscout/pull/3834) - Method name in tx tile +- [#3792](https://github.com/blockscout/blockscout/pull/3792) - Cancel pending transaction +- [#3786](https://github.com/blockscout/blockscout/pull/3786) - Read contract: enable methods with StateMutability: pure +- [#3758](https://github.com/blockscout/blockscout/pull/3758) - Add pool metadata display/change to Staking DApp +- [#3750](https://github.com/blockscout/blockscout/pull/3750) - getblocknobytime block module API endpoint + +### Fixes + +- [#3835](https://github.com/blockscout/blockscout/pull/3835) - Fix getTokenHolders API endpoint pagination +- [#3787](https://github.com/blockscout/blockscout/pull/3787) - Improve tokens list elements display +- [#3785](https://github.com/blockscout/blockscout/pull/3785) - Fix for write contract functionality: false and 0 boolean inputs are parsed as true +- [#3783](https://github.com/blockscout/blockscout/pull/3783) - Fix number of block confirmations +- [#3773](https://github.com/blockscout/blockscout/pull/3773) - Inventory pagination query performance improvement +- [#3767](https://github.com/blockscout/blockscout/pull/3767) - Decoded contract method input tuple reader fix +- [#3748](https://github.com/blockscout/blockscout/pull/3748) - Skip null topics in eth_getLogs API endpoint + +### Chore + +- [#3831](https://github.com/blockscout/blockscout/pull/3831) - Process type field in eth_getTransactionReceipt response +- [#3802](https://github.com/blockscout/blockscout/pull/3802) - Extend Become a Candidate popup in Staking DApp +- [#3801](https://github.com/blockscout/blockscout/pull/3801) - Poison package update +- [#3799](https://github.com/blockscout/blockscout/pull/3799) - Update credo, dialyxir mix packages +- [#3789](https://github.com/blockscout/blockscout/pull/3789) - Update repo organization +- [#3788](https://github.com/blockscout/blockscout/pull/3788) - Update fontawesome NPM package + +## 3.6.0-beta + +### Features + +- [#3743](https://github.com/blockscout/blockscout/pull/3743) - Minimal proxy pattern support (EIP-1167) +- [#3722](https://github.com/blockscout/blockscout/pull/3722) - Allow double quotes for (u)int arrays inputs during contract interaction +- [#3694](https://github.com/blockscout/blockscout/pull/3694) - LP tokens total liquidity +- [#3676](https://github.com/blockscout/blockscout/pull/3676) - Bridged tokens TLV in USD +- [#3674](https://github.com/blockscout/blockscout/pull/3674) - Display Sushiswap pools data +- [#3637](https://github.com/blockscout/blockscout/pull/3637) - getsourcecode API endpoint: show data for unverified contract from verified contract with the same bytecode +- [#3631](https://github.com/blockscout/blockscout/pull/3631) - Tokens search +- [#3631](https://github.com/blockscout/blockscout/pull/3631) - BSC OMNI bridge support +- [#3603](https://github.com/blockscout/blockscout/pull/3603) - Display method output parameter name at contract read page +- [#3597](https://github.com/blockscout/blockscout/pull/3597) - Show APY for delegators in Staking DApp +- [#3584](https://github.com/blockscout/blockscout/pull/3584) - Token holders API endpoint +- [#3564](https://github.com/blockscout/blockscout/pull/3564) - Staking welcome message + +### Fixes + +- [#3742](https://github.com/blockscout/blockscout/pull/3742) - Fix Sushiswap LP tokens custom metadata fetcher: bytes(n) symbol and name support +- [#3741](https://github.com/blockscout/blockscout/pull/3741) - Contract reader fix when there are multiple input params including an array type +- [#3735](https://github.com/blockscout/blockscout/pull/3735) - Token balance on demand fetcher memory leak fix +- [#3732](https://github.com/blockscout/blockscout/pull/3732) - POSDAO: fix snapshotting and remove temporary code +- [#3731](https://github.com/blockscout/blockscout/pull/3731) - Handle bad gateway at pending transactions fetcher +- [#3730](https://github.com/blockscout/blockscout/pull/3730) - Set default period for average block time counter refresh interval +- [#3729](https://github.com/blockscout/blockscout/pull/3729) - Token on-demand balance fetcher: handle nil balance +- [#3728](https://github.com/blockscout/blockscout/pull/3728) - Coinprice api endpoint: handle nil rates +- [#3723](https://github.com/blockscout/blockscout/pull/3723) - Fix losing digits at value conversion back from WEI +- [#3715](https://github.com/blockscout/blockscout/pull/3715) - Pending transactions sanitizer process +- [#3710](https://github.com/blockscout/blockscout/pull/3710) - Missing @destination in bridged-tokens template +- [#3707](https://github.com/blockscout/blockscout/pull/3707) - Fetch bridged token price by address of foreign token, not by symbol +- [#3686](https://github.com/blockscout/blockscout/pull/3686) - BSC bridged tokens detection fix +- [#3683](https://github.com/blockscout/blockscout/pull/3683) - Token instance image IPFS link display fix +- [#3655](https://github.com/blockscout/blockscout/pull/3655) - Handle absence of readAll function in some old/legacy browsers +- [#3634](https://github.com/blockscout/blockscout/pull/3634) - Fix transaction decoding view: support tuple types +- [#3623](https://github.com/blockscout/blockscout/pull/3623) - Ignore unrecognized messages in bridge counter processes +- [#3622](https://github.com/blockscout/blockscout/pull/3622) - Contract reader: fix int type output Ignore unrecognized messages in bridge counter processes +- [#3621](https://github.com/blockscout/blockscout/pull/3621) - Contract reader: :binary input/output fix +- [#3620](https://github.com/blockscout/blockscout/pull/3620) - Ignore unfamiliar messages by Explorer.Staking.ContractState module +- [#3611](https://github.com/blockscout/blockscout/pull/3611) - Fix logo size +- [#3600](https://github.com/blockscout/blockscout/pull/3600) - Prevent update validator metadata with empty name from contract +- [#3592](https://github.com/blockscout/blockscout/pull/3592), [#3601](https://github.com/blockscout/blockscout/pull/3601), [#3607](https://github.com/blockscout/blockscout/pull/3607) - Contract interaction: fix nested tuples in the output view, add formatting +- [#3583](https://github.com/blockscout/blockscout/pull/3583) - Reduce RPC requests and DB changes by Staking DApp +- [#3577](https://github.com/blockscout/blockscout/pull/3577) - Eliminate GraphiQL page XSS attack + +### Chore + +- [#3745](https://github.com/blockscout/blockscout/pull/3745) - Refactor and optimize Staking DApp +- [#3744](https://github.com/blockscout/blockscout/pull/3744) - Update Mix packages: timex, hackney, tzdata certifi +- [#3736](https://github.com/blockscout/blockscout/pull/3736), [#3739](https://github.com/blockscout/blockscout/pull/3739) - Contract writer: Fix sending a transaction with tuple input type +- [#3719](https://github.com/blockscout/blockscout/pull/3719) - Rename ethprice API endpoint +- [#3717](https://github.com/blockscout/blockscout/pull/3717) - Update alpine-elixir-phoenix 1.11.3 +- [#3714](https://github.com/blockscout/blockscout/pull/3714) - Application announcements management: whole explorer, staking dapp +- [#3712](https://github.com/blockscout/blockscout/pull/3712) - POSDAO refactoring: use pool ID instead of staking address +- [#3709](https://github.com/blockscout/blockscout/pull/3709) - Fix 413 Request Entity Too Large returned from single request batch +- [#3708](https://github.com/blockscout/blockscout/pull/3708) - NPM 6 -> 7 +- [#3701](https://github.com/blockscout/blockscout/pull/3701) - Increase LP tokens calc process re-check interval +- [#3700](https://github.com/blockscout/blockscout/pull/3700) - Update tool versions +- [#3697](https://github.com/blockscout/blockscout/pull/3697) - Update hackney dependency +- [#3696](https://github.com/blockscout/blockscout/pull/3696) - Table loader fix +- [#3688](https://github.com/blockscout/blockscout/pull/3688) - Reorganize staking buttons +- [#3687](https://github.com/blockscout/blockscout/pull/3687) - Miscellaneous minor fixes +- [#3667](https://github.com/blockscout/blockscout/pull/3667) - Store bridged token price in the DB +- [#3662](https://github.com/blockscout/blockscout/pull/3662) - Order bridged tokens in descending order by tokens holder for Omni bridge cap calculation +- [#3659](https://github.com/blockscout/blockscout/pull/3659) - Staking Dapp new buttons: swap, bridge +- [#3645](https://github.com/blockscout/blockscout/pull/3645) - Change Twitter handle +- [#3644](https://github.com/blockscout/blockscout/pull/3644) - Correct exchange rate for SURF.finance token +- [#3618](https://github.com/blockscout/blockscout/pull/3618) - Contracts verification up to 10 libraries +- [#3616](https://github.com/blockscout/blockscout/pull/3616) - POSDAO refactoring: use zero address instead of staker address for certain cases +- [#3612](https://github.com/blockscout/blockscout/pull/3612) - POSDAO refactoring: use 'getDelegatorPools' getter instead of 'getStakerPools' in Staking DApp +- [#3585](https://github.com/blockscout/blockscout/pull/3585) - Add autoswitching from eth_subscribe to eth_blockNumber in Staking DApp +- [#3574](https://github.com/blockscout/blockscout/pull/3574) - Correct UNI token price +- [#3569](https://github.com/blockscout/blockscout/pull/3569) - Allow re-define cache period vars at runtime +- [#3567](https://github.com/blockscout/blockscout/pull/3567) - Force to show filter at the page where filtered items list is empty +- [#3565](https://github.com/blockscout/blockscout/pull/3565) - Staking dapp: unhealthy state alert message + +## 3.5.1-beta + +### Features + +- [#3558](https://github.com/blockscout/blockscout/pull/3558) - Focus to search field with a forward slash key +- [#3541](https://github.com/blockscout/blockscout/pull/3541) - Staking dapp stats: total number of delegators, total staked amount +- [#3540](https://github.com/blockscout/blockscout/pull/3540) - Apply DarkForest custom theme to NFT instances + +### Fixes + +- [#3551](https://github.com/blockscout/blockscout/pull/3551) - Fix contract's method's output of tuple type + +### Chore + +- [#3557](https://github.com/blockscout/blockscout/pull/3557) - Single Staking menu +- [#3540](https://github.com/blockscout/blockscout/pull/3540), [#3545](https://github.com/blockscout/blockscout/pull/3545) - Support different versions of DarkForest (0.4 - 0.5) + +## 3.5.0-beta + +### Features + +- [#3536](https://github.com/blockscout/blockscout/pull/3536) - Revert reason in the result of contract's method call +- [#3532](https://github.com/blockscout/blockscout/pull/3532) - Contract interaction: an easy setting of precision for integer input +- [#3531](https://github.com/blockscout/blockscout/pull/3531) - Allow double quotes in input data of contract methods +- [#3515](https://github.com/blockscout/blockscout/pull/3515) - CRC total balance +- [#3513](https://github.com/blockscout/blockscout/pull/3513) - Allow square brackets for an array input data in contracts interaction +- [#3480](https://github.com/blockscout/blockscout/pull/3480) - Add support of Autonity client +- [#3470](https://github.com/blockscout/blockscout/pull/3470) - Display sum of tokens' USD value at tokens holder's address page +- [#3462](https://github.com/blockscout/blockscout/pull/3462) - Display price for bridged tokens + +### Fixes + +- [#3535](https://github.com/blockscout/blockscout/pull/3535) - Improve speed of tokens dropdown loading at owner address page +- [#3530](https://github.com/blockscout/blockscout/pull/3530) - Allow trailing/leading whitespaces for inputs for contract read methods +- [#3526](https://github.com/blockscout/blockscout/pull/3526) - Order staking pools +- [#3525](https://github.com/blockscout/blockscout/pull/3525), [#3533](https://github.com/blockscout/blockscout/pull/3533) - Address token balance on demand fetcher +- [#3514](https://github.com/blockscout/blockscout/pull/3514) - Read contract: fix internal server error +- [#3513](https://github.com/blockscout/blockscout/pull/3513) - Fix input data processing for method call (array type of data) +- [#3509](https://github.com/blockscout/blockscout/pull/3509) - Fix QR code tooltip appearance in mobile view +- [#3507](https://github.com/blockscout/blockscout/pull/3507), [#3510](https://github.com/blockscout/blockscout/pull/3510) - Fix left margin of balance card in mobile view +- [#3506](https://github.com/blockscout/blockscout/pull/3506) - Fix token transfer's tile styles: prevent overlapping of long names +- [#3505](https://github.com/blockscout/blockscout/pull/3505) - Fix Staking DApp first loading +- [#3433](https://github.com/blockscout/blockscout/pull/3433) - Token balances and rewards tables deadlocks elimination +- [#3494](https://github.com/blockscout/blockscout/pull/3494), [#3497](https://github.com/blockscout/blockscout/pull/3497), [#3504](https://github.com/blockscout/blockscout/pull/3504), [#3517](https://github.com/blockscout/blockscout/pull/3517) - Contracts interaction: fix method call with array[] inputs +- [#3494](https://github.com/blockscout/blockscout/pull/3494), [#3495](https://github.com/blockscout/blockscout/pull/3495) - Contracts interaction: fix tuple output display +- [#3479](https://github.com/blockscout/blockscout/pull/3479) - Fix working with big numbers in Staking DApp +- [#3477](https://github.com/blockscout/blockscout/pull/3477) - Contracts interaction: fix broken call of GnosisProxy contract methods with parameters +- [#3477](https://github.com/blockscout/blockscout/pull/3477) - Contracts interaction: fix broken call of fallback function +- [#3476](https://github.com/blockscout/blockscout/pull/3476) - Fix contract verification of precompiled contracts +- [#3467](https://github.com/blockscout/blockscout/pull/3467) - Fix Firefox styles +- [#3464](https://github.com/blockscout/blockscout/pull/3464) - Fix display of token transfers list at token page (fix unique identifier of a tile) + +- [#3457](https://github.com/blockscout/blockscout/pull/3457) - Fix endless block invalidation issue +- [#3457](https://github.com/blockscout/blockscout/pull/3457) - Fix doubled total transferred/minted/burnt tokens on transaction's page if block has reorg +- [#3457](https://github.com/blockscout/blockscout/pull/3457) - Fix doubled token transfer on block's page if block has reorg + +### Chore + +- [#3500](https://github.com/blockscout/blockscout/pull/3500) - Update solc version in explorer folder +- [#3498](https://github.com/blockscout/blockscout/pull/3498) - Make Staking DApp work with transferAndCall function +- [#3496](https://github.com/blockscout/blockscout/pull/3496) - Rollback websocket_client module to 1.3.0 +- [#3489](https://github.com/blockscout/blockscout/pull/3489) - Migrate to Webpack@5 +- [#3487](https://github.com/blockscout/blockscout/pull/3487) - Docker setup update to be compatible with Erlang OTP 23 +- [#3484](https://github.com/blockscout/blockscout/pull/3484) - Elixir upgrade to 11.2 +- [#3483](https://github.com/blockscout/blockscout/pull/3483) - Update outdated dependencies +- [#3483](https://github.com/blockscout/blockscout/pull/3483) - Migrate to Erlang/OTP 23 +- [#3468](https://github.com/blockscout/blockscout/pull/3468) - Do not check supported networks on application loading page +- [#3467](https://github.com/blockscout/blockscout/pull/3467) - NodeJS engine upgrade up to 14 +- [#3460](https://github.com/blockscout/blockscout/pull/3460) - Update Staking DApp scripts due to MetaMask breaking changes + +## 3.4.0-beta + +### Features + +- [#3442](https://github.com/blockscout/blockscout/pull/3442) - Constructor arguments autodetection in API verify endpoint +- [#3435](https://github.com/blockscout/blockscout/pull/3435) - Token transfers counter cache +- [#3420](https://github.com/blockscout/blockscout/pull/3420) - Enable read/write proxy tabs for Gnosis safe proxy contract +- [#3411](https://github.com/blockscout/blockscout/pull/3411) - Circles UBI theme +- [#3406](https://github.com/blockscout/blockscout/pull/3406), [#3409](https://github.com/blockscout/blockscout/pull/3409) - Adding mp4 files support for NFTs +- [#3398](https://github.com/blockscout/blockscout/pull/3398) - Collect and display gas usage per day at the main page +- [#3385](https://github.com/blockscout/blockscout/pull/3385), [#3397](https://github.com/blockscout/blockscout/pull/3397) - Total gas usage at the main page +- [#3384](https://github.com/blockscout/blockscout/pull/3384), [#3386](https://github.com/blockscout/blockscout/pull/3386) - Address total gas usage +- [#3377](https://github.com/blockscout/blockscout/pull/3377) - Add links to contract libraries +- [#2292](https://github.com/blockscout/blockscout/pull/2292), [#3356](https://github.com/blockscout/blockscout/pull/3356), [#3359](https://github.com/blockscout/blockscout/pull/3359), [#3360](https://github.com/blockscout/blockscout/pull/3360), [#3365](https://github.com/blockscout/blockscout/pull/3365) - Add Web UI for POSDAO Staking DApp +- [#3354](https://github.com/blockscout/blockscout/pull/3354) - Tx hash in EOA coin balance history +- [#3333](https://github.com/blockscout/blockscout/pull/3333), [#3337](https://github.com/blockscout/blockscout/pull/3337), [#3393](https://github.com/blockscout/blockscout/pull/3393) - Dark forest contract custom theme +- [#3330](https://github.com/blockscout/blockscout/pull/3330) - Caching of address transactions counter, remove query 10_000 rows limit + +### Fixes + +- [#3449](https://github.com/blockscout/blockscout/pull/3449) - Correct avg time calculation +- [#3443](https://github.com/blockscout/blockscout/pull/3443) - Improve blocks handling in Staking DApp +- [#3440](https://github.com/blockscout/blockscout/pull/3440) - Rewrite missing blocks range query +- [#3439](https://github.com/blockscout/blockscout/pull/3439) - Dark mode color fixes (search, charts) +- [#3437](https://github.com/blockscout/blockscout/pull/3437) - Fix Postgres Docker container +- [#3428](https://github.com/blockscout/blockscout/pull/3428) - Fix address tokens search +- [#3424](https://github.com/blockscout/blockscout/pull/3424) - Fix display of long NFT IDs +- [#3422](https://github.com/blockscout/blockscout/pull/3422) - Fix contract reader: tuple type +- [#3408](https://github.com/blockscout/blockscout/pull/3408) - Fix (total) difficulty display +- [#3401](https://github.com/blockscout/blockscout/pull/3401), [#3432](https://github.com/blockscout/blockscout/pull/3432) - Fix procedure of marking internal transactions as failed +- [#3400](https://github.com/blockscout/blockscout/pull/3400) - Add :last_block_number realtime chain event +- [#3399](https://github.com/blockscout/blockscout/pull/3399) - Fix Token transfers CSV export +- [#3396](https://github.com/blockscout/blockscout/pull/3396) - Handle exchange rates request throttled +- [#3382](https://github.com/blockscout/blockscout/pull/3382) - Check ets table exists for known tokens +- [#3376](https://github.com/blockscout/blockscout/pull/3376) - Fix contract nested inputs +- [#3375](https://github.com/blockscout/blockscout/pull/3375) - Prevent terminating of tokens/contracts process +- [#3374](https://github.com/blockscout/blockscout/pull/3374) - Fix find block timestamp query +- [#3373](https://github.com/blockscout/blockscout/pull/3373) - Fix horizontal scroll in Tokens table +- [#3370](https://github.com/blockscout/blockscout/pull/3370) - Improve contracts verification: refine constructor arguments extractor +- [#3368](https://github.com/blockscout/blockscout/pull/3368) - Fix Verify contract loading button width +- [#3357](https://github.com/blockscout/blockscout/pull/3357) - Fix token transfer realtime fetcher +- [#3353](https://github.com/blockscout/blockscout/pull/3353) - Fix xDai buttons hover color +- [#3352](https://github.com/blockscout/blockscout/pull/3352) - Fix dark body background +- [#3350](https://github.com/blockscout/blockscout/pull/3350) - Fix tokens list pagination +- [#3347](https://github.com/blockscout/blockscout/pull/3347) - Contract interaction: fix encoding of bytes output +- [#3346](https://github.com/blockscout/blockscout/pull/3346), [#3351](https://github.com/blockscout/blockscout/pull/3351) - Fix inventory tab pagination +- [#3344](https://github.com/blockscout/blockscout/pull/3344) - Fix logs search on address page +- [#3342](https://github.com/blockscout/blockscout/pull/3342) - Fix mobile styles for contract code tab +- [#3341](https://github.com/blockscout/blockscout/pull/3341) - Change Solc binary downloader path to official primary supported path +- [#3339](https://github.com/blockscout/blockscout/pull/3339) - Repair websocket subscription +- [#3329](https://github.com/blockscout/blockscout/pull/3329) - Fix pagination for bridged tokens list page +- [#3335](https://github.com/blockscout/blockscout/pull/3335) - MarketCap calculation: check that ETS tables exist before inserting new data or lookup from the table + +### Chore + +- [#5240](https://github.com/blockscout/blockscout/pull/5240) - Managing invalidation of address coin balance cache +- [#3450](https://github.com/blockscout/blockscout/pull/3450) - Replace window.web3 with window.ethereum +- [#3446](https://github.com/blockscout/blockscout/pull/3446), [#3448](https://github.com/blockscout/blockscout/pull/3448) - Set infinity timeout and increase cache invalidation period for counters +- [#3431](https://github.com/blockscout/blockscout/pull/3431) - Standardize token name definition, if name is empty +- [#3421](https://github.com/blockscout/blockscout/pull/3421) - Functions to enable GnosisSafe app link +- [#3414](https://github.com/blockscout/blockscout/pull/3414) - Manage lis of other explorers in the footer via env var +- [#3407](https://github.com/blockscout/blockscout/pull/3407) - Add EthereumJSONRPC.HTTP.HTTPoison.json_rpc function clause when URL is null +- [#3405](https://github.com/blockscout/blockscout/pull/3405) - N/A instead of 0 for market cap if it is not fetched +- [#3404](https://github.com/blockscout/blockscout/pull/3404) - DISABLE_KNOWN_TOKENS env var +- [#3403](https://github.com/blockscout/blockscout/pull/3403) - Refactor Coingecko interaction +- [#3394](https://github.com/blockscout/blockscout/pull/3394) - Actualize docker vars list +- [#3372](https://github.com/blockscout/blockscout/pull/3372), [#3380](https://github.com/blockscout/blockscout/pull/3380) - Improve all lists header container +- [#3371](https://github.com/blockscout/blockscout/pull/3371) - Eliminate dark background except Dark forest theme +- [#3366](https://github.com/blockscout/blockscout/pull/3366) - Stabilize tests execution in Github Actions CI +- [#3343](https://github.com/blockscout/blockscout/pull/3343) - Make (Bridged) Tokens' list page's header more compact + +## 3.3.3-beta + +### Features + +- [#3320](https://github.com/blockscout/blockscout/pull/3320) - Bridged tokens from AMB extensions support +- [#3311](https://github.com/blockscout/blockscout/pull/3311) - List of addresses with restricted access option +- [#3293](https://github.com/blockscout/blockscout/pull/3293) - Composite market cap for xDai: TokenBridge + OmniBridge +- [#3282](https://github.com/blockscout/blockscout/pull/3282), [#3318](https://github.com/blockscout/blockscout/pull/3318) - Import bridged tokens custom metadata +- [#3281](https://github.com/blockscout/blockscout/pull/3281) - Write contract: display currently connected address +- [#3279](https://github.com/blockscout/blockscout/pull/3279) - NFT instance: link to the app +- [#3278](https://github.com/blockscout/blockscout/pull/3278) - Support of fetching of NFT metadata from IPFS +- [#3273](https://github.com/blockscout/blockscout/pull/3273) - Update token metadata at burn/mint events +- [#3268](https://github.com/blockscout/blockscout/pull/3268) - Token total supply on-demand fetcher +- [#3261](https://github.com/blockscout/blockscout/pull/3261) - Bridged tokens table + +### Fixes + +- [#3323](https://github.com/blockscout/blockscout/pull/3323) - Fix logs list API endpoint response +- [#3319](https://github.com/blockscout/blockscout/pull/3319) - Eliminate horizontal scroll +- [#3314](https://github.com/blockscout/blockscout/pull/3314) - Handle nil values from response of CoinGecko price API +- [#3313](https://github.com/blockscout/blockscout/pull/3313) - Fix xDai styles: invisible tokens on address +- [#3312](https://github.com/blockscout/blockscout/pull/3312) - Replace symbol for some tokens to be able to find price in CoinGecko for OmniBridge balance +- [#3307](https://github.com/blockscout/blockscout/pull/3307) - Replace "latest" compiler version with the actual one +- [#3303](https://github.com/blockscout/blockscout/pull/3303) - Address contract twins feature performance +- [#3295](https://github.com/blockscout/blockscout/pull/3295) - Token instance: check if external_url is not null before trimming +- [#3291](https://github.com/blockscout/blockscout/pull/3291) - Support unlimited number of external rewards in block +- [#3290](https://github.com/blockscout/blockscout/pull/3290) - Eliminate protocol Jason.Encoder not implemented for... error +- [#3284](https://github.com/blockscout/blockscout/pull/3284) - Fix fetch_coin_balance query: coin balance delta +- [#3276](https://github.com/blockscout/blockscout/pull/3276) - Bridged tokens status/metadata fetcher refactoring +- [#3264](https://github.com/blockscout/blockscout/pull/3264) - Fix encoding of address output if function input exists +- [#3259](https://github.com/blockscout/blockscout/pull/3259), [#3269](https://github.com/blockscout/blockscout/pull/3269) - Contract interaction: array input type parsing fix +- [#3257](https://github.com/blockscout/blockscout/pull/3257) - Contracts read/write: method_id instead function_name as a key +- [#3256](https://github.com/blockscout/blockscout/pull/3256) - Fix for invisible validator address at block page and wrong alert text color at xDai + +### Chore + +- [#3327](https://github.com/blockscout/blockscout/pull/3327) - Handle various indexer fetchers errors in setup with non-archive node +- [#3325](https://github.com/blockscout/blockscout/pull/3325) - Dark theme improvements +- [#3316](https://github.com/blockscout/blockscout/pull/3316), [#3317](https://github.com/blockscout/blockscout/pull/3317) - xDai smile logo +- [#3315](https://github.com/blockscout/blockscout/pull/3315) - Environment variable to disable Bridge market cap updater +- [#3308](https://github.com/blockscout/blockscout/pull/3308) - Fixate latest stable release of Elixir, Node, Postgres +- [#3297](https://github.com/blockscout/blockscout/pull/3297) - Actualize names of default chains +- [#3285](https://github.com/blockscout/blockscout/pull/3285) - Switch to RPC endpoint polling if ETHEREUM_JSONRPC_WS_URL is an empty string +- [#3274](https://github.com/blockscout/blockscout/pull/3274) - Replace underscore with hyphen in routes +- [#3260](https://github.com/blockscout/blockscout/pull/3260) - Update NPM dependencies to fix known vulnerabilities +- [#3258](https://github.com/blockscout/blockscout/pull/3258) - Token transfer: check that block exists before retrieving timestamp + +## 3.3.2-beta + +### Features + +- [#3252](https://github.com/blockscout/blockscout/pull/3252) - Gas price at the main page +- [#3239](https://github.com/blockscout/blockscout/pull/3239) - Hide address page tabs if no items +- [#3236](https://github.com/blockscout/blockscout/pull/3236) - Easy verification of contracts which has verified twins (the same bytecode) +- [#3227](https://github.com/blockscout/blockscout/pull/3227) - Distinguishing of bridged tokens +- [#3224](https://github.com/blockscout/blockscout/pull/3224) - Top tokens page + +### Fixes + +- [#3249](https://github.com/blockscout/blockscout/pull/3249) - Fix incorrect ABI decoding of address in tuple output +- [#3237](https://github.com/blockscout/blockscout/pull/3237) - Refine contract method signature detection for read/write feature +- [#3235](https://github.com/blockscout/blockscout/pull/3235) - Fix coin supply api edpoint +- [#3233](https://github.com/blockscout/blockscout/pull/3233) - Fix for the contract verifiaction for solc 0.5 family with experimental features enabled +- [#3231](https://github.com/blockscout/blockscout/pull/3231) - Improve search: unlimited number of searching results +- [#3231](https://github.com/blockscout/blockscout/pull/3231) - Improve search: allow search with space +- [#3231](https://github.com/blockscout/blockscout/pull/3231) - Improve search: order by token holders in descending order and token/contract name is ascending order +- [#3226](https://github.com/blockscout/blockscout/pull/3226) - Fix notifier query for live update of token transfers +- [#3220](https://github.com/blockscout/blockscout/pull/3220) - Allow interaction with navbar menu at block-not-found page + +### Chore + +- [#3326](https://github.com/blockscout/blockscout/pull/3326) - Chart smooth lines +- [#3250](https://github.com/blockscout/blockscout/pull/3250) - Eliminate occurrences of obsolete env variable ETHEREUM_JSONRPC_JSON_RPC_TRANSPORT +- [#3240](https://github.com/blockscout/blockscout/pull/3240), [#3251](https://github.com/blockscout/blockscout/pull/3251) - various CSS imroving +- [f3a720](https://github.com/blockscout/blockscout/commit/2dd909c10a79b0bf4b7541a486be114152f3a720) - Make wobserver optional + +## 3.3.1-beta + +### Features + +- [#3216](https://github.com/blockscout/blockscout/pull/3216) - Display new token transfers at token page and address page without refreshing the page +- [#3199](https://github.com/blockscout/blockscout/pull/3199) - Show compilation error at contract verification +- [#3193](https://github.com/blockscout/blockscout/pull/3193) - Raw trace copy button +- [#3184](https://github.com/blockscout/blockscout/pull/3184) - Apps navbar menu item +- [#3145](https://github.com/blockscout/blockscout/pull/3145) - Pending txs per address API endpoint + +### Fixes + +- [#3219](https://github.com/blockscout/blockscout/pull/3219) - Fix revert reason message detection +- [#3215](https://github.com/blockscout/blockscout/pull/3215) - Coveralls in CI through Github Actions +- [#3214](https://github.com/blockscout/blockscout/pull/3214) - Fix current token balances fetcher +- [#3143](https://github.com/blockscout/blockscout/pull/3143) - Fix "Connection lost..." error at address page +- [#3209](https://github.com/blockscout/blockscout/pull/3209) - GraphQL: fix internal server error at request of internal transactions at address +- [#3207](https://github.com/blockscout/blockscout/pull/3207) - Fix read contract bytes array type output +- [#3203](https://github.com/blockscout/blockscout/pull/3203) - Improve "get mined blocks" query performance +- [#3202](https://github.com/blockscout/blockscout/pull/3202) - Fix contracts verification with experimental features enabled +- [#3201](https://github.com/blockscout/blockscout/pull/3201) - Connect to Metamask button +- [#3192](https://github.com/blockscout/blockscout/pull/3192) - Dropdown menu doesn't open at "not found" page +- [#3190](https://github.com/blockscout/blockscout/pull/3190) - Contract log/method decoded view improvements: eliminate horizontal scroll, remove excess borders, whitespaces +- [#3185](https://github.com/blockscout/blockscout/pull/3185) - Transaction page: decoding logs from nested contracts calls +- [#3182](https://github.com/blockscout/blockscout/pull/3182) - Besu: support revertReason key in eth_getTransactionReceipt endpoint +- [#3178](https://github.com/blockscout/blockscout/pull/3178) - Fix permanent fetching tokens... when read/write proxy tab is active +- [#3178](https://github.com/blockscout/blockscout/pull/3178) - Fix unavailable navbar menu when read/write proxy tab is active + +### Chore + +- [#3212](https://github.com/blockscout/blockscout/pull/3212) - GitHub actions CI config +- [#3210](https://github.com/blockscout/blockscout/pull/3210) - Update Phoenix up to 1.4.17 +- [#3206](https://github.com/blockscout/blockscout/pull/3206) - Update Elixir version: 1.10.2 -> 1.10.3 +- [#3204](https://github.com/blockscout/blockscout/pull/3204) - GraphQL Absinthe related packages update up to stable versions +- [#3180](https://github.com/blockscout/blockscout/pull/3180) - Return correct status in verify API endpoint if contract verified +- [#3180](https://github.com/blockscout/blockscout/pull/3180) - Remove Kovan from the list of default chains + +## 3.3.0-beta + +### Features + +- [#3174](https://github.com/blockscout/blockscout/pull/3174) - EIP-1967 support: transparent proxy pattern +- [#3173](https://github.com/blockscout/blockscout/pull/3173) - Display implementation address at read/write proxy tabs +- [#3171](https://github.com/blockscout/blockscout/pull/3171) - Import accounts/contracts/balances from Geth genesis.json +- [#3161](https://github.com/blockscout/blockscout/pull/3161) - Write proxy contracts feature +- [#3160](https://github.com/blockscout/blockscout/pull/3160) - Write contracts feature +- [#3157](https://github.com/blockscout/blockscout/pull/3157) - Read methods of implementation on proxy contract + +### Fixes + +- [#3168](https://github.com/blockscout/blockscout/pull/3168) - Eliminate internal server error at /accounts page with token-bridge type of supply and inexistent bridge contracts +- [#3169](https://github.com/blockscout/blockscout/pull/3169) - Fix for verification of contracts defined in genesis block + +### Chore + +## 3.2.0-beta + +### Features + +- [#3154](https://github.com/blockscout/blockscout/pull/3154) - Support of Hyperledger Besu client +- [#3153](https://github.com/blockscout/blockscout/pull/3153) - Proxy contracts: logs decoding using implementation ABI +- [#3153](https://github.com/blockscout/blockscout/pull/3153) - Proxy contracts: methods decoding using implementation ABI +- [#3149](https://github.com/blockscout/blockscout/pull/3149) - Display and store revert reason of tx on demand at transaction details page and at gettxinfo API endpoint. + +### Fixes + +### Chore + +- [#3152](https://github.com/blockscout/blockscout/pull/3152) - Fix contract compilation tests for old versions of compiler + +## 3.1.3-beta + +### Features + +- [#3125](https://github.com/blockscout/blockscout/pull/3125) - Availability to configure a number of days to consider at coin balance history chart via environment variable + +### Fixes + +- [#3146](https://github.com/blockscout/blockscout/pull/3146) - Fix coin balance history page: order of items, fix if no balance changes +- [#3142](https://github.com/blockscout/blockscout/pull/3142) - Speed-up last coin balance timestamp query (coin balance history page performance improvement) +- [#3140](https://github.com/blockscout/blockscout/pull/3140) - Fix performance of the balance changing history list loading +- [#3133](https://github.com/blockscout/blockscout/pull/3133) - Take into account FIRST_BLOCK in trace_ReplayBlockTransactions requests +- [#3132](https://github.com/blockscout/blockscout/pull/3132) - Fix performance of coin supply API endpoints +- [#3130](https://github.com/blockscout/blockscout/pull/3130) - Take into account FIRST_BLOCK for block rewards fetching +- [#3128](https://github.com/blockscout/blockscout/pull/3128) - Token instance metadata retriever refinement: add processing of token metadata if only image URL is passed to token URI +- [#3126](https://github.com/blockscout/blockscout/pull/3126) - Fetch balance only for blocks which are greater or equal block with FIRST_BLOCK number +- [#3125](https://github.com/blockscout/blockscout/pull/3125) - Fix performance of coin balance history chart +- [#3122](https://github.com/blockscout/blockscout/pull/3122) - Exclude balance percentage calculation for burn address on accounts page +- [#3121](https://github.com/blockscout/blockscout/pull/3121) - Geth: handle response from eth_getblockbyhash JSON RPC method without totalDifficulty (uncle blocks) +- [#3119](https://github.com/blockscout/blockscout/pull/3119), [#3120](https://github.com/blockscout/blockscout/pull/3120) - Fix performance of Inventory tab loading for ERC-721 tokens +- [#3114](https://github.com/blockscout/blockscout/pull/3114) - Fix performance of "Blocks validated" page +- [#3112](https://github.com/blockscout/blockscout/pull/3112) - Fix verification of contracts, compiled with nightly builds of solc compiler +- [#3112](https://github.com/blockscout/blockscout/pull/3112) - Check compiler version at contract verification +- [#3106](https://github.com/blockscout/blockscout/pull/3106) - Fix verification of contracts with `immutable` declaration +- [#3106](https://github.com/blockscout/blockscout/pull/3106), [#3115](https://github.com/blockscout/blockscout/pull/3115) - Fix verification of contracts, created from factory (from internal transaction) + +### Chore + +- [#3137](https://github.com/blockscout/blockscout/pull/3137) - RSK Papyrus Release v2.0.1 hardfork: cumulativeDifficulty +- [#3134](https://github.com/blockscout/blockscout/pull/3134) - Get last value of fetched coinsupply API endpoint from DB if cache is empty +- [#3124](https://github.com/blockscout/blockscout/pull/3124) - Display upper border for tx speed if the value cannot be calculated + +## 3.1.2-beta + +### Features + +- [#3089](https://github.com/blockscout/blockscout/pull/3089) - CoinGecko API coin id environment variable +- [#3069](https://github.com/blockscout/blockscout/pull/3069) - Make a link to address page on decoded constructor argument of address type +- [#3067](https://github.com/blockscout/blockscout/pull/3067) - Show proper title of the tile or container for token burnings/mintings instead of "Token Transfer" +- [#3066](https://github.com/blockscout/blockscout/pull/3066) - ERC-721 token instance page: link to token added +- [#3065](https://github.com/blockscout/blockscout/pull/3065) - Transactions history chart + +### Fixes + +- [#3097](https://github.com/blockscout/blockscout/pull/3097) - Fix contract reader decoding +- [#3095](https://github.com/blockscout/blockscout/pull/3095) - Fix constructor arguments decoding +- [#3092](https://github.com/blockscout/blockscout/pull/3092) - Contract verification: constructor arguments search search refinement +- [#3077](https://github.com/blockscout/blockscout/pull/3077) - Finally speedup pending tx list +- [#3076](https://github.com/blockscout/blockscout/pull/3076) - Speedup tx list query on address page: check if an address has a reward, check if this is actual payout key of the validator - beneficiary, return only mined txs in tx list query +- [#3071](https://github.com/blockscout/blockscout/pull/3071) - Speedup list of token transfers per token query +- [#3070](https://github.com/blockscout/blockscout/pull/3070) - Index creation to blazingly speedup token holders query +- [#3064](https://github.com/blockscout/blockscout/pull/3064) - Automatically define Block reward contract address in TokenBridge supply module +- [#3061](https://github.com/blockscout/blockscout/pull/3061) - Fix verification of contracts with error messages in require in parent contract +- [#2756](https://github.com/blockscout/blockscout/pull/2756) - Improve subquery joins + +### Chore + +- [#3100](https://github.com/blockscout/blockscout/pull/3100) - Update npm packages +- [#3099](https://github.com/blockscout/blockscout/pull/3099) - Remove pending txs cache +- [#3093](https://github.com/blockscout/blockscout/pull/3093) - Extend list of env vars for Docker setup +- [#3084](https://github.com/blockscout/blockscout/pull/3084) - Bump Elixir version 1.10.2 +- [#3079](https://github.com/blockscout/blockscout/pull/3079) - Extend optionality of websockets to Geth + +## 3.1.1-beta + +### Features + +- [#3058](https://github.com/blockscout/blockscout/pull/3058) - Searching by verified contract name + +### Fixes + +- [#3053](https://github.com/blockscout/blockscout/pull/3053) - Fix ABI decoding in contracts methods, logs (migrate to ex_abi 0.3.0) +- [#3044](https://github.com/blockscout/blockscout/pull/3044) - Prevent division by zero on /accounts page +- [#3043](https://github.com/blockscout/blockscout/pull/3043) - Extract host name for split couple of indexer and web app +- [#3042](https://github.com/blockscout/blockscout/pull/3042) - Speedup pending txs list query +- [#2944](https://github.com/blockscout/blockscout/pull/2944), [#3046](https://github.com/blockscout/blockscout/pull/3046) - Split js logic into multiple files + +## 3.1.0-beta + +### Features + +- [#3013](https://github.com/blockscout/blockscout/pull/3013), [#3026](https://github.com/blockscout/blockscout/pull/3026), [#3031](https://github.com/blockscout/blockscout/pull/3031) - Raw trace of transaction on-demand +- [#3000](https://github.com/blockscout/blockscout/pull/3000) - Get rid of storing of first trace for all types of transactions for Parity variant +- [#2875](https://github.com/blockscout/blockscout/pull/2875) - Save contract code from Parity genesis file +- [#2834](https://github.com/blockscout/blockscout/pull/2834), [#3009](https://github.com/blockscout/blockscout/pull/3009), [#3014](https://github.com/blockscout/blockscout/pull/3014), [#3033](https://github.com/blockscout/blockscout/pull/3033) - always redirect to checksummed hash + +### Fixes + +- [#3037](https://github.com/blockscout/blockscout/pull/3037) - Make buttons color at verification page consistent +- [#3034](https://github.com/blockscout/blockscout/pull/3034) - Support stateMutability=view to define reading functions in smart-contracts +- [#3029](https://github.com/blockscout/blockscout/pull/3029) - Fix transactions and blocks appearance on the main page +- [#3028](https://github.com/blockscout/blockscout/pull/3028) - Decrease polling period value for realtime fetcher +- [#3027](https://github.com/blockscout/blockscout/pull/3027) - Rescue for SUPPORTED_CHAINS env var parsing +- [#3025](https://github.com/blockscout/blockscout/pull/3025) - Fix splitting of indexer/web components setup +- [#3024](https://github.com/blockscout/blockscout/pull/3024) - Fix pool size default value in config +- [#3021](https://github.com/blockscout/blockscout/pull/3021), [#3022](https://github.com/blockscout/blockscout/pull/3022) - Refine dev/test config +- [#3016](https://github.com/blockscout/blockscout/pull/3016), [#3017](https://github.com/blockscout/blockscout/pull/3017) - Fix token instance QR code data +- [#3012](https://github.com/blockscout/blockscout/pull/3012) - Speedup token transfers list query +- [#3011](https://github.com/blockscout/blockscout/pull/3011) - Revert realtime fetcher small skips feature +- [#3007](https://github.com/blockscout/blockscout/pull/3007) - Fix copy UTF8 tx input action +- [#2996](https://github.com/blockscout/blockscout/pull/2996) - Fix awesomplete lib loading in Firefox +- [#2993](https://github.com/blockscout/blockscout/pull/2993) - Fix path definition for contract verification endpoint +- [#2990](https://github.com/blockscout/blockscout/pull/2990) - Fix import of Parity spec file +- [#2989](https://github.com/blockscout/blockscout/pull/2989) - Introduce API_PATH env var +- [#2988](https://github.com/blockscout/blockscout/pull/2988) - Fix web manifest accessibility +- [#2967](https://github.com/blockscout/blockscout/pull/2967) - Fix styles loading for firefox +- [#2950](https://github.com/blockscout/blockscout/pull/2950) - Add `creationMethod` to `EthereumJSONRPC.Parity.Trace.Action.entry_to_elixir` +- [#2897](https://github.com/blockscout/blockscout/pull/2897) - remove duplicate indexes +- [#2883](https://github.com/blockscout/blockscout/pull/2883) - Fix long contracts names + +### Chore + +- [#3032](https://github.com/blockscout/blockscout/pull/3032) - Remove indexing status alert for Ganache variant +- [#3030](https://github.com/blockscout/blockscout/pull/3030) - Remove default websockets URL from config +- [#2995](https://github.com/blockscout/blockscout/pull/2995) - Support API_PATH env var in Docker file + +## 3.0.0-beta + +### Features + +- [#2835](https://github.com/blockscout/blockscout/pull/2835), [#2871](https://github.com/blockscout/blockscout/pull/2871), [#2872](https://github.com/blockscout/blockscout/pull/2872), [#2886](https://github.com/blockscout/blockscout/pull/2886), [#2925](https://github.com/blockscout/blockscout/pull/2925), [#2936](https://github.com/blockscout/blockscout/pull/2936), [#2949](https://github.com/blockscout/blockscout/pull/2949), [#2940](https://github.com/blockscout/blockscout/pull/2940), [#2958](https://github.com/blockscout/blockscout/pull/2958) - Add "block_hash" to logs, token_transfers and internal transactions and "pending blocks operations" approach +- [#2975](https://github.com/blockscout/blockscout/pull/2975) - Refine UX of contracts verification +- [#2926](https://github.com/blockscout/blockscout/pull/2926) - API endpoint: sum balances except burnt address +- [#2918](https://github.com/blockscout/blockscout/pull/2918) - Add tokenID for tokentx API action explicitly + +### Fixes + +- [#2969](https://github.com/blockscout/blockscout/pull/2969) - Fix contract constructor require msg appearance in constructor arguments encoded view +- [#2964](https://github.com/blockscout/blockscout/pull/2964) - Fix bug in skipping of constructor arguments in contract verification +- [#2961](https://github.com/blockscout/blockscout/pull/2961) - Add a guard that addresses is enum in `values` function in `read contract` page +- [#2960](https://github.com/blockscout/blockscout/pull/2960) - Add BLOCKSCOUT_HOST to docker setup +- [#2956](https://github.com/blockscout/blockscout/pull/2956) - Add support of 0.6.x version of compiler +- [#2955](https://github.com/blockscout/blockscout/pull/2955) - Move socket path to env +- [#2938](https://github.com/blockscout/blockscout/pull/2938) - utf8 copy tx input tooltip +- [#2934](https://github.com/blockscout/blockscout/pull/2934) - RSK release 1.2.0 breaking changes support +- [#2933](https://github.com/blockscout/blockscout/pull/2933) - Get rid of deadlock in the query to address_current_token_balance table +- [#2932](https://github.com/blockscout/blockscout/pull/2932) - fix duplicate websocket connection +- [#2928](https://github.com/blockscout/blockscout/pull/2928) - Speedup pending block ops int txs to fetch query +- [#2924](https://github.com/blockscout/blockscout/pull/2924) - Speedup address to logs query +- [#2915](https://github.com/blockscout/blockscout/pull/2915) - Speedup of blocks_without_reward_query +- [#2914](https://github.com/blockscout/blockscout/pull/2914) - Reduce execution time of stream_unfetched_token_instances query +- [#2910](https://github.com/blockscout/blockscout/pull/2910) - Reorganize queries and indexes for internal_transactions table +- [#2908](https://github.com/blockscout/blockscout/pull/2908) - Fix performance of address page +- [#2906](https://github.com/blockscout/blockscout/pull/2906) - fix address sum cache +- [#2902](https://github.com/blockscout/blockscout/pull/2902) - Offset in blocks retrieval for average block time +- [#2900](https://github.com/blockscout/blockscout/pull/2900) - check fetched instance metadata in multiple places +- [#2899](https://github.com/blockscout/blockscout/pull/2899) - fix empty buffered task +- [#2887](https://github.com/blockscout/blockscout/pull/2887) - increase chart loading speed + +### Chore + +- [#2959](https://github.com/blockscout/blockscout/pull/2959) - Remove logs from test folder too in the cleaning script +- [#2954](https://github.com/blockscout/blockscout/pull/2954) - Upgrade absinthe and ecto deps +- [#2947](https://github.com/blockscout/blockscout/pull/2947) - Upgrade Circle CI postgres Docker image +- [#2946](https://github.com/blockscout/blockscout/pull/2946) - Fix vulnerable NPM deps +- [#2942](https://github.com/blockscout/blockscout/pull/2942) - Actualize Docker setup +- [#2896](https://github.com/blockscout/blockscout/pull/2896) - Disable Parity websockets tests +- [#2873](https://github.com/blockscout/blockscout/pull/2873) - bump elixir to 1.9.4 + +## 2.1.1-beta + +### Features + +- [#2862](https://github.com/blockscout/blockscout/pull/2862) - Coin total supply from DB API endpoint +- [#2857](https://github.com/blockscout/blockscout/pull/2857) - Extend getsourcecode API view with new output fields +- [#2822](https://github.com/blockscout/blockscout/pull/2822) - Estimated address count on the main page, if cache is empty +- [#2821](https://github.com/blockscout/blockscout/pull/2821) - add autodetection of constructor arguments +- [#2825](https://github.com/blockscout/blockscout/pull/2825) - separate token transfers and transactions +- [#2787](https://github.com/blockscout/blockscout/pull/2787) - async fetching of address counters +- [#2791](https://github.com/blockscout/blockscout/pull/2791) - add ipc client +- [#2449](https://github.com/blockscout/blockscout/pull/2449) - add ability to send notification events through postgres notify + +### Fixes + +- [#2864](https://github.com/blockscout/blockscout/pull/2864) - add token instance metadata type check +- [#2855](https://github.com/blockscout/blockscout/pull/2855) - Fix favicons load +- [#2854](https://github.com/blockscout/blockscout/pull/2854) - Fix all npm vulnerabilities +- [#2851](https://github.com/blockscout/blockscout/pull/2851) - Fix paths for front assets +- [#2843](https://github.com/blockscout/blockscout/pull/2843) - fix realtime fetcher small skips feature +- [#2841](https://github.com/blockscout/blockscout/pull/2841) - LUKSO dashboard height fix +- [#2837](https://github.com/blockscout/blockscout/pull/2837) - fix txlist ordering issue +- [#2830](https://github.com/blockscout/blockscout/pull/2830) - Fix wrong color of contract icon on xDai chain +- [#2829](https://github.com/blockscout/blockscout/pull/2829) - Fix for stuck gas limit label and value +- [#2828](https://github.com/blockscout/blockscout/pull/2828) - Fix for script that clears compilation/launching assets +- [#2800](https://github.com/blockscout/blockscout/pull/2800) - return not found for not verified contract for token read_contract +- [#2806](https://github.com/blockscout/blockscout/pull/2806) - Fix blocks fetching on the main page +- [#2803](https://github.com/blockscout/blockscout/pull/2803) - Fix block validator custom tooltip +- [#2748](https://github.com/blockscout/blockscout/pull/2748) - Rewrite token updater +- [#2704](https://github.com/blockscout/blockscout/pull/2704) - refetch null values in token balances +- [#2690](https://github.com/blockscout/blockscout/pull/2690) - do not stich json rpc config into module for net version cache + +### Chore + +- [#2878](https://github.com/blockscout/blockscout/pull/2878) - Decrease loaders showing delay on the main page +- [#2859](https://github.com/blockscout/blockscout/pull/2859) - Add eth_blockNumber API endpoint to eth_rpc section +- [#2846](https://github.com/blockscout/blockscout/pull/2846) - Remove networks images preload +- [#2845](https://github.com/blockscout/blockscout/pull/2845) - Set outline none for nav dropdown item in mobile view (fix for Safari) +- [#2844](https://github.com/blockscout/blockscout/pull/2844) - Extend external reward types up to 20 +- [#2827](https://github.com/blockscout/blockscout/pull/2827) - Node js 12.13.0 (latest LTS release) support +- [#2818](https://github.com/blockscout/blockscout/pull/2818) - allow hiding marketcap percentage +- [#2817](https://github.com/blockscout/blockscout/pull/2817) - move docker integration documentation to blockscout docs +- [#2808](https://github.com/blockscout/blockscout/pull/2808) - Add tooltip for tx input +- [#2807](https://github.com/blockscout/blockscout/pull/2807) - 422 page +- [#2805](https://github.com/blockscout/blockscout/pull/2805) - Update supported chains default option +- [#2801](https://github.com/blockscout/blockscout/pull/2801) - remove unused clause in address_to_unique_tokens query + +## 2.1.0-beta + +### Features + +- [#2776](https://github.com/blockscout/blockscout/pull/2776) - fetch token counters async +- [#2772](https://github.com/blockscout/blockscout/pull/2772) - add token instance images to the token inventory tab +- [#2733](https://github.com/blockscout/blockscout/pull/2733) - Add cache for first page of uncles +- [#2735](https://github.com/blockscout/blockscout/pull/2735) - Add pending transactions cache +- [#2726](https://github.com/blockscout/blockscout/pull/2726) - Remove internal_transaction block_number setting from blocks runner +- [#2717](https://github.com/blockscout/blockscout/pull/2717) - Improve speed of nonconsensus data removal +- [#2679](https://github.com/blockscout/blockscout/pull/2679) - added fixed height for card chain blocks and card chain transactions +- [#2678](https://github.com/blockscout/blockscout/pull/2678) - fixed dashboard banner height bug +- [#2672](https://github.com/blockscout/blockscout/pull/2672) - added new theme for xUSDT +- [#2667](https://github.com/blockscout/blockscout/pull/2667) - Add ETS-based cache for accounts page +- [#2666](https://github.com/blockscout/blockscout/pull/2666) - fetch token counters in parallel +- [#2665](https://github.com/blockscout/blockscout/pull/2665) - new menu layout for mobile devices +- [#2663](https://github.com/blockscout/blockscout/pull/2663) - Fetch address counters in parallel +- [#2642](https://github.com/blockscout/blockscout/pull/2642) - add ERC721 coin instance page +- [#2762](https://github.com/blockscout/blockscout/pull/2762) - on-fly fetching of token instances +- [#2470](https://github.com/blockscout/blockscout/pull/2470) - Allow Realtime Fetcher to wait for small skips + +### Fixes + +- [#4325](https://github.com/blockscout/blockscout/pull/4325) - Fix search on `/tokens` page +- [#2793](https://github.com/blockscout/blockscout/pull/2793) - Hide "We are indexing this chain right now. Some of the counts may be inaccurate" banner if no txs in blockchain +- [#2779](https://github.com/blockscout/blockscout/pull/2779) - fix fetching `latin1` encoded data +- [#2799](https://github.com/blockscout/blockscout/pull/2799) - fix catchup fetcher for empty node and db +- [#2783](https://github.com/blockscout/blockscout/pull/2783) - Fix stuck value and ticker on the token page +- [#2781](https://github.com/blockscout/blockscout/pull/2781) - optimize txlist json rpc +- [#2777](https://github.com/blockscout/blockscout/pull/2777) - Remove duplicate blocks from changes_list before import +- [#2770](https://github.com/blockscout/blockscout/pull/2770) - do not re-fetch token instances without uris +- [#2769](https://github.com/blockscout/blockscout/pull/2769) - optimize token token transfers query +- [#2768](https://github.com/blockscout/blockscout/pull/2768) - Remove nonconsensus blocks from cache after internal transactions importing +- [#2761](https://github.com/blockscout/blockscout/pull/2761) - add indexes for token instances fetching queries +- [#2767](https://github.com/blockscout/blockscout/pull/2767) - fix websocket subscriptions with token instances +- [#2765](https://github.com/blockscout/blockscout/pull/2765) - fixed width issue for cards in mobile view for Transaction Details page +- [#2755](https://github.com/blockscout/blockscout/pull/2755) - various token instance fetcher fixes +- [#2753](https://github.com/blockscout/blockscout/pull/2753) - fix nft token instance images +- [#2750](https://github.com/blockscout/blockscout/pull/2750) - fixed contract buttons color for NFT token instance on each theme +- [#2746](https://github.com/blockscout/blockscout/pull/2746) - fixed wrong alignment in logs decoded view +- [#2745](https://github.com/blockscout/blockscout/pull/2745) - optimize addresses page +- [#2742](https://github.com/blockscout/blockscout/pull/2742) - +fixed menu hovers in dark mode desktop view +- [#2737](https://github.com/blockscout/blockscout/pull/2737) - switched hardcoded subnetwork value to elixir expression for mobile menu +- [#2736](https://github.com/blockscout/blockscout/pull/2736) - do not update cache if no blocks were inserted +- [#2731](https://github.com/blockscout/blockscout/pull/2731) - fix library verification +- [#2718](https://github.com/blockscout/blockscout/pull/2718) - Include all addresses taking part in transactions in wallets' addresses counter +- [#2709](https://github.com/blockscout/blockscout/pull/2709) - Fix stuck label and value for uncle block height +- [#2707](https://github.com/blockscout/blockscout/pull/2707) - fix for dashboard banner chart legend items +- [#2706](https://github.com/blockscout/blockscout/pull/2706) - fix empty total_supply in coin gecko response +- [#2701](https://github.com/blockscout/blockscout/pull/2701) - Exclude nonconsensus blocks from avg block time calculation by default +- [#2696](https://github.com/blockscout/blockscout/pull/2696) - do not update fetched_coin_balance with nil +- [#2693](https://github.com/blockscout/blockscout/pull/2693) - remove non consensus internal transactions +- [#2691](https://github.com/blockscout/blockscout/pull/2691) - fix exchange rate websocket update for Rootstock +- [#2688](https://github.com/blockscout/blockscout/pull/2688) - fix try it out section +- [#2687](https://github.com/blockscout/blockscout/pull/2687) - remove non-consensus token transfers, logs when inserting new consensus blocks +- [#2684](https://github.com/blockscout/blockscout/pull/2684) - do not filter pending logs +- [#2682](https://github.com/blockscout/blockscout/pull/2682) - Use Task.start instead of Task.async in caches +- [#2671](https://github.com/blockscout/blockscout/pull/2671) - fixed buttons color at smart contract section +- [#2660](https://github.com/blockscout/blockscout/pull/2660) - set correct last value for coin balances chart data +- [#2619](https://github.com/blockscout/blockscout/pull/2619) - Enforce DB transaction's order to prevent deadlocks +- [#2738](https://github.com/blockscout/blockscout/pull/2738) - do not fail block `internal_transactions_indexed_at` field update + +### Chore + +- [#2797](https://github.com/blockscout/blockscout/pull/2797) - Return old style menu +- [#2796](https://github.com/blockscout/blockscout/pull/2796) - Optimize all images with ImageOptim +- [#2794](https://github.com/blockscout/blockscout/pull/2786) - update hosted versions in readme +- [#2789](https://github.com/blockscout/blockscout/pull/2786) - remove projects table in readme, link to docs version +- [#2786](https://github.com/blockscout/blockscout/pull/2786) - updated docs links, removed docs folder +- [#2752](https://github.com/blockscout/blockscout/pull/2752) - allow enabling internal transactions for simple token transfers txs +- [#2749](https://github.com/blockscout/blockscout/pull/2749) - fix opt 22.1 support +- [#2744](https://github.com/blockscout/blockscout/pull/2744) - Disable Geth tests in CI +- [#2724](https://github.com/blockscout/blockscout/pull/2724) - fix ci by commenting a line in hackney library +- [#2708](https://github.com/blockscout/blockscout/pull/2708) - add log index to logs view +- [#2723](https://github.com/blockscout/blockscout/pull/2723) - get rid of ex_json_schema warnings +- [#2740](https://github.com/blockscout/blockscout/pull/2740) - add verify contract rpc doc + +## 2.0.4-beta + +### Features + +- [#2636](https://github.com/blockscout/blockscout/pull/2636) - Execute all address' transactions page queries in parallel +- [#2596](https://github.com/blockscout/blockscout/pull/2596) - support AuRa's empty step reward type +- [#2588](https://github.com/blockscout/blockscout/pull/2588) - add verification submission comment +- [#2505](https://github.com/blockscout/blockscout/pull/2505) - support POA Network emission rewards +- [#2581](https://github.com/blockscout/blockscout/pull/2581) - Add generic Map-like Cache behaviour and implementation +- [#2561](https://github.com/blockscout/blockscout/pull/2561) - Add token's type to the response of tokenlist method +- [#2555](https://github.com/blockscout/blockscout/pull/2555) - find and show decoding candidates for logs +- [#2499](https://github.com/blockscout/blockscout/pull/2499) - import emission reward ranges +- [#2497](https://github.com/blockscout/blockscout/pull/2497) - Add generic Ordered Cache behaviour and implementation + +### Fixes + +- [#2659](https://github.com/blockscout/blockscout/pull/2659) - Multipurpose front-end part update +- [#2640](https://github.com/blockscout/blockscout/pull/2640) - SVG network icons +- [#2635](https://github.com/blockscout/blockscout/pull/2635) - optimize ERC721 inventory query +- [#2626](https://github.com/blockscout/blockscout/pull/2626) - Fixing 2 Mobile UI Issues +- [#2623](https://github.com/blockscout/blockscout/pull/2623) - fix a blinking test +- [#2616](https://github.com/blockscout/blockscout/pull/2616) - deduplicate coin history records by delta +- [#2613](https://github.com/blockscout/blockscout/pull/2613) - fix getminedblocks rpc endpoint +- [#2612](https://github.com/blockscout/blockscout/pull/2612) - Add cache updating independently from Indexer +- [#2610](https://github.com/blockscout/blockscout/pull/2610) - use CoinGecko instead of CoinMarketcap for exchange rates +- [#2592](https://github.com/blockscout/blockscout/pull/2592) - process new metadata format for whisper +- [#2591](https://github.com/blockscout/blockscout/pull/2591) - Fix url error in API page +- [#2572](https://github.com/blockscout/blockscout/pull/2572) - Ease non-critical css +- [#2570](https://github.com/blockscout/blockscout/pull/2570) - Network icons preload +- [#2569](https://github.com/blockscout/blockscout/pull/2569) - do not fetch emission rewards for transactions csv exporter +- [#2568](https://github.com/blockscout/blockscout/pull/2568) - filter pending token transfers +- [#2564](https://github.com/blockscout/blockscout/pull/2564) - fix first page button for uncles and reorgs +- [#2563](https://github.com/blockscout/blockscout/pull/2563) - Fix view less transfers button +- [#2538](https://github.com/blockscout/blockscout/pull/2538) - fetch the last not empty coin balance records +- [#2468](https://github.com/blockscout/blockscout/pull/2468) - fix confirmations for non consensus blocks + +### Chore + +- [#2662](https://github.com/blockscout/blockscout/pull/2662) - fetch coin gecko id based on the coin symbol +- [#2646](https://github.com/blockscout/blockscout/pull/2646) - Added Xerom to list of Additional Chains using BlockScout +- [#2634](https://github.com/blockscout/blockscout/pull/2634) - add Lukso to networks dropdown +- [#2617](https://github.com/blockscout/blockscout/pull/2617) - skip cache update if there are no blocks inserted +- [#2611](https://github.com/blockscout/blockscout/pull/2611) - fix js dependency vulnerabilities +- [#2594](https://github.com/blockscout/blockscout/pull/2594) - do not start genesis data fetching periodically +- [#2590](https://github.com/blockscout/blockscout/pull/2590) - restore backward compatablity with old releases +- [#2577](https://github.com/blockscout/blockscout/pull/2577) - Need recompile column in the env vars table +- [#2574](https://github.com/blockscout/blockscout/pull/2574) - limit request body in json rpc error +- [#2566](https://github.com/blockscout/blockscout/pull/2566) - upgrade absinthe phoenix + +## 2.0.3-beta + +### Features + +- [#2433](https://github.com/blockscout/blockscout/pull/2433) - Add a functionality to try Eth RPC methods in the documentation +- [#2529](https://github.com/blockscout/blockscout/pull/2529) - show both eth value and token transfers on transaction overview page +- [#2376](https://github.com/blockscout/blockscout/pull/2376) - Split API and WebApp routes +- [#2477](https://github.com/blockscout/blockscout/pull/2477) - aggregate token transfers on transaction page +- [#2458](https://github.com/blockscout/blockscout/pull/2458) - Add LAST_BLOCK var to add ability indexing in the range of blocks +- [#2456](https://github.com/blockscout/blockscout/pull/2456) - fetch pending transactions for geth +- [#2403](https://github.com/blockscout/blockscout/pull/2403) - Return gasPrice field at the result of gettxinfo method + +### Fixes + +- [#2562](https://github.com/blockscout/blockscout/pull/2562) - Fix dark theme flickering +- [#2560](https://github.com/blockscout/blockscout/pull/2560) - fix slash before not empty path in docs +- [#2559](https://github.com/blockscout/blockscout/pull/2559) - fix rsk total supply for empty exchange rate +- [#2553](https://github.com/blockscout/blockscout/pull/2553) - Dark theme import to the end of sass +- [#2550](https://github.com/blockscout/blockscout/pull/2550) - correctly encode decimal values for frontend +- [#2549](https://github.com/blockscout/blockscout/pull/2549) - Fix wrong colour of tooltip +- [#2548](https://github.com/blockscout/blockscout/pull/2548) - CSS preload support in Firefox +- [#2547](https://github.com/blockscout/blockscout/pull/2547) - do not show eth value if it's zero on the transaction overview page +- [#2543](https://github.com/blockscout/blockscout/pull/2543) - do not hide search input during logs search +- [#2524](https://github.com/blockscout/blockscout/pull/2524) - fix dark theme validator data styles +- [#2532](https://github.com/blockscout/blockscout/pull/2532) - don't show empty token transfers on the transaction overview page +- [#2528](https://github.com/blockscout/blockscout/pull/2528) - fix coin history chart data +- [#2520](https://github.com/blockscout/blockscout/pull/2520) - Hide loading message when fetching is failed +- [#2523](https://github.com/blockscout/blockscout/pull/2523) - Avoid importing internal_transactions of pending transactions +- [#2519](https://github.com/blockscout/blockscout/pull/2519) - enable `First` page button in pagination +- [#2518](https://github.com/blockscout/blockscout/pull/2518) - create suggested indexes +- [#2517](https://github.com/blockscout/blockscout/pull/2517) - remove duplicate indexes +- [#2515](https://github.com/blockscout/blockscout/pull/2515) - do not aggregate NFT token transfers +- [#2514](https://github.com/blockscout/blockscout/pull/2514) - Isolating of staking dapp css && extracting of non-critical css +- [#2512](https://github.com/blockscout/blockscout/pull/2512) - alert link fix +- [#2509](https://github.com/blockscout/blockscout/pull/2509) - value-ticker gaps fix +- [#2508](https://github.com/blockscout/blockscout/pull/2508) - logs view columns fix +- [#2506](https://github.com/blockscout/blockscout/pull/2506) - fix two active tab in the top menu +- [#2503](https://github.com/blockscout/blockscout/pull/2503) - Mitigate autocompletion library influence to page loading performance +- [#2502](https://github.com/blockscout/blockscout/pull/2502) - increase reward task timeout +- [#2463](https://github.com/blockscout/blockscout/pull/2463) - dark theme fixes +- [#2496](https://github.com/blockscout/blockscout/pull/2496) - fix docker build +- [#2495](https://github.com/blockscout/blockscout/pull/2495) - fix logs for indexed chain +- [#2459](https://github.com/blockscout/blockscout/pull/2459) - fix top addresses query +- [#2425](https://github.com/blockscout/blockscout/pull/2425) - Force to show address view for checksummed address even if it is not in DB +- [#2551](https://github.com/blockscout/blockscout/pull/2551) - Correctly handle dynamically created Bootstrap tooltips + +### Chore + +- [#2554](https://github.com/blockscout/blockscout/pull/2554) - remove extra slash for endpoint url in docs +- [#2552](https://github.com/blockscout/blockscout/pull/2552) - remove brackets for token holders percentage +- [#2507](https://github.com/blockscout/blockscout/pull/2507) - update minor version of ecto, ex_machina, phoenix_live_reload +- [#2516](https://github.com/blockscout/blockscout/pull/2516) - update absinthe plug from fork +- [#2473](https://github.com/blockscout/blockscout/pull/2473) - get rid of cldr warnings +- [#2402](https://github.com/blockscout/blockscout/pull/2402) - bump otp version to 22.0 +- [#2492](https://github.com/blockscout/blockscout/pull/2492) - hide decoded row if event is not decoded +- [#2490](https://github.com/blockscout/blockscout/pull/2490) - enable credo duplicated code check +- [#2432](https://github.com/blockscout/blockscout/pull/2432) - bump credo version +- [#2457](https://github.com/blockscout/blockscout/pull/2457) - update mix.lock +- [#2435](https://github.com/blockscout/blockscout/pull/2435) - Replace deprecated extract-text-webpack-plugin with mini-css-extract-plugin +- [#2450](https://github.com/blockscout/blockscout/pull/2450) - Fix clearance of logs and node_modules folders in clearing script +- [#2434](https://github.com/blockscout/blockscout/pull/2434) - get rid of timex warnings +- [#2402](https://github.com/blockscout/blockscout/pull/2402) - bump otp version to 22.0 +- [#2373](https://github.com/blockscout/blockscout/pull/2373) - Add script to validate internal_transactions constraint for large DBs + +## 2.0.2-beta + +### Features + +- [#2412](https://github.com/blockscout/blockscout/pull/2412) - dark theme +- [#2399](https://github.com/blockscout/blockscout/pull/2399) - decode verified smart contract's logs +- [#2391](https://github.com/blockscout/blockscout/pull/2391) - Controllers Improvements +- [#2379](https://github.com/blockscout/blockscout/pull/2379) - Disable network selector when is empty +- [#2374](https://github.com/blockscout/blockscout/pull/2374) - decode constructor arguments for verified smart contracts +- [#2366](https://github.com/blockscout/blockscout/pull/2366) - paginate eth logs +- [#2360](https://github.com/blockscout/blockscout/pull/2360) - add default evm version to smart contract verification +- [#2352](https://github.com/blockscout/blockscout/pull/2352) - Fetch rewards in parallel with transactions +- [#2294](https://github.com/blockscout/blockscout/pull/2294) - add healthy block period checking endpoint +- [#2324](https://github.com/blockscout/blockscout/pull/2324) - set timeout for loading message on the main page + +### Fixes + +- [#2421](https://github.com/blockscout/blockscout/pull/2421) - Fix hiding of loader for txs on the main page +- [#2420](https://github.com/blockscout/blockscout/pull/2420) - fetch data from cache in healthy endpoint +- [#2416](https://github.com/blockscout/blockscout/pull/2416) - Fix "page not found" handling in the router +- [#2413](https://github.com/blockscout/blockscout/pull/2413) - remove outer tables for decoded data +- [#2410](https://github.com/blockscout/blockscout/pull/2410) - preload smart contract for logs decoding +- [#2405](https://github.com/blockscout/blockscout/pull/2405) - added templates for table loader and tile loader +- [#2398](https://github.com/blockscout/blockscout/pull/2398) - show only one decoded candidate +- [#2389](https://github.com/blockscout/blockscout/pull/2389) - Reduce Lodash lib size (86% of lib methods are not used) +- [#2388](https://github.com/blockscout/blockscout/pull/2388) - add create2 support to geth's js tracer +- [#2387](https://github.com/blockscout/blockscout/pull/2387) - fix not existing keys in transaction json rpc +- [#2378](https://github.com/blockscout/blockscout/pull/2378) - Page performance: exclude moment.js localization files except EN, remove unused css +- [#2368](https://github.com/blockscout/blockscout/pull/2368) - add two columns of smart contract info +- [#2375](https://github.com/blockscout/blockscout/pull/2375) - Update created_contract_code_indexed_at on transaction import conflict +- [#2346](https://github.com/blockscout/blockscout/pull/2346) - Avoid fetching internal transactions of blocks that still need refetching +- [#2350](https://github.com/blockscout/blockscout/pull/2350) - fix invalid User agent headers +- [#2345](https://github.com/blockscout/blockscout/pull/2345) - do not override existing market records +- [#2337](https://github.com/blockscout/blockscout/pull/2337) - set url params for prod explicitly +- [#2341](https://github.com/blockscout/blockscout/pull/2341) - fix transaction input json encoding +- [#2311](https://github.com/blockscout/blockscout/pull/2311) - fix market history overriding with zeroes +- [#2310](https://github.com/blockscout/blockscout/pull/2310) - parse url for api docs +- [#2299](https://github.com/blockscout/blockscout/pull/2299) - fix interpolation in error message +- [#2303](https://github.com/blockscout/blockscout/pull/2303) - fix transaction csv download link +- [#2304](https://github.com/blockscout/blockscout/pull/2304) - footer grid fix for md resolution +- [#2291](https://github.com/blockscout/blockscout/pull/2291) - dashboard fix for md resolution, transactions load fix, block info row fix, addresses page issue, check mark issue +- [#2326](https://github.com/blockscout/blockscout/pull/2326) - fix nested constructor arguments + +### Chore + +- [#2422](https://github.com/blockscout/blockscout/pull/2422) - check if address_id is binary in token_transfers_csv endpoint +- [#2418](https://github.com/blockscout/blockscout/pull/2418) - Remove parentheses in market cap percentage +- [#2401](https://github.com/blockscout/blockscout/pull/2401) - add ENV vars to manage updating period of average block time and market history cache +- [#2363](https://github.com/blockscout/blockscout/pull/2363) - add parameters example for eth rpc +- [#2342](https://github.com/blockscout/blockscout/pull/2342) - Upgrade Postgres image version in Docker setup +- [#2325](https://github.com/blockscout/blockscout/pull/2325) - Reduce function input to address' hash only where possible +- [#2323](https://github.com/blockscout/blockscout/pull/2323) - Group Explorer caches +- [#2305](https://github.com/blockscout/blockscout/pull/2305) - Improve Address controllers +- [#2302](https://github.com/blockscout/blockscout/pull/2302) - fix names for xDai source +- [#2289](https://github.com/blockscout/blockscout/pull/2289) - Optional websockets for dev environment +- [#2307](https://github.com/blockscout/blockscout/pull/2307) - add GoJoy to README +- [#2293](https://github.com/blockscout/blockscout/pull/2293) - remove request idle timeout configuration +- [#2255](https://github.com/blockscout/blockscout/pull/2255) - bump elixir version to 1.9.0 + +## 2.0.1-beta + +### Features + +- [#2283](https://github.com/blockscout/blockscout/pull/2283) - Add transactions cache +- [#2182](https://github.com/blockscout/blockscout/pull/2182) - add market history cache +- [#2109](https://github.com/blockscout/blockscout/pull/2109) - use bigger updates instead of `Multi` transactions in BlocksTransactionsMismatch +- [#2075](https://github.com/blockscout/blockscout/pull/2075) - add blocks cache +- [#2151](https://github.com/blockscout/blockscout/pull/2151) - hide dropdown menu then other networks list is empty +- [#2191](https://github.com/blockscout/blockscout/pull/2191) - allow to configure token metadata update interval +- [#2146](https://github.com/blockscout/blockscout/pull/2146) - feat: add eth_getLogs rpc endpoint +- [#2216](https://github.com/blockscout/blockscout/pull/2216) - Improve token's controllers by avoiding unnecessary preloads +- [#2235](https://github.com/blockscout/blockscout/pull/2235) - save and show additional validation fields to smart contract +- [#2190](https://github.com/blockscout/blockscout/pull/2190) - show all token transfers +- [#2193](https://github.com/blockscout/blockscout/pull/2193) - feat: add BLOCKSCOUT_HOST, and use it in API docs +- [#2266](https://github.com/blockscout/blockscout/pull/2266) - allow excluding uncles from average block time calculation + +### Fixes + +- [#2290](https://github.com/blockscout/blockscout/pull/2290) - Add eth_get_balance.json to AddressView's render +- [#2286](https://github.com/blockscout/blockscout/pull/2286) - banner stats issues on sm resolutions, transactions title issue +- [#2284](https://github.com/blockscout/blockscout/pull/2284) - add 404 status for not existing pages +- [#2244](https://github.com/blockscout/blockscout/pull/2244) - fix internal transactions failing to be indexed because of constraint +- [#2281](https://github.com/blockscout/blockscout/pull/2281) - typo issues, dropdown issues +- [#2278](https://github.com/blockscout/blockscout/pull/2278) - increase threshold for scientific notation +- [#2275](https://github.com/blockscout/blockscout/pull/2275) - Description for networks selector +- [#2263](https://github.com/blockscout/blockscout/pull/2263) - added an ability to close network selector on outside click +- [#2257](https://github.com/blockscout/blockscout/pull/2257) - 'download csv' button added to different tabs +- [#2242](https://github.com/blockscout/blockscout/pull/2242) - added styles for 'download csv' button +- [#2261](https://github.com/blockscout/blockscout/pull/2261) - header logo aligned to the center properly +- [#2254](https://github.com/blockscout/blockscout/pull/2254) - search length issue, tile link wrapping issue +- [#2238](https://github.com/blockscout/blockscout/pull/2238) - header content alignment issue, hide navbar on outside click +- [#2229](https://github.com/blockscout/blockscout/pull/2229) - gap issue between qr and copy button in token transfers, top cards width and height issue +- [#2201](https://github.com/blockscout/blockscout/pull/2201) - footer columns fix +- [#2179](https://github.com/blockscout/blockscout/pull/2179) - fix docker build error +- [#2165](https://github.com/blockscout/blockscout/pull/2165) - sort blocks by timestamp when calculating average block time +- [#2175](https://github.com/blockscout/blockscout/pull/2175) - fix coinmarketcap response errors +- [#2164](https://github.com/blockscout/blockscout/pull/2164) - fix large numbers in balance view card +- [#2155](https://github.com/blockscout/blockscout/pull/2155) - fix pending transaction query +- [#2183](https://github.com/blockscout/blockscout/pull/2183) - tile content aligning for mobile resolution fix, dai logo fix +- [#2162](https://github.com/blockscout/blockscout/pull/2162) - contract creation tile color changed +- [#2144](https://github.com/blockscout/blockscout/pull/2144) - 'page not found' images path fixed for goerli +- [#2142](https://github.com/blockscout/blockscout/pull/2142) - Removed posdao theme and logo, added 'page not found' image for goerli +- [#2138](https://github.com/blockscout/blockscout/pull/2138) - badge colors issue, api titles issue +- [#2129](https://github.com/blockscout/blockscout/pull/2129) - Fix for width of explorer elements +- [#2121](https://github.com/blockscout/blockscout/pull/2121) - Binding of 404 page +- [#2120](https://github.com/blockscout/blockscout/pull/2120) - footer links and socials focus color issue +- [#2113](https://github.com/blockscout/blockscout/pull/2113) - renewed logos for rsk, dai, blockscout; themes color changes for lukso; error images for lukso +- [#2112](https://github.com/blockscout/blockscout/pull/2112) - themes color improvements, dropdown color issue +- [#2110](https://github.com/blockscout/blockscout/pull/2110) - themes colors issues, ui issues +- [#2103](https://github.com/blockscout/blockscout/pull/2103) - ui issues for all themes +- [#2090](https://github.com/blockscout/blockscout/pull/2090) - updated some ETC theme colors +- [#2096](https://github.com/blockscout/blockscout/pull/2096) - RSK theme fixes +- [#2093](https://github.com/blockscout/blockscout/pull/2093) - detect token transfer type for deprecated erc721 spec +- [#2111](https://github.com/blockscout/blockscout/pull/2111) - improve address transaction controller +- [#2108](https://github.com/blockscout/blockscout/pull/2108) - fix uncle fetching without full transactions +- [#2128](https://github.com/blockscout/blockscout/pull/2128) - add new function clause for uncle errors +- [#2123](https://github.com/blockscout/blockscout/pull/2123) - fix coins percentage view +- [#2119](https://github.com/blockscout/blockscout/pull/2119) - fix map logging +- [#2130](https://github.com/blockscout/blockscout/pull/2130) - fix navigation +- [#2148](https://github.com/blockscout/blockscout/pull/2148) - filter pending logs +- [#2147](https://github.com/blockscout/blockscout/pull/2147) - add rsk format of checksum +- [#2149](https://github.com/blockscout/blockscout/pull/2149) - remove pending transaction count +- [#2177](https://github.com/blockscout/blockscout/pull/2177) - remove duplicate entries from UncleBlock's Fetcher +- [#2169](https://github.com/blockscout/blockscout/pull/2169) - add more validator reward types for xDai +- [#2173](https://github.com/blockscout/blockscout/pull/2173) - handle correctly empty transactions +- [#2174](https://github.com/blockscout/blockscout/pull/2174) - fix reward channel joining +- [#2186](https://github.com/blockscout/blockscout/pull/2186) - fix net version test +- [#2196](https://github.com/blockscout/blockscout/pull/2196) - Nethermind client fixes +- [#2237](https://github.com/blockscout/blockscout/pull/2237) - fix rsk total_supply +- [#2198](https://github.com/blockscout/blockscout/pull/2198) - reduce transaction status and error constraint +- [#2167](https://github.com/blockscout/blockscout/pull/2167) - feat: document eth rpc api mimicking endpoints +- [#2225](https://github.com/blockscout/blockscout/pull/2225) - fix metadata decoding in Solidity 0.5.9 smart contract verification +- [#2204](https://github.com/blockscout/blockscout/pull/2204) - fix large contract verification +- [#2258](https://github.com/blockscout/blockscout/pull/2258) - reduce BlocksTransactionsMismatch memory footprint +- [#2247](https://github.com/blockscout/blockscout/pull/2247) - hide logs search if there are no logs +- [#2248](https://github.com/blockscout/blockscout/pull/2248) - sort block after query execution for average block time +- [#2249](https://github.com/blockscout/blockscout/pull/2249) - More transaction controllers improvements +- [#2267](https://github.com/blockscout/blockscout/pull/2267) - Modify implementation of `where_transaction_has_multiple_internal_transactions` +- [#2270](https://github.com/blockscout/blockscout/pull/2270) - Remove duplicate params in `Indexer.Fetcher.TokenBalance` +- [#2268](https://github.com/blockscout/blockscout/pull/2268) - remove not existing assigns in html code +- [#2276](https://github.com/blockscout/blockscout/pull/2276) - remove port in docs + +### Chore + +- [#2127](https://github.com/blockscout/blockscout/pull/2127) - use previouse chromedriver version +- [#2118](https://github.com/blockscout/blockscout/pull/2118) - show only the last decompiled contract +- [#2255](https://github.com/blockscout/blockscout/pull/2255) - upgrade elixir version to 1.9.0 +- [#2256](https://github.com/blockscout/blockscout/pull/2256) - use the latest version of chromedriver + +## 2.0.0-beta + +### Features + +- [#2044](https://github.com/blockscout/blockscout/pull/2044) - New network selector. +- [#2091](https://github.com/blockscout/blockscout/pull/2091) - Added "Question" modal. +- [#1963](https://github.com/blockscout/blockscout/pull/1963), [#1959](https://github.com/blockscout/blockscout/pull/1959), [#1948](https://github.com/blockscout/blockscout/pull/1948), [#1936](https://github.com/blockscout/blockscout/pull/1936), [#1925](https://github.com/blockscout/blockscout/pull/1925), [#1922](https://github.com/blockscout/blockscout/pull/1922), [#1903](https://github.com/blockscout/blockscout/pull/1903), [#1874](https://github.com/blockscout/blockscout/pull/1874), [#1895](https://github.com/blockscout/blockscout/pull/1895), [#2031](https://github.com/blockscout/blockscout/pull/2031), [#2073](https://github.com/blockscout/blockscout/pull/2073), [#2074](https://github.com/blockscout/blockscout/pull/2074), - added new themes and logos for poa, eth, rinkeby, goerli, ropsten, kovan, sokol, xdai, etc, rsk and default theme +- [#1726](https://github.com/blockscout/blockscout/pull/2071) - Updated styles for the new smart contract page. +- [#2081](https://github.com/blockscout/blockscout/pull/2081) - Tooltip for 'more' button, explorers logos added +- [#2010](https://github.com/blockscout/blockscout/pull/2010) - added "block not found" and "tx not found pages" +- [#1928](https://github.com/blockscout/blockscout/pull/1928) - pagination styles were updated +- [#1940](https://github.com/blockscout/blockscout/pull/1940) - qr modal button and background issue +- [#1907](https://github.com/blockscout/blockscout/pull/1907) - dropdown color bug fix (lukso theme) and tooltip color bug fix +- [#1859](https://github.com/blockscout/blockscout/pull/1859) - feat: show raw transaction traces +- [#1941](https://github.com/blockscout/blockscout/pull/1941) - feat: add on demand fetching and stale attr to rpc +- [#1957](https://github.com/blockscout/blockscout/pull/1957) - Calculate stakes ratio before insert pools +- [#1956](https://github.com/blockscout/blockscout/pull/1956) - add logs tab to address +- [#1952](https://github.com/blockscout/blockscout/pull/1952) - feat: exclude empty contracts by default +- [#1954](https://github.com/blockscout/blockscout/pull/1954) - feat: use creation init on self destruct +- [#2036](https://github.com/blockscout/blockscout/pull/2036) - New tables for staking pools and delegators +- [#1974](https://github.com/blockscout/blockscout/pull/1974) - feat: previous page button logic +- [#1999](https://github.com/blockscout/blockscout/pull/1999) - load data async on addresses page +- [#1807](https://github.com/blockscout/blockscout/pull/1807) - New theming capabilites. +- [#2040](https://github.com/blockscout/blockscout/pull/2040) - Verification links to other explorers for ETH +- [#2037](https://github.com/blockscout/blockscout/pull/2037) - add address logs search functionality +- [#2012](https://github.com/blockscout/blockscout/pull/2012) - make all pages pagination async +- [#2064](https://github.com/blockscout/blockscout/pull/2064) - feat: add fields to tx apis, small cleanups +- [#2100](https://github.com/blockscout/blockscout/pull/2100) - feat: eth_get_balance rpc endpoint + +### Fixes + +- [#2228](https://github.com/blockscout/blockscout/pull/2228) - favorites duplication issues, active radio issue +- [#2207](https://github.com/blockscout/blockscout/pull/2207) - new 'download csv' button design +- [#2206](https://github.com/blockscout/blockscout/pull/2206) - added styles for 'Download All Transactions as CSV' button +- [#2099](https://github.com/blockscout/blockscout/pull/2099) - logs search input width +- [#2098](https://github.com/blockscout/blockscout/pull/2098) - nav dropdown issue, logo size issue +- [#2082](https://github.com/blockscout/blockscout/pull/2082) - dropdown styles, tooltip gap fix, 404 page added +- [#2077](https://github.com/blockscout/blockscout/pull/2077) - ui issues +- [#2072](https://github.com/blockscout/blockscout/pull/2072) - Fixed checkmarks not showing correctly in tabs. +- [#2066](https://github.com/blockscout/blockscout/pull/2066) - fixed length of logs search input +- [#2056](https://github.com/blockscout/blockscout/pull/2056) - log search form styles added +- [#2043](https://github.com/blockscout/blockscout/pull/2043) - Fixed modal dialog width for 'verify other explorers' +- [#2025](https://github.com/blockscout/blockscout/pull/2025) - Added a new color to display transactions' errors. +- [#2033](https://github.com/blockscout/blockscout/pull/2033) - Header nav. dropdown active element color issue +- [#2019](https://github.com/blockscout/blockscout/pull/2019) - Fixed the missing tx hashes. +- [#2020](https://github.com/blockscout/blockscout/pull/2020) - Fixed a bug triggered when a second click to a selected tab caused the other tabs to hide. +- [#1944](https://github.com/blockscout/blockscout/pull/1944) - fixed styles for token's dropdown. +- [#1926](https://github.com/blockscout/blockscout/pull/1926) - status label alignment +- [#1849](https://github.com/blockscout/blockscout/pull/1849) - Improve chains menu +- [#1868](https://github.com/blockscout/blockscout/pull/1868) - fix: logs list endpoint performance +- [#1822](https://github.com/blockscout/blockscout/pull/1822) - Fix style breaks in decompiled contract code view +- [#1885](https://github.com/blockscout/blockscout/pull/1885) - highlight reserved words in decompiled code +- [#1896](https://github.com/blockscout/blockscout/pull/1896) - re-query tokens in top nav automplete +- [#1905](https://github.com/blockscout/blockscout/pull/1905) - fix reorgs, uncles pagination +- [#1904](https://github.com/blockscout/blockscout/pull/1904) - fix `BLOCK_COUNT_CACHE_TTL` env var type +- [#1915](https://github.com/blockscout/blockscout/pull/1915) - fallback to 2 latest evm versions +- [#1937](https://github.com/blockscout/blockscout/pull/1937) - Check the presence of overlap[i] object before retrieving properties from it +- [#1960](https://github.com/blockscout/blockscout/pull/1960) - do not remove bold text in decompiled contacts +- [#1966](https://github.com/blockscout/blockscout/pull/1966) - fix: add fields for contract filter performance +- [#2017](https://github.com/blockscout/blockscout/pull/2017) - fix: fix to/from filters on tx list pages +- [#2008](https://github.com/blockscout/blockscout/pull/2008) - add new function clause for xDai network beneficiaries +- [#2009](https://github.com/blockscout/blockscout/pull/2009) - addresses page improvements +- [#2027](https://github.com/blockscout/blockscout/pull/2027) - fix: `BlocksTransactionsMismatch` ignoring blocks without transactions +- [#2062](https://github.com/blockscout/blockscout/pull/2062) - fix: uniq by hash, instead of transaction +- [#2052](https://github.com/blockscout/blockscout/pull/2052) - allow bytes32 for name and symbol +- [#2047](https://github.com/blockscout/blockscout/pull/2047) - fix: show creating internal transactions +- [#2014](https://github.com/blockscout/blockscout/pull/2014) - fix: use better queries for listLogs endpoint +- [#2027](https://github.com/blockscout/blockscout/pull/2027) - fix: `BlocksTransactionsMismatch` ignoring blocks without transactions +- [#2070](https://github.com/blockscout/blockscout/pull/2070) - reduce `max_concurrency` of `BlocksTransactionsMismatch` fetcher +- [#2083](https://github.com/blockscout/blockscout/pull/2083) - allow total_difficuly to be nil +- [#2086](https://github.com/blockscout/blockscout/pull/2086) - fix geth's staticcall without output + +### Chore + +- [#1900](https://github.com/blockscout/blockscout/pull/1900) - SUPPORTED_CHAINS ENV var +- [#1958](https://github.com/blockscout/blockscout/pull/1958) - Default value for release link env var +- [#1964](https://github.com/blockscout/blockscout/pull/1964) - ALLOWED_EVM_VERSIONS env var +- [#1975](https://github.com/blockscout/blockscout/pull/1975) - add log index to transaction view +- [#1988](https://github.com/blockscout/blockscout/pull/1988) - Fix wrong parity tasks names in Circle CI +- [#2000](https://github.com/blockscout/blockscout/pull/2000) - docker/Makefile: always set a container name +- [#2018](https://github.com/blockscout/blockscout/pull/2018) - Use PORT env variable in dev config +- [#2055](https://github.com/blockscout/blockscout/pull/2055) - Increase timeout for geth indexers +- [#2069](https://github.com/blockscout/blockscout/pull/2069) - Docsify integration: static docs page generation + +## 1.3.15-beta + +### Features + +- [#1857](https://github.com/blockscout/blockscout/pull/1857) - Re-implement Geth JS internal transaction tracer in Elixir +- [#1989](https://github.com/blockscout/blockscout/pull/1989) - fix: consolidate address w/ balance one at a time +- [#2002](https://github.com/blockscout/blockscout/pull/2002) - Get estimated count of blocks when cache is empty + +### Fixes + +- [#1869](https://github.com/blockscout/blockscout/pull/1869) - Fix output and gas extraction in JS tracer for Geth +- [#1992](https://github.com/blockscout/blockscout/pull/1992) - fix: support https for wobserver polling +- [#2027](https://github.com/blockscout/blockscout/pull/2027) - fix: `BlocksTransactionsMismatch` ignoring blocks without transactions + +## 1.3.14-beta + +- [#1812](https://github.com/blockscout/blockscout/pull/1812) - add pagination to addresses page +- [#1920](https://github.com/blockscout/blockscout/pull/1920) - fix: remove source code fields from list endpoint +- [#1876](https://github.com/blockscout/blockscout/pull/1876) - async calculate a count of blocks + +### Fixes + +- [#1917](https://github.com/blockscout/blockscout/pull/1917) - Force block refetch if transaction is re-collated in a different block + +### Chore + +- [#1892](https://github.com/blockscout/blockscout/pull/1892) - Remove temporary worker modules + +## 1.3.13-beta + +### Features + +- [#1933](https://github.com/blockscout/blockscout/pull/1933) - add eth_BlockNumber json rpc method + +### Fixes + +- [#1875](https://github.com/blockscout/blockscout/pull/1875) - fix: resolve false positive constructor arguments +- [#1881](https://github.com/blockscout/blockscout/pull/1881) - fix: store solc versions locally for performance +- [#1898](https://github.com/blockscout/blockscout/pull/1898) - check if the constructor has arguments before verifying constructor arguments + +## 1.3.12-beta + +Reverting of synchronous block counter, implemented in #1848 + +## 1.3.11-beta + +### Features + +- [#1815](https://github.com/blockscout/blockscout/pull/1815) - Be able to search without prefix "0x" +- [#1813](https://github.com/blockscout/blockscout/pull/1813) - Add total blocks counter to the main page +- [#1806](https://github.com/blockscout/blockscout/pull/1806) - Verify contracts with a post request +- [#1848](https://github.com/blockscout/blockscout/pull/1848) - Add cache for block counter + +### Fixes + +- [#1829](https://github.com/blockscout/blockscout/pull/1829) - Handle nil quantities in block decoding routine +- [#1830](https://github.com/blockscout/blockscout/pull/1830) - Make block size field nullable +- [#1840](https://github.com/blockscout/blockscout/pull/1840) - Handle case when total supply is nil +- [#1838](https://github.com/blockscout/blockscout/pull/1838) - Block counter calculates only consensus blocks + +### Chore + +- [#1814](https://github.com/blockscout/blockscout/pull/1814) - Clear build artefacts script +- [#1837](https://github.com/blockscout/blockscout/pull/1837) - Add -f flag to clear_build.sh script delete static folder + +## 1.3.10-beta + +### Features + +- [#1739](https://github.com/blockscout/blockscout/pull/1739) - highlight decompiled source code +- [#1696](https://github.com/blockscout/blockscout/pull/1696) - full-text search by tokens +- [#1742](https://github.com/blockscout/blockscout/pull/1742) - Support RSK +- [#1777](https://github.com/blockscout/blockscout/pull/1777) - show ERC-20 token transfer info on transaction page +- [#1770](https://github.com/blockscout/blockscout/pull/1770) - set a websocket keepalive from config +- [#1789](https://github.com/blockscout/blockscout/pull/1789) - add ERC-721 info to transaction overview page +- [#1801](https://github.com/blockscout/blockscout/pull/1801) - Staking pools fetching + +### Fixes + +- [#1724](https://github.com/blockscout/blockscout/pull/1724) - Remove internal tx and token balance fetching from realtime fetcher +- [#1727](https://github.com/blockscout/blockscout/pull/1727) - add logs pagination in rpc api +- [#1740](https://github.com/blockscout/blockscout/pull/1740) - fix empty block time +- [#1743](https://github.com/blockscout/blockscout/pull/1743) - sort decompiled smart contracts in lexicographical order +- [#1756](https://github.com/blockscout/blockscout/pull/1756) - add today's token balance from the previous value +- [#1769](https://github.com/blockscout/blockscout/pull/1769) - add timestamp to block overview +- [#1768](https://github.com/blockscout/blockscout/pull/1768) - fix first block parameter +- [#1778](https://github.com/blockscout/blockscout/pull/1778) - Make websocket optional for realtime fetcher +- [#1790](https://github.com/blockscout/blockscout/pull/1790) - fix constructor arguments verification +- [#1793](https://github.com/blockscout/blockscout/pull/1793) - fix top nav autocomplete +- [#1795](https://github.com/blockscout/blockscout/pull/1795) - fix line numbers for decompiled contracts +- [#1803](https://github.com/blockscout/blockscout/pull/1803) - use coinmarketcap for total_supply by default +- [#1802](https://github.com/blockscout/blockscout/pull/1802) - make coinmarketcap's number of pages configurable +- [#1799](https://github.com/blockscout/blockscout/pull/1799) - Use eth_getUncleByBlockHashAndIndex for uncle block fetching +- [#1531](https://github.com/blockscout/blockscout/pull/1531) - docker: fix dockerFile for secp256k1 building +- [#1835](https://github.com/blockscout/blockscout/pull/1835) - fix: ignore `pong` messages without error + +### Chore + +- [#1804](https://github.com/blockscout/blockscout/pull/1804) - (Chore) Divide chains by Mainnet/Testnet in menu +- [#1783](https://github.com/blockscout/blockscout/pull/1783) - Update README with the chains that use Blockscout +- [#1780](https://github.com/blockscout/blockscout/pull/1780) - Update link to the Github repo in the footer +- [#1757](https://github.com/blockscout/blockscout/pull/1757) - Change twitter acc link to official Blockscout acc twitter +- [#1749](https://github.com/blockscout/blockscout/pull/1749) - Replace the link in the footer with the official POA announcements tg channel link +- [#1718](https://github.com/blockscout/blockscout/pull/1718) - Flatten indexer module hierarchy and supervisor tree +- [#1753](https://github.com/blockscout/blockscout/pull/1753) - Add a check mark to decompiled contract tab +- [#1744](https://github.com/blockscout/blockscout/pull/1744) - remove `0x0..0` from tests +- [#1763](https://github.com/blockscout/blockscout/pull/1763) - Describe indexer structure and list existing fetchers +- [#1800](https://github.com/blockscout/blockscout/pull/1800) - Disable lazy logging check in Credo + +## 1.3.9-beta + +### Features + +- [#1662](https://github.com/blockscout/blockscout/pull/1662) - allow specifying number of optimization runs +- [#1654](https://github.com/blockscout/blockscout/pull/1654) - add decompiled code tab +- [#1661](https://github.com/blockscout/blockscout/pull/1661) - try to compile smart contract with the latest evm version +- [#1665](https://github.com/blockscout/blockscout/pull/1665) - Add contract verification RPC endpoint. +- [#1706](https://github.com/blockscout/blockscout/pull/1706) - allow setting update interval for addresses with b + +### Fixes + +- [#1669](https://github.com/blockscout/blockscout/pull/1669) - do not fail if multiple matching tokens are found +- [#1691](https://github.com/blockscout/blockscout/pull/1691) - decrease token metadata update interval +- [#1688](https://github.com/blockscout/blockscout/pull/1688) - do not fail if failure reason is atom +- [#1692](https://github.com/blockscout/blockscout/pull/1692) - exclude decompiled smart contract from encoding +- [#1684](https://github.com/blockscout/blockscout/pull/1684) - Discard child block with parent_hash not matching hash of imported block +- [#1699](https://github.com/blockscout/blockscout/pull/1699) - use seconds as transaction cache period measure +- [#1697](https://github.com/blockscout/blockscout/pull/1697) - fix failing in rpc if balance is empty +- [#1711](https://github.com/blockscout/blockscout/pull/1711) - rescue failing repo in block number cache update +- [#1712](https://github.com/blockscout/blockscout/pull/1712) - do not set contract code from transaction input +- [#1714](https://github.com/blockscout/blockscout/pull/1714) - fix average block time calculation + +### Chore + +- [#1693](https://github.com/blockscout/blockscout/pull/1693) - Add a checklist to the PR template + +## 1.3.8-beta + +### Features + +- [#1611](https://github.com/blockscout/blockscout/pull/1611) - allow setting the first indexing block +- [#1596](https://github.com/blockscout/blockscout/pull/1596) - add endpoint to create decompiled contracts +- [#1634](https://github.com/blockscout/blockscout/pull/1634) - add transaction count cache + +### Fixes + +- [#1630](https://github.com/blockscout/blockscout/pull/1630) - (Fix) colour for release link in the footer +- [#1621](https://github.com/blockscout/blockscout/pull/1621) - Modify query to fetch failed contract creations +- [#1614](https://github.com/blockscout/blockscout/pull/1614) - Do not fetch burn address token balance +- [#1639](https://github.com/blockscout/blockscout/pull/1614) - Optimize token holder count updates when importing address current balances +- [#1643](https://github.com/blockscout/blockscout/pull/1643) - Set internal_transactions_indexed_at for empty blocks +- [#1647](https://github.com/blockscout/blockscout/pull/1647) - Fix typo in view +- [#1650](https://github.com/blockscout/blockscout/pull/1650) - Add petersburg evm version to smart contract verifier +- [#1657](https://github.com/blockscout/blockscout/pull/1657) - Force consensus loss for parent block if its hash mismatches parent_hash + +### Chore + +## 1.3.7-beta + +### Features + +### Fixes + +- [#1615](https://github.com/blockscout/blockscout/pull/1615) - Add more logging to code fixer process +- [#1613](https://github.com/blockscout/blockscout/pull/1613) - Fix USD fee value +- [#1577](https://github.com/blockscout/blockscout/pull/1577) - Add process to fix contract with code +- [#1583](https://github.com/blockscout/blockscout/pull/1583) - Chunk JSON-RPC batches in case connection times out + +### Chore + +- [#1610](https://github.com/blockscout/blockscout/pull/1610) - Add PIRL to Readme + +## 1.3.6-beta + +### Features + +- [#1589](https://github.com/blockscout/blockscout/pull/1589) - RPC endpoint to list addresses +- [#1567](https://github.com/blockscout/blockscout/pull/1567) - Allow setting different configuration just for realtime fetcher +- [#1562](https://github.com/blockscout/blockscout/pull/1562) - Add incoming transactions count to contract view +- [#1608](https://github.com/blockscout/blockscout/pull/1608) - Add listcontracts RPC Endpoint + +### Fixes + +- [#1595](https://github.com/blockscout/blockscout/pull/1595) - Reduce block_rewards in the catchup fetcher +- [#1590](https://github.com/blockscout/blockscout/pull/1590) - Added guard for fetching blocks with invalid number +- [#1588](https://github.com/blockscout/blockscout/pull/1588) - Fix usd value on address page +- [#1586](https://github.com/blockscout/blockscout/pull/1586) - Exact timestamp display +- [#1581](https://github.com/blockscout/blockscout/pull/1581) - Consider `creates` param when fetching transactions +- [#1559](https://github.com/blockscout/blockscout/pull/1559) - Change v column type for Transactions table + +### Chore + +- [#1579](https://github.com/blockscout/blockscout/pull/1579) - Add SpringChain to the list of Additional Chains Utilizing BlockScout +- [#1578](https://github.com/blockscout/blockscout/pull/1578) - Refine contributing procedure +- [#1572](https://github.com/blockscout/blockscout/pull/1572) - Add option to disable block rewards in indexer config + +## 1.3.5-beta + +### Features + +- [#1560](https://github.com/blockscout/blockscout/pull/1560) - Allow executing smart contract functions in arbitrarily sized batches +- [#1543](https://github.com/blockscout/blockscout/pull/1543) - Use trace_replayBlockTransactions API for faster tracing +- [#1558](https://github.com/blockscout/blockscout/pull/1558) - Allow searching by token symbol +- [#1551](https://github.com/blockscout/blockscout/pull/1551) Exact date and time for Transaction details page +- [#1547](https://github.com/blockscout/blockscout/pull/1547) - Verify smart contracts with evm versions +- [#1540](https://github.com/blockscout/blockscout/pull/1540) - Fetch ERC721 token balances if sender is '0x0..0' +- [#1539](https://github.com/blockscout/blockscout/pull/1539) - Add the link to release in the footer +- [#1519](https://github.com/blockscout/blockscout/pull/1519) - Create contract methods +- [#1496](https://github.com/blockscout/blockscout/pull/1496) - Remove dropped/replaced transactions in pending transactions list +- [#1492](https://github.com/blockscout/blockscout/pull/1492) - Disable usd value for an empty exchange rate +- [#1466](https://github.com/blockscout/blockscout/pull/1466) - Decoding candidates for unverified contracts + +### Fixes + +- [#1545](https://github.com/blockscout/blockscout/pull/1545) - Fix scheduling of latest block polling in Realtime Fetcher +- [#1554](https://github.com/blockscout/blockscout/pull/1554) - Encode integer parameters when calling smart contract functions +- [#1537](https://github.com/blockscout/blockscout/pull/1537) - Fix test that depended on date +- [#1534](https://github.com/blockscout/blockscout/pull/1534) - Render a nicer error when creator cannot be determined +- [#1527](https://github.com/blockscout/blockscout/pull/1527) - Add index to value_fetched_at +- [#1518](https://github.com/blockscout/blockscout/pull/1518) - Select only distinct failed transactions +- [#1516](https://github.com/blockscout/blockscout/pull/1516) - Fix coin balance params reducer for pending transaction +- [#1511](https://github.com/blockscout/blockscout/pull/1511) - Set correct log level for production +- [#1510](https://github.com/blockscout/blockscout/pull/1510) - Fix test that fails every 1st day of the month +- [#1509](https://github.com/blockscout/blockscout/pull/1509) - Add index to blocks' consensus +- [#1508](https://github.com/blockscout/blockscout/pull/1508) - Remove duplicated indexes +- [#1505](https://github.com/blockscout/blockscout/pull/1505) - Use https instead of ssh for absinthe libs +- [#1501](https://github.com/blockscout/blockscout/pull/1501) - Constructor_arguments must be type `text` +- [#1498](https://github.com/blockscout/blockscout/pull/1498) - Add index for created_contract_address_hash in transactions +- [#1493](https://github.com/blockscout/blockscout/pull/1493) - Do not do work in process initialization +- [#1487](https://github.com/blockscout/blockscout/pull/1487) - Limit geth sync to 128 blocks +- [#1484](https://github.com/blockscout/blockscout/pull/1484) - Allow decoding input as utf-8 +- [#1479](https://github.com/blockscout/blockscout/pull/1479) - Remove smoothing from coin balance chart + +### Chore + +- [https://github.com/blockscout/blockscout/pull/1532](https://github.com/blockscout/blockscout/pull/1532) - Upgrade elixir to 1.8.1 +- [https://github.com/blockscout/blockscout/pull/1553](https://github.com/blockscout/blockscout/pull/1553) - Dockerfile: remove 1.7.1 version pin FROM bitwalker/alpine-elixir-phoenix +- [https://github.com/blockscout/blockscout/pull/1465](https://github.com/blockscout/blockscout/pull/1465) - Resolve lodash security alert diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..bfc85b0 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, +religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at andrew@poa.network. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..122503b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,62 @@ +## Contributing + +1. Fork it ( ) +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Write tests that cover your work +4. Commit your changes (`git commit -am 'Add some feature'`) +5. Push to the branch (`git push origin my-new-feature`) +6. Create a new Pull Request +7. Update CHANGELOG.md with the link to PR and description of the changes + +### General + +* Commits should be one logical change that still allows all tests to pass. Prefer smaller commits if there could be two levels of logic grouping. The goal is to allow contributors in the future (including your own future self) to determine your reasoning for making changes and to allow them to cherry-pick, patch or port those changes in isolation to other branches or forks. +* If during your PR you reveal a pre-existing bug: + 1. Try to isolate the bug and fix it on an independent branch and PR it first. + 2. Try to fix the bug in a separate commit from other changes: + 1. Commit the code in the broken state that revealed the bug originally + 2. Commit the fix for the bug. + 3. Continue original PR work. + +### Enhancements + +Enhancements cover all changes that make users lives better: + +* [feature requests filed as issues](https://github.com/blockscout/blockscout/labels/enhancement) that impact end-user [contributors](https://github.com/blockscout/blockscout/labels/contributor) and [developers](https://github.com/blockscout/blockscout/labels/developer) +* changes to the [architecture](https://github.com/blockscout/blockscout/labels/architecture) that make it easier for contributors (in the GitHub sense), dev-ops, and deployers to maintain and run blockscout + +### Bug Fixes + +For bug fixes, whenever possible, there should be at least 2 commits: + +1. A regression test commit that contains tests that demonstrate the bug and show as failing. +2. The bug fix commit that shows the regression test now passing. + +This format ensures that we can run the test to reproduce the original bug without depending on the new code in the fix, which could lead to the test falsely passing. + +### Incompatible Changes + +Incompatible changes can arise as a side-effect of either Enhancements or Bug Fixes. During Enhancements, incompatible changes can occur because, as an example, in order to support showing end-users new data, the database schema may need to be changed and the index rebuilt from scratch. During bug fixes, incompatible changes can occur because in order to fix a bug, the schema had to change, or how certain internal APIs are called changed. + +* Incompatible changes should be called out explicitly, with any steps the various user roles need to do to upgrade. +* If a schema change occurs that requires a re-index add the following to the Pull Request description: + + ```markdown + **NOTE**: A database reset and re-index is required + ``` + +### Pull Request + +There is a [PULL_REQUEST_TEMPLATE.md](PULL_REQUEST_TEMPLATE.md) for this repository, but since it can't fill in the title for you, please follow the following steps when opening a Pull Request before filling in the template: + +* [ ] Title + * [ ] Prefix labels if you don't have permissions to set labels in the GitHub interface. + * (bug) for [bug](https://github.com/blockscout/blockscout/labels/bug) fixes + * (enhancement) for [enhancement](https://github.com/blockscout/blockscout/labels/enhancement)s + * (incompatible changes) for [incompatible changes](https://github.com/blockscout/blockscout/labels/incompatible%20changes), such a refactor that removes functionality, changes arguments, or makes something required that wasn't previously. + * [ ] Single sentence summary of change + * What was fixed for bugs + * What was added for enhancements + * What was changed for incompatible changes + +See [#255](https://github.com/blockscout/blockscout/pull/255) as an example PR that uses GitHub keywords and a Changelog to explain multiple changes. diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..1e5bff0 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,22 @@ +*Describe your issue here.* + +### Environment + +* Deployment type (Manual/Docker/Docker-compose): +* Elixir & Erlang/OTP versions (`elixir -version`): +* Node JS version (`node -v`): +* Operating System: +* Blockscout Version/branch/commit: +* Archive node type && version (Erigon/Geth/Nethermind/Ganache/?): + +### Steps to reproduce + +*Tell us how to reproduce this issue. ❤️ if you can push up a branch to your fork with a regression test we can run to reproduce locally.* + +### Expected behaviour + +*Tell us what should happen.* + +### Actual behaviour + +*Tell us what happens instead.* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..001deec --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,29 @@ +*[GitHub keywords to close any associated issues](https://blog.github.com/2013-05-14-closing-issues-via-pull-requests/)* + +## Motivation + +*Why we should merge these changes. If using GitHub keywords to close [issues](https://github.com/poanetwork/blockscout/issues), this is optional as the motivation can be read on the issue page.* + +## Changelog + +### Enhancements +*Things you added that don't break anything. Regression tests for Bug Fixes count as Enhancements.* + +### Bug Fixes +*Things you changed that fix bugs. If a fixes a bug, but in so doing adds a new requirement, removes code, or requires a database reset and reindex, the breaking part of the change should be added to Incompatible Changes below also.* + +### Incompatible Changes +*Things you broke while doing Enhancements and Bug Fixes. Breaking changes include (1) adding new requirements and (2) removing code. Renaming counts as (2) because a rename is a removal followed by an add.* + +## Upgrading + +*If you have any Incompatible Changes in the above Changelog, outline how users of prior versions can upgrade once this PR lands or when reviewers are testing locally. A common upgrading step is "Database reset and re-index required".* + +## Checklist for your Pull Request (PR) + + - [ ] I added an entry to `CHANGELOG.md` with this PR + - [ ] If I added new functionality, I added tests covering it. + - [ ] If I fixed a bug, I added a regression test to prevent the bug from silently reappearing again. + - [ ] I checked whether I should update the docs and did so by submitting a PR to https://github.com/blockscout/docs + - [ ] If I added/changed/removed ENV var, I submitted a PR to https://github.com/blockscout/docs to update the list of env vars at https://github.com/blockscout/docs/blob/master/for-developers/information-and-settings/env-variables.md and I updated the version to `master` in the Version column. Changes will be reflected in this table: https://docs.blockscout.com/for-developers/information-and-settings/env-variables. + - [ ] If I add new indices into DB, I checked, that they are not redundant with PGHero or other tools diff --git a/README.md b/README.md new file mode 100644 index 0000000..316e101 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +

BlockScout

+

Blockchain Explorer for inspecting and analyzing EVM Chains.

+
+ +[![Blockscout](https://github.com/blockscout/blockscout/workflows/Blockscout/badge.svg?branch=master)](https://github.com/blockscout/blockscout/actions) +[![](https://dcbadge.vercel.app/api/server/blockscout?style=flat)](https://discord.gg/blockscout) + +
+ + +BlockScout provides a comprehensive, easy-to-use interface for users to view, confirm, and inspect transactions on EVM (Ethereum Virtual Machine) blockchains. This includes the POA Network, Gnosis Chain, Ethereum Classic and other **Ethereum testnets, private networks and sidechains**. + +See our [project documentation](https://docs.blockscout.com/) for detailed information and setup instructions. + +For questions, comments and feature requests see the [discussions section](https://github.com/blockscout/blockscout/discussions). + +## About BlockScout + +BlockScout is an Elixir application that allows users to search transactions, view accounts and balances, and verify smart contracts on the Ethereum network including all forks and sidechains. + +Currently available full-featured block explorers (Etherscan, Etherchain, Blockchair) are closed systems which are not independently verifiable. As Ethereum sidechains continue to proliferate in both private and public settings, transparent, open-source tools are needed to analyze and validate transactions. + +## Supported Projects + +BlockScout supports a number of projects. Hosted instances include POA Network, Gnosis Chain, Ethereum Classic, Sokol & Kovan testnets, and other EVM chains. + +- [List of hosted mainnets, testnets, and additional chains using BlockScout](https://docs.blockscout.com/for-projects/supported-projects) +- [Hosted instance versions](https://docs.blockscout.com/about/use-cases/hosted-blockscout) + +## Getting Started + +See the [project documentation](https://docs.blockscout.com/) for instructions: + +- [Requirements](https://docs.blockscout.com/for-developers/information-and-settings/requirements) +- [Ansible deployment](https://docs.blockscout.com/for-developers/ansible-deployment) +- [Manual deployment](https://docs.blockscout.com/for-developers/manual-deployment) +- [ENV variables](https://docs.blockscout.com/for-developers/information-and-settings/env-variables) +- [Configuration options](https://docs.blockscout.com/for-developers/configuration-options) + +## Acknowledgements + +We would like to thank the [EthPrize foundation](http://ethprize.io/) for their funding support. + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution and pull request protocol. We expect contributors to follow our [code of conduct](CODE_OF_CONDUCT.md) when submitting code or comments. + +## License + +[![License: GPL v3.0](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) + +This project is licensed under the GNU General Public License v3.0. See the [LICENSE](LICENSE) file for details. diff --git a/apps/block_scout_web/.sobelow-conf b/apps/block_scout_web/.sobelow-conf new file mode 100644 index 0000000..99d6ca9 --- /dev/null +++ b/apps/block_scout_web/.sobelow-conf @@ -0,0 +1,12 @@ +[ + verbose: false, + private: true, + skip: true, + router: "lib/block_scout_web/router.ex", + exit: "low", + format: "compact", + ignore: ["Config.Headers", "Config.CSWH", "XSS.SendResp", "XSS.Raw"], + ignore_files: [ + "apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex" + ] +] diff --git a/apps/block_scout_web/API blueprint.md b/apps/block_scout_web/API blueprint.md new file mode 100644 index 0000000..9cbc20a --- /dev/null +++ b/apps/block_scout_web/API blueprint.md @@ -0,0 +1,2228 @@ +FORMAT: 1A +HOST:http://blockscout.com/xdai/testnet +# + + +# API Documentation + + +# Group BlockScoutWeb.Account.Api.V1.UserController +## BlockScoutWeb.Account.Api.V1.UserController [/api/account/v1/user/info] +### BlockScoutWeb.Account.Api.V1.UserController info [GET /api/account/v1/user/info] + + + + + ++ Request Get info about user +**GET**  `/api/account/v1/user/info` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTBkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTEzQGJsb2Nrc2NvdXQuY29tZAACaWRh42QABG5hbWVtAAAAC1VzZXIgVGVzdDEwZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjEwZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDEwZAAMd2F0Y2hsaXN0X2lkYeM.d_nsIdBT4zP1sObizRp2ufpZ2-HDGFD1puY3eNSvftY; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gur6Ap5Rc1YAAAYC + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "nickname": "test_user10", + "name": "User Test10", + "email": "test_user-13@blockscout.com", + "avatar": "https://example.com/avatar/test_user10" + } +### BlockScoutWeb.Account.Api.V1.UserController create_tag_address [POST /api/account/v1/user/tags/address] + + + + + ++ Request Add private address tag +**POST**  `/api/account/v1/user/tags/address` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "name": "MyName", + "address_hash": "0x3e9ac8f16c92bc4f093357933b5befbf1e16987b" + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMmQABWVtYWlsbQAAABp0ZXN0X3VzZXItMkBibG9ja3Njb3V0LmNvbWQAAmlkYdtkAARuYW1lbQAAAApVc2VyIFRlc3QyZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjJkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwMmQADHdhdGNobGlzdF9pZGHb.XPfo6e6fTpCgSOVWcAgze_SHHkf_6UVp-SfOi2EVKcM; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gt7Hha-gjLUAABDh + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "name": "MyName", + "id": 65, + "address_hash": "0x3e9ac8f16c92bc4f093357933b5befbf1e16987b" + } + +# Group BlockScoutWeb.Account.Api.V1.TagsController +## BlockScoutWeb.Account.Api.V1.TagsController [/api/account/v1/tags/address/0x3e9ac8f16c92bc4f093357933b5befbf1e16987b] +### BlockScoutWeb.Account.Api.V1.TagsController tags_address [GET /api/account/v1/tags/address/{address_hash}] + + + + ++ Parameters + + address_hash: `0x3e9ac8f16c92bc4f093357933b5befbf1e16987b` + address_hash: 0x3e9ac8f16c92bc4f093357933b5befbf1e16987b + + ++ Request Get tags for address +**GET**  `/api/account/v1/tags/address/0x3e9ac8f16c92bc4f093357933b5befbf1e16987b` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMmQABWVtYWlsbQAAABp0ZXN0X3VzZXItMkBibG9ja3Njb3V0LmNvbWQAAmlkYdtkAARuYW1lbQAAAApVc2VyIFRlc3QyZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjJkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwMmQADHdhdGNobGlzdF9pZGHb.XPfo6e6fTpCgSOVWcAgze_SHHkf_6UVp-SfOi2EVKcM; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gt8j_62gjLUAABFB + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "watchlist_names": [], + "personal_tags": [ + { + "label": "MyName", + "display_name": "MyName", + "address_hash": "0x3e9ac8f16c92bc4f093357933b5befbf1e16987b" + } + ], + "common_tags": [] + } + +# Group BlockScoutWeb.Account.Api.V1.UserController +## BlockScoutWeb.Account.Api.V1.UserController [/api/account/v1/user/tags/address/72] +### BlockScoutWeb.Account.Api.V1.UserController update_tag_address [PUT /api/account/v1/user/tags/address/{id}] + + + + ++ Parameters + + id: `72` + id: 72 + + ++ Request Edit private address tag +**PUT**  `/api/account/v1/user/tags/address/72` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "name": "name3", + "address_hash": "0x0000000000000000000000000000000000000054" + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTdkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTIxQGJsb2Nrc2NvdXQuY29tZAACaWRh6mQABG5hbWVtAAAAC1VzZXIgVGVzdDE3ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE3ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE3ZAAMd2F0Y2hsaXN0X2lkYeo.SwNPw9upySrwQX8GCp62J924WYWbJY-WNA31fMLjUas; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvKquVfUECUAAB4B + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "name": "name3", + "id": 72, + "address_hash": "0x0000000000000000000000000000000000000054" + } +### BlockScoutWeb.Account.Api.V1.UserController tags_address [GET /api/account/v1/user/tags/address] + + + + + ++ Request Get private addresses tags +**GET**  `/api/account/v1/user/tags/address` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTFkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTE0QGJsb2Nrc2NvdXQuY29tZAACaWRh5GQABG5hbWVtAAAAC1VzZXIgVGVzdDExZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjExZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDExZAAMd2F0Y2hsaXN0X2lkYeQ.YOpB44xZNsuC9o5OZZQWpH-ijPijlYkT_fApVrfNuhs; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1guwn5VVeZtAAABdh + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + [ + { + "name": "name2", + "id": 71, + "address_hash": "0x000000000000000000000000000000000000003a" + }, + { + "name": "name1", + "id": 70, + "address_hash": "0x0000000000000000000000000000000000000039" + }, + { + "name": "name0", + "id": 69, + "address_hash": "0x0000000000000000000000000000000000000038" + } + ] +### BlockScoutWeb.Account.Api.V1.UserController delete_tag_address [DELETE /api/account/v1/user/tags/address/{id}] + + + + ++ Parameters + + id: `66` + id: 66 + + ++ Request Delete private address tag +**DELETE**  `/api/account/v1/user/tags/address/66` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyNmQABWVtYWlsbQAAABp0ZXN0X3VzZXItN0BibG9ja3Njb3V0LmNvbWQAAmlkYd9kAARuYW1lbQAAAApVc2VyIFRlc3Q2ZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjZkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwNmQADHdhdGNobGlzdF9pZGHf.2gy24vcTMAaovCIPA7q8PYmlv1ojuZGzgHCkQ6n_W70; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1guUM2L0cz9IAABXh + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "message": "OK" + } +### BlockScoutWeb.Account.Api.V1.UserController create_tag_transaction [POST /api/account/v1/user/tags/transaction] + + + + + ++ Request Create private transaction tag +**POST**  `/api/account/v1/user/tags/transaction` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000009", + "name": "MyName" + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjFkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI1QGJsb2Nrc2NvdXQuY29tZAACaWRh7mQABG5hbWVtAAAAC1VzZXIgVGVzdDIxZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIxZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIxZAAMd2F0Y2hsaXN0X2lkYe4.OALg_k0K4kFbxlwrk2_wILKz3Ojtx5g-lwqsQWUvTHE; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvV7jRTkLOwAACCB + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000009", + "name": "MyName", + "id": 72 + } + + ++ Request Error on try to create private transaction tag for tx does not exist +**POST**  `/api/account/v1/user/tags/transaction` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000008", + "name": "MyName" + } + ++ Response 422 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjFkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI1QGJsb2Nrc2NvdXQuY29tZAACaWRh7mQABG5hbWVtAAAAC1VzZXIgVGVzdDIxZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIxZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIxZAAMd2F0Y2hsaXN0X2lkYe4.OALg_k0K4kFbxlwrk2_wILKz3Ojtx5g-lwqsQWUvTHE; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvVV0ZPkLOwAACBh + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "errors": { + "tx_hash": [ + "Transaction does not exist" + ] + } + } + +# Group BlockScoutWeb.Account.Api.V1.TagsController +## BlockScoutWeb.Account.Api.V1.TagsController [/api/account/v1/tags/transaction/0x0000000000000000000000000000000000000000000000000000000000000009] +### BlockScoutWeb.Account.Api.V1.TagsController tags_transaction [GET /api/account/v1/tags/transaction/{transaction_hash}] + + + + ++ Parameters + + transaction_hash: `0x0000000000000000000000000000000000000000000000000000000000000009` + transaction_hash: 0x0000000000000000000000000000000000000000000000000000000000000009 + + ++ Request Get tags for transaction +**GET**  `/api/account/v1/tags/transaction/0x0000000000000000000000000000000000000000000000000000000000000009` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjFkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI1QGJsb2Nrc2NvdXQuY29tZAACaWRh7mQABG5hbWVtAAAAC1VzZXIgVGVzdDIxZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIxZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIxZAAMd2F0Y2hsaXN0X2lkYe4.OALg_k0K4kFbxlwrk2_wILKz3Ojtx5g-lwqsQWUvTHE; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvWZkx3kLOwAACCh + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "watchlist_names": [], + "personal_tx_tag": { + "label": "MyName" + }, + "personal_tags": [], + "common_tags": [] + } + +# Group BlockScoutWeb.Account.Api.V1.UserController +## BlockScoutWeb.Account.Api.V1.UserController [/api/account/v1/user/tags/transaction/65] +### BlockScoutWeb.Account.Api.V1.UserController update_tag_transaction [PUT /api/account/v1/user/tags/transaction/{id}] + + + + ++ Parameters + + id: `65` + id: 65 + + ++ Request Edit private transaction tag +**PUT**  `/api/account/v1/user/tags/transaction/65` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000001", + "name": "name1" + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyOGQABWVtYWlsbQAAABp0ZXN0X3VzZXItOUBibG9ja3Njb3V0LmNvbWQAAmlkYeFkAARuYW1lbQAAAApVc2VyIFRlc3Q4ZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjhkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwOGQADHdhdGNobGlzdF9pZGHh.CybEtb6DRCGrUsJ2qnEERIZwD6pRhUfUSwFugOLA9kg; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gunOuMiiGZsAAASi + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000001", + "name": "name1", + "id": 65 + } +### BlockScoutWeb.Account.Api.V1.UserController tags_transaction [GET /api/account/v1/user/tags/transaction] + + + + + ++ Request Get private transactions tags +**GET**  `/api/account/v1/user/tags/transaction` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTRkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTE4QGJsb2Nrc2NvdXQuY29tZAACaWRh52QABG5hbWVtAAAAC1VzZXIgVGVzdDE0ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE0ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE0ZAAMd2F0Y2hsaXN0X2lkYec.CDHGLjvSgiNStdl55exaXgWiuAWfGw65IX3_vK5h5dU; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gu9MDrtpGp0AABnh + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + [ + { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000004", + "name": "name2", + "id": 68 + }, + { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000003", + "name": "name1", + "id": 67 + }, + { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000002", + "name": "name0", + "id": 66 + } + ] +### BlockScoutWeb.Account.Api.V1.UserController delete_tag_transaction [DELETE /api/account/v1/user/tags/transaction/{id}] + + + + ++ Parameters + + id: `69` + id: 69 + + ++ Request Delete private transaction tag +**DELETE**  `/api/account/v1/user/tags/transaction/69` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTZkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTIwQGJsb2Nrc2NvdXQuY29tZAACaWRh6WQABG5hbWVtAAAAC1VzZXIgVGVzdDE2ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE2ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE2ZAAMd2F0Y2hsaXN0X2lkYek.LsY5H_7VsGeJ-WoDRIReTCTZmPTJNCTjme7ZshEuEpQ; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvGE13QyfYIAAByB + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "message": "OK" + } +### BlockScoutWeb.Account.Api.V1.UserController create_watchlist [POST /api/account/v1/user/watchlist] + + + + + ++ Request Add address to watch list +**POST**  `/api/account/v1/user/watchlist` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "notification_settings": { + "native": { + "outcoming": true, + "incoming": true + }, + "ERC-721": { + "outcoming": true, + "incoming": false + }, + "ERC-20": { + "outcoming": true, + "incoming": true + } + }, + "notification_methods": { + "email": true + }, + "name": "test16", + "address_hash": "0x0000000000000000000000000000000000000011" + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyM2QABWVtYWlsbQAAABp0ZXN0X3VzZXItM0BibG9ja3Njb3V0LmNvbWQAAmlkYdxkAARuYW1lbQAAAApVc2VyIFRlc3QzZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjNkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwM2QADHdhdGNobGlzdF9pZGHc.ujumccFj98DtF6Rf_O0i31DGgry0eHmykzCC1xvjVfY; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gt-4UWemyBYAABJB + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "notification_settings": { + "native": { + "outcoming": true, + "incoming": true + }, + "ERC-721": { + "outcoming": true, + "incoming": false + }, + "ERC-20": { + "outcoming": true, + "incoming": true + } + }, + "notification_methods": { + "email": true + }, + "name": "test16", + "id": 75, + "exchange_rate": null, + "address_hash": "0x0000000000000000000000000000000000000011", + "address_balance": null + } +### BlockScoutWeb.Account.Api.V1.UserController watchlist [GET /api/account/v1/user/watchlist] + + + + + ++ Request Get addresses from watchlists +**GET**  `/api/account/v1/user/watchlist` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyM2QABWVtYWlsbQAAABp0ZXN0X3VzZXItM0BibG9ja3Njb3V0LmNvbWQAAmlkYdxkAARuYW1lbQAAAApVc2VyIFRlc3QzZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjNkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwM2QADHdhdGNobGlzdF9pZGHc.ujumccFj98DtF6Rf_O0i31DGgry0eHmykzCC1xvjVfY; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1guCYRuamyBYAAANj + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + [ + { + "notification_settings": { + "native": { + "outcoming": true, + "incoming": false + }, + "ERC-721": { + "outcoming": true, + "incoming": false + }, + "ERC-20": { + "outcoming": false, + "incoming": true + } + }, + "notification_methods": { + "email": false + }, + "name": "test17", + "id": 76, + "exchange_rate": null, + "address_hash": "0x0000000000000000000000000000000000000012", + "address_balance": null + }, + { + "notification_settings": { + "native": { + "outcoming": true, + "incoming": true + }, + "ERC-721": { + "outcoming": true, + "incoming": false + }, + "ERC-20": { + "outcoming": true, + "incoming": true + } + }, + "notification_methods": { + "email": true + }, + "name": "test16", + "id": 75, + "exchange_rate": null, + "address_hash": "0x0000000000000000000000000000000000000011", + "address_balance": null + } + ] +### BlockScoutWeb.Account.Api.V1.UserController delete_watchlist [DELETE /api/account/v1/user/watchlist/{id}] + + + + ++ Parameters + + id: `82` + id: 82 + + ++ Request Delete address from watchlist by id +**DELETE**  `/api/account/v1/user/watchlist/82` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTlkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTIzQGJsb2Nrc2NvdXQuY29tZAACaWRh7GQABG5hbWVtAAAAC1VzZXIgVGVzdDE5ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE5ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE5ZAAMd2F0Y2hsaXN0X2lkYew.slyWFXgdvd78Pwp3lyrU5tmgCtF7VNIPHxnFkfAQ-YQ; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvR861_DWHcAAAhC + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "message": "OK" + } +### BlockScoutWeb.Account.Api.V1.UserController update_watchlist [PUT /api/account/v1/user/watchlist/{id}] + + + + ++ Parameters + + id: `80` + id: 80 + + ++ Request Edit watchlist address +**PUT**  `/api/account/v1/user/watchlist/80` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "notification_settings": { + "native": { + "outcoming": false, + "incoming": false + }, + "ERC-721": { + "outcoming": true, + "incoming": true + }, + "ERC-20": { + "outcoming": false, + "incoming": false + } + }, + "notification_methods": { + "email": false + }, + "name": "test21", + "address_hash": "0x0000000000000000000000000000000000000023" + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyN2QABWVtYWlsbQAAABp0ZXN0X3VzZXItOEBibG9ja3Njb3V0LmNvbWQAAmlkYeBkAARuYW1lbQAAAApVc2VyIFRlc3Q3ZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjdkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwN2QADHdhdGNobGlzdF9pZGHg.2IaE2naK_o4H_guVwcTb0JZIp2hs2c4fvtASxCmIWHM; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gugvkSj5PXEAAANi + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "notification_settings": { + "native": { + "outcoming": false, + "incoming": false + }, + "ERC-721": { + "outcoming": true, + "incoming": true + }, + "ERC-20": { + "outcoming": false, + "incoming": false + } + }, + "notification_methods": { + "email": false + }, + "name": "test21", + "id": 80, + "exchange_rate": null, + "address_hash": "0x0000000000000000000000000000000000000023", + "address_balance": null + } +### BlockScoutWeb.Account.Api.V1.UserController create_watchlist [POST /api/account/v1/user/watchlist] + + + + + ++ Request Example of error on creating watchlist address +**POST**  `/api/account/v1/user/watchlist` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "notification_settings": { + "native": { + "outcoming": false, + "incoming": false + }, + "ERC-721": { + "outcoming": true, + "incoming": false + }, + "ERC-20": { + "outcoming": true, + "incoming": true + } + }, + "notification_methods": { + "email": false + }, + "name": "test18", + "address_hash": "0x0000000000000000000000000000000000000013" + } + ++ Response 422 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyNGQABWVtYWlsbQAAABp0ZXN0X3VzZXItNEBibG9ja3Njb3V0LmNvbWQAAmlkYd1kAARuYW1lbQAAAApVc2VyIFRlc3Q0ZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjRkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwNGQADHdhdGNobGlzdF9pZGHd.jCNAb9dB6WGIZv9wIVL9tpikIPr056ChTYcDeSWdnG4; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1guGsUmFGrIUAABMB + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "errors": { + "watchlist_id": [ + "Address already added to the watch list" + ] + } + } +### BlockScoutWeb.Account.Api.V1.UserController update_watchlist [PUT /api/account/v1/user/watchlist/{id}] + + + + ++ Parameters + + id: `79` + id: 79 + + ++ Request Example of error on editing watchlist address +**PUT**  `/api/account/v1/user/watchlist/79` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "notification_settings": { + "native": { + "outcoming": false, + "incoming": false + }, + "ERC-721": { + "outcoming": true, + "incoming": false + }, + "ERC-20": { + "outcoming": true, + "incoming": true + } + }, + "notification_methods": { + "email": false + }, + "name": "test18", + "address_hash": "0x0000000000000000000000000000000000000013" + } + ++ Response 422 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyNGQABWVtYWlsbQAAABp0ZXN0X3VzZXItNEBibG9ja3Njb3V0LmNvbWQAAmlkYd1kAARuYW1lbQAAAApVc2VyIFRlc3Q0ZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjRkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwNGQADHdhdGNobGlzdF9pZGHd.jCNAb9dB6WGIZv9wIVL9tpikIPr056ChTYcDeSWdnG4; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1guIKk8ZGrIUAABNB + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "errors": { + "watchlist_id": [ + "Address already added to the watch list" + ] + } + } +### BlockScoutWeb.Account.Api.V1.UserController create_api_key [POST /api/account/v1/user/api_keys] + + + + + ++ Request Add api key +**POST**  `/api/account/v1/user/api_keys` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "name": "test" + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjBkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI0QGJsb2Nrc2NvdXQuY29tZAACaWRh7WQABG5hbWVtAAAAC1VzZXIgVGVzdDIwZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIwZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIwZAAMd2F0Y2hsaXN0X2lkYe0.hIRgUayy_NKWZARAIxD2-TPy3PaP5kQSHuKGOLxxwz0; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvTjkbFZ2PwAACBB + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "name": "test", + "api_key": "05b65dfd-0d08-4aa1-b22b-95e3fc8a55e5" + } + + ++ Request Example of error on creating api key +**POST**  `/api/account/v1/user/api_keys` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "name": "test" + } + ++ Response 422 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTVkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTE5QGJsb2Nrc2NvdXQuY29tZAACaWRh6GQABG5hbWVtAAAAC1VzZXIgVGVzdDE1ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE1ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE1ZAAMd2F0Y2hsaXN0X2lkYeg.M4suuaCnSncg5sgQepwyEGrDqMcSle2BvUjGq5qw0Q8; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gu_KXoEIU2IAABrh + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "errors": { + "name": [ + "Max 3 keys per account" + ] + } + } +### BlockScoutWeb.Account.Api.V1.UserController api_keys [GET /api/account/v1/user/api_keys] + + + + + ++ Request Get api keys list +**GET**  `/api/account/v1/user/api_keys` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTVkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTE5QGJsb2Nrc2NvdXQuY29tZAACaWRh6GQABG5hbWVtAAAAC1VzZXIgVGVzdDE1ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE1ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE1ZAAMd2F0Y2hsaXN0X2lkYeg.M4suuaCnSncg5sgQepwyEGrDqMcSle2BvUjGq5qw0Q8; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gu_ZqjIIU2IAABsB + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + [ + { + "name": "test", + "api_key": "3d07da0e-428e-4410-bc54-43ab544e20f4" + }, + { + "name": "test", + "api_key": "92036fb5-a22a-418d-ac3a-0415e731d55a" + }, + { + "name": "test", + "api_key": "0262ffe5-6d6a-4f79-8444-479e8be85d0e" + } + ] +### BlockScoutWeb.Account.Api.V1.UserController update_api_key [PUT /api/account/v1/user/api_keys/{api_key}] + + + + ++ Parameters + + api_key: `6bcec727-d945-4785-99b6-c6094bbf0452` + api_key: 6bcec727-d945-4785-99b6-c6094bbf0452 + + ++ Request Edit api key +**PUT**  `/api/account/v1/user/api_keys/6bcec727-d945-4785-99b6-c6094bbf0452` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "name": "test_1" + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMGQABWVtYWlsbQAAABp0ZXN0X3VzZXItMEBibG9ja3Njb3V0LmNvbWQAAmlkYdlkAARuYW1lbQAAAApVc2VyIFRlc3QwZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjBkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwMGQADHdhdGNobGlzdF9pZGHZ.eNhiwGmTdeNAVqQGfVgtac9gGTsoXnysChIBQN75BQk; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gtunEs8BJMYAABCE + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "name": "test_1", + "api_key": "6bcec727-d945-4785-99b6-c6094bbf0452" + } +### BlockScoutWeb.Account.Api.V1.UserController delete_api_key [DELETE /api/account/v1/user/api_keys/{api_key}] + + + + ++ Parameters + + api_key: `0e26955f-5431-4652-84da-d08aded97a28` + api_key: 0e26955f-5431-4652-84da-d08aded97a28 + + ++ Request Delete api key +**DELETE**  `/api/account/v1/user/api_keys/0e26955f-5431-4652-84da-d08aded97a28` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMThkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTIyQGJsb2Nrc2NvdXQuY29tZAACaWRh62QABG5hbWVtAAAAC1VzZXIgVGVzdDE4ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE4ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE4ZAAMd2F0Y2hsaXN0X2lkYes.NYp71-Be73f-HTquq2QWWCa70c169Rd9GXDOOSCdC34; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvMpP3rEvHcAAAei + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "message": "OK" + } +### BlockScoutWeb.Account.Api.V1.UserController create_custom_abi [POST /api/account/v1/user/custom_abis] + + + + + ++ Request Add custom abi +**POST**  `/api/account/v1/user/custom_abis` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "name": "test26", + "contract_address_hash": "0x0000000000000000000000000000000000000089", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjNkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTM3QGJsb2Nrc2NvdXQuY29tZAACaWRh8GQABG5hbWVtAAAAC1VzZXIgVGVzdDIzZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIzZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIzZAAMd2F0Y2hsaXN0X2lkYfA.EgDkDw8R9zBMVjqsTcEWr77klYQVx6QOCcxXyN7EAqg; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvk62Sj0d-gAAArC + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "name": "test26", + "id": 161, + "contract_address_hash": "0x0000000000000000000000000000000000000089", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + } + + ++ Request Example of error on creating custom abi +**POST**  `/api/account/v1/user/custom_abis` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "name": "test15", + "contract_address_hash": "0x0000000000000000000000000000000000000010", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + } + ++ Response 422 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMWQABWVtYWlsbQAAABp0ZXN0X3VzZXItMUBibG9ja3Njb3V0LmNvbWQAAmlkYdpkAARuYW1lbQAAAApVc2VyIFRlc3QxZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjFkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwMWQADHdhdGNobGlzdF9pZGHa.ynGrz6gad7RIkTh1lopco9xXNhiI-y6Bm6ecAnv3Usg; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gt5BIL0fpssAABCB + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "errors": { + "name": [ + "Max 15 ABIs per account" + ] + } + } +### BlockScoutWeb.Account.Api.V1.UserController custom_abis [GET /api/account/v1/user/custom_abis] + + + + + ++ Request Get custom abis list +**GET**  `/api/account/v1/user/custom_abis` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMWQABWVtYWlsbQAAABp0ZXN0X3VzZXItMUBibG9ja3Njb3V0LmNvbWQAAmlkYdpkAARuYW1lbQAAAApVc2VyIFRlc3QxZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjFkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwMWQADHdhdGNobGlzdF9pZGHa.ynGrz6gad7RIkTh1lopco9xXNhiI-y6Bm6ecAnv3Usg; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gt5U3pwfpssAABCh + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + [ + { + "name": "test14", + "id": 159, + "contract_address_hash": "0x000000000000000000000000000000000000000f", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test13", + "id": 158, + "contract_address_hash": "0x000000000000000000000000000000000000000e", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test12", + "id": 157, + "contract_address_hash": "0x000000000000000000000000000000000000000d", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test11", + "id": 156, + "contract_address_hash": "0x000000000000000000000000000000000000000c", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test10", + "id": 155, + "contract_address_hash": "0x000000000000000000000000000000000000000b", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test9", + "id": 154, + "contract_address_hash": "0x000000000000000000000000000000000000000a", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test8", + "id": 153, + "contract_address_hash": "0x0000000000000000000000000000000000000009", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test7", + "id": 152, + "contract_address_hash": "0x0000000000000000000000000000000000000008", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test6", + "id": 151, + "contract_address_hash": "0x0000000000000000000000000000000000000007", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test5", + "id": 150, + "contract_address_hash": "0x0000000000000000000000000000000000000006", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test4", + "id": 149, + "contract_address_hash": "0x0000000000000000000000000000000000000005", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test3", + "id": 148, + "contract_address_hash": "0x0000000000000000000000000000000000000004", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test2", + "id": 147, + "contract_address_hash": "0x0000000000000000000000000000000000000003", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test1", + "id": 146, + "contract_address_hash": "0x0000000000000000000000000000000000000002", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test0", + "id": 145, + "contract_address_hash": "0x0000000000000000000000000000000000000001", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + } + ] +### BlockScoutWeb.Account.Api.V1.UserController update_custom_abi [PUT /api/account/v1/user/custom_abis/{id}] + + + + ++ Parameters + + id: `160` + id: 160 + + ++ Request Edit custom abi +**PUT**  `/api/account/v1/user/custom_abis/160` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "name": "test23", + "contract_address_hash": "0x0000000000000000000000000000000000000046", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTNkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTE3QGJsb2Nrc2NvdXQuY29tZAACaWRh5mQABG5hbWVtAAAAC1VzZXIgVGVzdDEzZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjEzZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDEzZAAMd2F0Y2hsaXN0X2lkYeY.sl0nMtxBkMGt3aK7ohM3AYMcNEI-l37Xvqvl9qZ2Tso; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gu0y0bFQlB0AAAbi + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "name": "test23", + "id": 160, + "contract_address_hash": "0x0000000000000000000000000000000000000046", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + } +### BlockScoutWeb.Account.Api.V1.UserController delete_custom_abi [DELETE /api/account/v1/user/custom_abis/{id}] + + + + ++ Parameters + + id: `162` + id: 162 + + ++ Request Delete custom abi +**DELETE**  `/api/account/v1/user/custom_abis/162` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjRkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTM4QGJsb2Nrc2NvdXQuY29tZAACaWRh8WQABG5hbWVtAAAAC1VzZXIgVGVzdDI0ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjI0ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDI0ZAAMd2F0Y2hsaXN0X2lkYfE.i0XOrEfBULTfd08Ig4nhy_veB1sWxl2UWYT9kkveABw; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvnkpEhLN3QAACMB + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "message": "OK" + } +### BlockScoutWeb.Account.Api.V1.UserController create_public_tags_request [POST /api/account/v1/user/public_tags] + + + + + ++ Request Submit request to add a public tag +**POST**  `/api/account/v1/user/public_tags` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "website": "website3", + "tags": "Tag5;Tag6", + "is_owner": false, + "full_name": "full name3", + "email": "test_user-16@blockscout.com", + "company": "company3", + "addresses": [ + "0x000000000000000000000000000000000000003b", + "0x000000000000000000000000000000000000003c", + "0x000000000000000000000000000000000000003d", + "0x000000000000000000000000000000000000003e", + "0x000000000000000000000000000000000000003f", + "0x0000000000000000000000000000000000000040", + "0x0000000000000000000000000000000000000041", + "0x0000000000000000000000000000000000000042", + "0x0000000000000000000000000000000000000043", + "0x0000000000000000000000000000000000000044" + ], + "additional_comment": "additional_comment3" + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTJkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTE1QGJsb2Nrc2NvdXQuY29tZAACaWRh5WQABG5hbWVtAAAAC1VzZXIgVGVzdDEyZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjEyZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDEyZAAMd2F0Y2hsaXN0X2lkYeU.8B0VERlCeTBlp1w0Zys_ZGaVIKj0VYi6pV2wMnCjeac; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1guxmyw_F-rUAAATj + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "website": "website3", + "tags": "Tag5;Tag6", + "submission_date": "2022-09-03T21:02:22.651943Z", + "is_owner": false, + "id": 146, + "full_name": "full name3", + "email": "test_user-16@blockscout.com", + "company": "company3", + "addresses": [ + "0x000000000000000000000000000000000000003b", + "0x000000000000000000000000000000000000003c", + "0x000000000000000000000000000000000000003d", + "0x000000000000000000000000000000000000003e", + "0x000000000000000000000000000000000000003f", + "0x0000000000000000000000000000000000000040", + "0x0000000000000000000000000000000000000041", + "0x0000000000000000000000000000000000000042", + "0x0000000000000000000000000000000000000043", + "0x0000000000000000000000000000000000000044" + ], + "additional_comment": "additional_comment3" + } +### BlockScoutWeb.Account.Api.V1.UserController public_tags_requests [GET /api/account/v1/user/public_tags] + + + + + ++ Request Get list of requests to add a public tag +**GET**  `/api/account/v1/user/public_tags` + + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjJkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI2QGJsb2Nrc2NvdXQuY29tZAACaWRh72QABG5hbWVtAAAAC1VzZXIgVGVzdDIyZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIyZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIyZAAMd2F0Y2hsaXN0X2lkYe8.oZY96LW6ZLfw1aK-C5TYkrK2GRNQEJCapnUSkd5OjXU; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvdQvQ8r6iIAAAki + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + [ + { + "website": "website13", + "tags": "Tag18;Tag19", + "submission_date": "2022-09-03T21:02:23.000000Z", + "is_owner": false, + "id": 156, + "full_name": "full name13", + "email": "test_user-36@blockscout.com", + "company": "company13", + "addresses": [ + "0x0000000000000000000000000000000000000084", + "0x0000000000000000000000000000000000000085", + "0x0000000000000000000000000000000000000086", + "0x0000000000000000000000000000000000000087", + "0x0000000000000000000000000000000000000088" + ], + "additional_comment": "additional_comment13" + }, + { + "website": "website12", + "tags": "Tag17", + "submission_date": "2022-09-03T21:02:23.000000Z", + "is_owner": true, + "id": 155, + "full_name": "full name12", + "email": "test_user-35@blockscout.com", + "company": "company12", + "addresses": [ + "0x0000000000000000000000000000000000000083" + ], + "additional_comment": "additional_comment12" + }, + { + "website": "website11", + "tags": "Tag16", + "submission_date": "2022-09-03T21:02:23.000000Z", + "is_owner": false, + "id": 154, + "full_name": "full name11", + "email": "test_user-34@blockscout.com", + "company": "company11", + "addresses": [ + "0x000000000000000000000000000000000000007b", + "0x000000000000000000000000000000000000007c", + "0x000000000000000000000000000000000000007d", + "0x000000000000000000000000000000000000007e", + "0x000000000000000000000000000000000000007f", + "0x0000000000000000000000000000000000000080", + "0x0000000000000000000000000000000000000081", + "0x0000000000000000000000000000000000000082" + ], + "additional_comment": "additional_comment11" + }, + { + "website": "website10", + "tags": "Tag15", + "submission_date": "2022-09-03T21:02:23.000000Z", + "is_owner": false, + "id": 153, + "full_name": "full name10", + "email": "test_user-33@blockscout.com", + "company": "company10", + "addresses": [ + "0x0000000000000000000000000000000000000073", + "0x0000000000000000000000000000000000000074", + "0x0000000000000000000000000000000000000075", + "0x0000000000000000000000000000000000000076", + "0x0000000000000000000000000000000000000077", + "0x0000000000000000000000000000000000000078", + "0x0000000000000000000000000000000000000079", + "0x000000000000000000000000000000000000007a" + ], + "additional_comment": "additional_comment10" + }, + { + "website": "website9", + "tags": "Tag14", + "submission_date": "2022-09-03T21:02:23.000000Z", + "is_owner": false, + "id": 152, + "full_name": "full name9", + "email": "test_user-32@blockscout.com", + "company": "company9", + "addresses": [ + "0x000000000000000000000000000000000000006d", + "0x000000000000000000000000000000000000006e", + "0x000000000000000000000000000000000000006f", + "0x0000000000000000000000000000000000000070", + "0x0000000000000000000000000000000000000071", + "0x0000000000000000000000000000000000000072" + ], + "additional_comment": "additional_comment9" + }, + { + "website": "website8", + "tags": "Tag13", + "submission_date": "2022-09-03T21:02:23.000000Z", + "is_owner": false, + "id": 151, + "full_name": "full name8", + "email": "test_user-31@blockscout.com", + "company": "company8", + "addresses": [ + "0x0000000000000000000000000000000000000064", + "0x0000000000000000000000000000000000000065", + "0x0000000000000000000000000000000000000066", + "0x0000000000000000000000000000000000000067", + "0x0000000000000000000000000000000000000068", + "0x0000000000000000000000000000000000000069", + "0x000000000000000000000000000000000000006a", + "0x000000000000000000000000000000000000006b", + "0x000000000000000000000000000000000000006c" + ], + "additional_comment": "additional_comment8" + }, + { + "website": "website7", + "tags": "Tag11;Tag12", + "submission_date": "2022-09-03T21:02:23.000000Z", + "is_owner": true, + "id": 150, + "full_name": "full name7", + "email": "test_user-30@blockscout.com", + "company": "company7", + "addresses": [ + "0x0000000000000000000000000000000000000063" + ], + "additional_comment": "additional_comment7" + }, + { + "website": "website6", + "tags": "Tag9;Tag10", + "submission_date": "2022-09-03T21:02:23.000000Z", + "is_owner": false, + "id": 149, + "full_name": "full name6", + "email": "test_user-29@blockscout.com", + "company": "company6", + "addresses": [ + "0x0000000000000000000000000000000000000060", + "0x0000000000000000000000000000000000000061", + "0x0000000000000000000000000000000000000062" + ], + "additional_comment": "additional_comment6" + }, + { + "website": "website5", + "tags": "Tag8", + "submission_date": "2022-09-03T21:02:23.000000Z", + "is_owner": true, + "id": 148, + "full_name": "full name5", + "email": "test_user-28@blockscout.com", + "company": "company5", + "addresses": [ + "0x000000000000000000000000000000000000005e", + "0x000000000000000000000000000000000000005f" + ], + "additional_comment": "additional_comment5" + }, + { + "website": "website4", + "tags": "Tag7", + "submission_date": "2022-09-03T21:02:23.000000Z", + "is_owner": false, + "id": 147, + "full_name": "full name4", + "email": "test_user-27@blockscout.com", + "company": "company4", + "addresses": [ + "0x000000000000000000000000000000000000005b", + "0x000000000000000000000000000000000000005c", + "0x000000000000000000000000000000000000005d" + ], + "additional_comment": "additional_comment4" + } + ] +### BlockScoutWeb.Account.Api.V1.UserController delete_public_tags_request [DELETE /api/account/v1/user/public_tags/{id}] + + + + ++ Parameters + + id: `156` + id: 156 + + ++ Request Delete public tags request +**DELETE**  `/api/account/v1/user/public_tags/156` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "remove_reason": "reason" + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjJkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI2QGJsb2Nrc2NvdXQuY29tZAACaWRh72QABG5hbWVtAAAAC1VzZXIgVGVzdDIyZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIyZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIyZAAMd2F0Y2hsaXN0X2lkYe8.oZY96LW6ZLfw1aK-C5TYkrK2GRNQEJCapnUSkd5OjXU; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1gvdm8H0r6iIAAAlC + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "message": "OK" + } +### BlockScoutWeb.Account.Api.V1.UserController update_public_tags_request [PUT /api/account/v1/user/public_tags/{id}] + + + + ++ Parameters + + id: `145` + id: 145 + + ++ Request Edit request to add a public tag +**PUT**  `/api/account/v1/user/public_tags/145` + + + Headers + + content-type: multipart/mixed; boundary=plug_conn_test + + Body + + { + "website": "website2", + "tags": "Tag3;Tag4", + "is_owner": false, + "full_name": "full name2", + "email": "test_user-12@blockscout.com", + "company": "company2", + "addresses": [ + "0x000000000000000000000000000000000000002f", + "0x0000000000000000000000000000000000000030", + "0x0000000000000000000000000000000000000031", + "0x0000000000000000000000000000000000000032", + "0x0000000000000000000000000000000000000033", + "0x0000000000000000000000000000000000000034", + "0x0000000000000000000000000000000000000035", + "0x0000000000000000000000000000000000000036", + "0x0000000000000000000000000000000000000037" + ], + "additional_comment": "additional_comment2" + } + ++ Response 200 + + + Headers + + set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyOWQABWVtYWlsbQAAABt0ZXN0X3VzZXItMTBAYmxvY2tzY291dC5jb21kAAJpZGHiZAAEbmFtZW0AAAAKVXNlciBUZXN0OWQACG5pY2tuYW1lbQAAAAp0ZXN0X3VzZXI5ZAADdWlkbQAAAA9ibG9ja3Njb3V0fDAwMDlkAAx3YXRjaGxpc3RfaWRh4g.cM2caeO_bvTyojrTAKD7Tt4WEPEIsHwTMmWkTEVgSLo; path=/; HttpOnly + content-type: application/json; charset=utf-8 + cache-control: max-age=0, private, must-revalidate + x-request-id: FxF1guqVaODqqc8AAAUi + access-control-allow-credentials: true + access-control-allow-origin: * + access-control-expose-headers: + + Body + + { + "website": "website2", + "tags": "Tag3;Tag4", + "submission_date": "2022-09-03T21:02:23.000000Z", + "is_owner": false, + "id": 145, + "full_name": "full name2", + "email": "test_user-12@blockscout.com", + "company": "company2", + "addresses": [ + "0x000000000000000000000000000000000000002f", + "0x0000000000000000000000000000000000000030", + "0x0000000000000000000000000000000000000031", + "0x0000000000000000000000000000000000000032", + "0x0000000000000000000000000000000000000033", + "0x0000000000000000000000000000000000000034", + "0x0000000000000000000000000000000000000035", + "0x0000000000000000000000000000000000000036", + "0x0000000000000000000000000000000000000037" + ], + "additional_comment": "additional_comment2" + } + diff --git a/apps/block_scout_web/API.md b/apps/block_scout_web/API.md new file mode 100644 index 0000000..afc68c1 --- /dev/null +++ b/apps/block_scout_web/API.md @@ -0,0 +1,2227 @@ +# API Documentation + + * [BlockScoutWeb.Account.Api.V1.UserController](#blockscoutweb-account-api-v1-usercontroller) + * [info](#blockscoutweb-account-api-v1-usercontroller-info) + * [create_tag_address](#blockscoutweb-account-api-v1-usercontroller-create_tag_address) + * [BlockScoutWeb.Account.Api.V1.TagsController](#blockscoutweb-account-api-v1-tagscontroller) + * [tags_address](#blockscoutweb-account-api-v1-tagscontroller-tags_address) + * [BlockScoutWeb.Account.Api.V1.UserController](#blockscoutweb-account-api-v1-usercontroller) + * [update_tag_address](#blockscoutweb-account-api-v1-usercontroller-update_tag_address) + * [tags_address](#blockscoutweb-account-api-v1-usercontroller-tags_address) + * [delete_tag_address](#blockscoutweb-account-api-v1-usercontroller-delete_tag_address) + * [create_tag_transaction](#blockscoutweb-account-api-v1-usercontroller-create_tag_transaction) + * [BlockScoutWeb.Account.Api.V1.TagsController](#blockscoutweb-account-api-v1-tagscontroller) + * [tags_transaction](#blockscoutweb-account-api-v1-tagscontroller-tags_transaction) + * [BlockScoutWeb.Account.Api.V1.UserController](#blockscoutweb-account-api-v1-usercontroller) + * [update_tag_transaction](#blockscoutweb-account-api-v1-usercontroller-update_tag_transaction) + * [tags_transaction](#blockscoutweb-account-api-v1-usercontroller-tags_transaction) + * [delete_tag_transaction](#blockscoutweb-account-api-v1-usercontroller-delete_tag_transaction) + * [create_watchlist](#blockscoutweb-account-api-v1-usercontroller-create_watchlist) + * [watchlist](#blockscoutweb-account-api-v1-usercontroller-watchlist) + * [delete_watchlist](#blockscoutweb-account-api-v1-usercontroller-delete_watchlist) + * [update_watchlist](#blockscoutweb-account-api-v1-usercontroller-update_watchlist) + * [create_watchlist](#blockscoutweb-account-api-v1-usercontroller-create_watchlist) + * [update_watchlist](#blockscoutweb-account-api-v1-usercontroller-update_watchlist) + * [create_api_key](#blockscoutweb-account-api-v1-usercontroller-create_api_key) + * [api_keys](#blockscoutweb-account-api-v1-usercontroller-api_keys) + * [update_api_key](#blockscoutweb-account-api-v1-usercontroller-update_api_key) + * [delete_api_key](#blockscoutweb-account-api-v1-usercontroller-delete_api_key) + * [create_custom_abi](#blockscoutweb-account-api-v1-usercontroller-create_custom_abi) + * [custom_abis](#blockscoutweb-account-api-v1-usercontroller-custom_abis) + * [update_custom_abi](#blockscoutweb-account-api-v1-usercontroller-update_custom_abi) + * [delete_custom_abi](#blockscoutweb-account-api-v1-usercontroller-delete_custom_abi) + * [create_public_tags_request](#blockscoutweb-account-api-v1-usercontroller-create_public_tags_request) + * [public_tags_requests](#blockscoutweb-account-api-v1-usercontroller-public_tags_requests) + * [delete_public_tags_request](#blockscoutweb-account-api-v1-usercontroller-delete_public_tags_request) + * [update_public_tags_request](#blockscoutweb-account-api-v1-usercontroller-update_public_tags_request) + +## BlockScoutWeb.Account.Api.V1.UserController +### info +#### Get info about user + +##### Request +* __Method:__ GET +* __Path:__ /api/account/v1/user/info + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyNGQABWVtYWlsbQAAABp0ZXN0X3VzZXItNEBibG9ja3Njb3V0LmNvbWQAAmlkYcRkAARuYW1lbQAAAApVc2VyIFRlc3Q0ZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjRkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwNGQADHdhdGNobGlzdF9pZGHE.Ovcc2Vzzv4fhFzmirtQjJ06gcqQwUHMMlju7VX24fyo; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y1_QfU9-YaIAAGdh +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "nickname": "test_user4", + "name": "User Test4", + "email": "test_user-4@blockscout.com", + "avatar": "https://example.com/avatar/test_user4" +} +``` + +### create_tag_address +#### Add private address tag + +##### Request +* __Method:__ POST +* __Path:__ /api/account/v1/user/tags/address +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "name": "MyName", + "address_hash": "0x3e9ac8f16c92bc4f093357933b5befbf1e16987b" +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMThkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTIyQGJsb2Nrc2NvdXQuY29tZAACaWRh0mQABG5hbWVtAAAAC1VzZXIgVGVzdDE4ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE4ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE4ZAAMd2F0Y2hsaXN0X2lkYdI.tFFJ387fBBdBFuMzzeaWcMTeapzMHnbuEfnqTdq5lJ8; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3ALw8xSCMAAAHAC +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "name": "MyName", + "id": 61, + "address_hash": "0x3e9ac8f16c92bc4f093357933b5befbf1e16987b" +} +``` + +## BlockScoutWeb.Account.Api.V1.TagsController +### tags_address +#### Get tags for address + +##### Request +* __Method:__ GET +* __Path:__ /api/account/v1/tags/address/0x3e9ac8f16c92bc4f093357933b5befbf1e16987b + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMThkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTIyQGJsb2Nrc2NvdXQuY29tZAACaWRh0mQABG5hbWVtAAAAC1VzZXIgVGVzdDE4ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE4ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE4ZAAMd2F0Y2hsaXN0X2lkYdI.tFFJ387fBBdBFuMzzeaWcMTeapzMHnbuEfnqTdq5lJ8; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3BIWjdSCMAAAG4B +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "watchlist_names": [], + "personal_tags": [ + { + "label": "MyName", + "display_name": "MyName", + "address_hash": "0x3e9ac8f16c92bc4f093357933b5befbf1e16987b" + } + ], + "common_tags": [] +} +``` + +## BlockScoutWeb.Account.Api.V1.UserController +### update_tag_address +#### Edit private address tag + +##### Request +* __Method:__ PUT +* __Path:__ /api/account/v1/user/tags/address/57 +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "name": "name3", + "address_hash": "0x0000000000000000000000000000000000000016" +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyN2QABWVtYWlsbQAAABt0ZXN0X3VzZXItMTBAYmxvY2tzY291dC5jb21kAAJpZGHHZAAEbmFtZW0AAAAKVXNlciBUZXN0N2QACG5pY2tuYW1lbQAAAAp0ZXN0X3VzZXI3ZAADdWlkbQAAAA9ibG9ja3Njb3V0fDAwMDdkAAx3YXRjaGxpc3RfaWRhxw.Bn03yTZrlP0m6amYLQVeI-pvhvUf1F6d9SGAkDTLEck; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2IdgOjzsTkAAGYC +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "name": "name3", + "id": 57, + "address_hash": "0x0000000000000000000000000000000000000016" +} +``` + +### tags_address +#### Get private addresses tags + +##### Request +* __Method:__ GET +* __Path:__ /api/account/v1/user/tags/address + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTVkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTE5QGJsb2Nrc2NvdXQuY29tZAACaWRhz2QABG5hbWVtAAAAC1VzZXIgVGVzdDE1ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE1ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE1ZAAMd2F0Y2hsaXN0X2lkYc8.AoYBq7uUH9JOt11vL4-71qtsXMzpPDFsx8BV97n1Y-o; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2ynKDFWAsYAAG5C +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +[ + { + "name": "name2", + "id": 60, + "address_hash": "0x000000000000000000000000000000000000003f" + }, + { + "name": "name1", + "id": 59, + "address_hash": "0x000000000000000000000000000000000000003e" + }, + { + "name": "name0", + "id": 58, + "address_hash": "0x000000000000000000000000000000000000003d" + } +] +``` + +### delete_tag_address +#### Delete private address tag + +##### Request +* __Method:__ DELETE +* __Path:__ /api/account/v1/user/tags/address/62 + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjRkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTM4QGJsb2Nrc2NvdXQuY29tZAACaWRh2GQABG5hbWVtAAAAC1VzZXIgVGVzdDI0ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjI0ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDI0ZAAMd2F0Y2hsaXN0X2lkYdg.x6Qf5zC5gCGQrKy2MbTqd3Xt7S_2oUYaCnO-pbZwRMI; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3biZmVZE0MAAHKC +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "message": "OK" +} +``` + +### create_tag_transaction +#### Error on try to create private transaction tag for tx does not exist + +##### Request +* __Method:__ POST +* __Path:__ /api/account/v1/user/tags/transaction +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000008", + "name": "MyName" +} +``` + +##### Response +* __Status__: 422 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTlkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTIzQGJsb2Nrc2NvdXQuY29tZAACaWRh02QABG5hbWVtAAAAC1VzZXIgVGVzdDE5ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE5ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE5ZAAMd2F0Y2hsaXN0X2lkYdM.zuwR-sOIcF7Xpo97W6G9Szzi_BPlu6Pu9_4kn7T2c10; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3DXWVBu-HUAAG6h +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "errors": { + "tx_hash": [ + "Transaction does not exist" + ] + } +} +``` + +#### Create private transaction tag + +##### Request +* __Method:__ POST +* __Path:__ /api/account/v1/user/tags/transaction +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000009", + "name": "MyName" +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTlkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTIzQGJsb2Nrc2NvdXQuY29tZAACaWRh02QABG5hbWVtAAAAC1VzZXIgVGVzdDE5ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE5ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE5ZAAMd2F0Y2hsaXN0X2lkYdM.zuwR-sOIcF7Xpo97W6G9Szzi_BPlu6Pu9_4kn7T2c10; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3EB0Ytu-HUAAG7B +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000009", + "name": "MyName", + "id": 64 +} +``` + +## BlockScoutWeb.Account.Api.V1.TagsController +### tags_transaction +#### Get tags for transaction + +##### Request +* __Method:__ GET +* __Path:__ /api/account/v1/tags/transaction/0x0000000000000000000000000000000000000000000000000000000000000009 + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTlkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTIzQGJsb2Nrc2NvdXQuY29tZAACaWRh02QABG5hbWVtAAAAC1VzZXIgVGVzdDE5ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE5ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE5ZAAMd2F0Y2hsaXN0X2lkYdM.zuwR-sOIcF7Xpo97W6G9Szzi_BPlu6Pu9_4kn7T2c10; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3Efe0tu-HUAAG7h +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "watchlist_names": [], + "personal_tx_tag": { + "label": "MyName" + }, + "personal_tags": [], + "common_tags": [] +} +``` + +## BlockScoutWeb.Account.Api.V1.UserController +### update_tag_transaction +#### Edit private transaction tag + +##### Request +* __Method:__ PUT +* __Path:__ /api/account/v1/user/tags/transaction/57 +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000001", + "name": "name1" +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMGQABWVtYWlsbQAAABp0ZXN0X3VzZXItMEBibG9ja3Njb3V0LmNvbWQAAmlkYcBkAARuYW1lbQAAAApVc2VyIFRlc3QwZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjBkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwMGQADHdhdGNobGlzdF9pZGHA.-aMP6TTEeEfxopoeChJPvTvjkSRD9_ZgaeLDlOC21gU; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y1xoENHeIlkAAGEi +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000001", + "name": "name1", + "id": 57 +} +``` + +### tags_transaction +#### Get private transactions tags + +##### Request +* __Method:__ GET +* __Path:__ /api/account/v1/user/tags/transaction + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTRkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTE4QGJsb2Nrc2NvdXQuY29tZAACaWRhzmQABG5hbWVtAAAAC1VzZXIgVGVzdDE0ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE0ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE0ZAAMd2F0Y2hsaXN0X2lkYc4.8SGhlMOY4aB444Afz1VajofmGp9YZbrfbVkZ4BTyaBI; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2tEsVp5P30AAGzi +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +[ + { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000004", + "name": "name2", + "id": 60 + }, + { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000003", + "name": "name1", + "id": 59 + }, + { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000002", + "name": "name0", + "id": 58 + } +] +``` + +### delete_tag_transaction +#### Delete private transaction tag + +##### Request +* __Method:__ DELETE +* __Path:__ /api/account/v1/user/tags/transaction/61 + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTZkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTIwQGJsb2Nrc2NvdXQuY29tZAACaWRh0GQABG5hbWVtAAAAC1VzZXIgVGVzdDE2ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE2ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE2ZAAMd2F0Y2hsaXN0X2lkYdA.YfL9L7-UIBleRbWWhHNvutNuw8Y4SadvwGFmGwakxQA; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y26c9UuC4TcAAGwh +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "message": "OK" +} +``` + +### create_watchlist +#### Add address to watch list + +##### Request +* __Method:__ POST +* __Path:__ /api/account/v1/user/watchlist +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "notification_settings": { + "native": { + "outcoming": false, + "incoming": true + }, + "ERC-721": { + "outcoming": false, + "incoming": true + }, + "ERC-20": { + "outcoming": false, + "incoming": false + } + }, + "notification_methods": { + "email": true + }, + "name": "test2", + "address_hash": "0x0000000000000000000000000000000000000007" +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyM2QABWVtYWlsbQAAABp0ZXN0X3VzZXItM0BibG9ja3Njb3V0LmNvbWQAAmlkYcNkAARuYW1lbQAAAApVc2VyIFRlc3QzZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjNkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwM2QADHdhdGNobGlzdF9pZGHD.kv5nnz8sVGLaopoZs9ppOfu0hfpFi58yuisPDN6PtPI; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y16Kv_0GzWcAAGKi +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "notification_settings": { + "native": { + "outcoming": false, + "incoming": true + }, + "ERC-721": { + "outcoming": false, + "incoming": true + }, + "ERC-20": { + "outcoming": false, + "incoming": false + } + }, + "notification_methods": { + "email": true + }, + "name": "test2", + "id": 68, + "exchange_rate": null, + "address_hash": "0x0000000000000000000000000000000000000007", + "address_balance": null +} +``` + +### watchlist +#### Get addresses from watchlists + +##### Request +* __Method:__ GET +* __Path:__ /api/account/v1/user/watchlist + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyM2QABWVtYWlsbQAAABp0ZXN0X3VzZXItM0BibG9ja3Njb3V0LmNvbWQAAmlkYcNkAARuYW1lbQAAAApVc2VyIFRlc3QzZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjNkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwM2QADHdhdGNobGlzdF9pZGHD.kv5nnz8sVGLaopoZs9ppOfu0hfpFi58yuisPDN6PtPI; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y19FyIUGzWcAAGMC +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +[ + { + "notification_settings": { + "native": { + "outcoming": false, + "incoming": false + }, + "ERC-721": { + "outcoming": true, + "incoming": false + }, + "ERC-20": { + "outcoming": true, + "incoming": false + } + }, + "notification_methods": { + "email": false + }, + "name": "test3", + "id": 69, + "exchange_rate": null, + "address_hash": "0x0000000000000000000000000000000000000008", + "address_balance": null + }, + { + "notification_settings": { + "native": { + "outcoming": false, + "incoming": true + }, + "ERC-721": { + "outcoming": false, + "incoming": true + }, + "ERC-20": { + "outcoming": false, + "incoming": false + } + }, + "notification_methods": { + "email": true + }, + "name": "test2", + "id": 68, + "exchange_rate": null, + "address_hash": "0x0000000000000000000000000000000000000007", + "address_balance": null + } +] +``` + +### delete_watchlist +#### Delete address from watchlist by id + +##### Request +* __Method:__ DELETE +* __Path:__ /api/account/v1/user/watchlist/74 + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTFkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTE0QGJsb2Nrc2NvdXQuY29tZAACaWRhy2QABG5hbWVtAAAAC1VzZXIgVGVzdDExZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjExZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDExZAAMd2F0Y2hsaXN0X2lkYcs.YjW8nzuA66id0ADg2qpyjTMGfKJ7BHhjU_HdVq8w8vk; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2f5j2WpY30AAGuC +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "message": "OK" +} +``` + +### update_watchlist +#### Edit watchlist address + +##### Request +* __Method:__ PUT +* __Path:__ /api/account/v1/user/watchlist/67 +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "notification_settings": { + "native": { + "outcoming": false, + "incoming": true + }, + "ERC-721": { + "outcoming": true, + "incoming": true + }, + "ERC-20": { + "outcoming": true, + "incoming": true + } + }, + "notification_methods": { + "email": true + }, + "name": "test1", + "address_hash": "0x0000000000000000000000000000000000000006" +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMWQABWVtYWlsbQAAABp0ZXN0X3VzZXItMUBibG9ja3Njb3V0LmNvbWQAAmlkYcFkAARuYW1lbQAAAApVc2VyIFRlc3QxZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjFkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwMWQADHdhdGNobGlzdF9pZGHB.3KOkZkPrcMrRXfooQckn-zi6xmax1LJMBGBSjmGM8ww; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y12FoNKu97sAAGch +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "notification_settings": { + "native": { + "outcoming": false, + "incoming": true + }, + "ERC-721": { + "outcoming": true, + "incoming": true + }, + "ERC-20": { + "outcoming": true, + "incoming": true + } + }, + "notification_methods": { + "email": true + }, + "name": "test1", + "id": 67, + "exchange_rate": null, + "address_hash": "0x0000000000000000000000000000000000000006", + "address_balance": null +} +``` + +### create_watchlist +#### Example of error on creating watchlist address + +##### Request +* __Method:__ POST +* __Path:__ /api/account/v1/user/watchlist +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "notification_settings": { + "native": { + "outcoming": false, + "incoming": true + }, + "ERC-721": { + "outcoming": false, + "incoming": false + }, + "ERC-20": { + "outcoming": true, + "incoming": false + } + }, + "notification_methods": { + "email": false + }, + "name": "test4", + "address_hash": "0x0000000000000000000000000000000000000017" +} +``` + +##### Response +* __Status__: 422 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyOGQABWVtYWlsbQAAABt0ZXN0X3VzZXItMTFAYmxvY2tzY291dC5jb21kAAJpZGHIZAAEbmFtZW0AAAAKVXNlciBUZXN0OGQACG5pY2tuYW1lbQAAAAp0ZXN0X3VzZXI4ZAADdWlkbQAAAA9ibG9ja3Njb3V0fDAwMDhkAAx3YXRjaGxpc3RfaWRhyA.q1Rmte0qLd31GbmpA46bE8rXo2okwzX8aD_oDHn8CIQ; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2MCqHvooPMAAGbi +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "errors": { + "watchlist_id": [ + "Address already added to the watch list" + ] + } +} +``` + +### update_watchlist +#### Example of error on editing watchlist address + +##### Request +* __Method:__ PUT +* __Path:__ /api/account/v1/user/watchlist/72 +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "notification_settings": { + "native": { + "outcoming": false, + "incoming": true + }, + "ERC-721": { + "outcoming": false, + "incoming": false + }, + "ERC-20": { + "outcoming": true, + "incoming": false + } + }, + "notification_methods": { + "email": false + }, + "name": "test4", + "address_hash": "0x0000000000000000000000000000000000000017" +} +``` + +##### Response +* __Status__: 422 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyOGQABWVtYWlsbQAAABt0ZXN0X3VzZXItMTFAYmxvY2tzY291dC5jb21kAAJpZGHIZAAEbmFtZW0AAAAKVXNlciBUZXN0OGQACG5pY2tuYW1lbQAAAAp0ZXN0X3VzZXI4ZAADdWlkbQAAAA9ibG9ja3Njb3V0fDAwMDhkAAx3YXRjaGxpc3RfaWRhyA.q1Rmte0qLd31GbmpA46bE8rXo2okwzX8aD_oDHn8CIQ; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2Nh1eHooPMAAGci +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "errors": { + "watchlist_id": [ + "Address already added to the watch list" + ] + } +} +``` + +### create_api_key +#### Add api key + +##### Request +* __Method:__ POST +* __Path:__ /api/account/v1/user/api_keys +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "name": "test" +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMmQABWVtYWlsbQAAABp0ZXN0X3VzZXItMkBibG9ja3Njb3V0LmNvbWQAAmlkYcJkAARuYW1lbQAAAApVc2VyIFRlc3QyZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjJkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwMmQADHdhdGNobGlzdF9pZGHC.ULESD1_sOySz8eEVGnagUzGw6eMIx_8Pwoyr_5S3K0M; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y14XlMBqXaQAAGHi +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "name": "test", + "api_key": "de9ef457-3f47-48d3-affa-79ad9d3b27b9" +} +``` + +#### Example of error on creating api key + +##### Request +* __Method:__ POST +* __Path:__ /api/account/v1/user/api_keys +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "name": "test" +} +``` + +##### Response +* __Status__: 422 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjJkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI2QGJsb2Nrc2NvdXQuY29tZAACaWRh1mQABG5hbWVtAAAAC1VzZXIgVGVzdDIyZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIyZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIyZAAMd2F0Y2hsaXN0X2lkYdY.P37J2lZZdHaT4P-RatVaXCx77UcSH3s_TMx-FieaYk0; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3LmuuofZKYAAG_h +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "errors": { + "name": [ + "Max 3 keys per account" + ] + } +} +``` + +### api_keys +#### Get api keys list + +##### Request +* __Method:__ GET +* __Path:__ /api/account/v1/user/api_keys + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjJkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI2QGJsb2Nrc2NvdXQuY29tZAACaWRh1mQABG5hbWVtAAAAC1VzZXIgVGVzdDIyZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIyZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIyZAAMd2F0Y2hsaXN0X2lkYdY.P37J2lZZdHaT4P-RatVaXCx77UcSH3s_TMx-FieaYk0; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3LyOSIfZKYAAHAB +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +[ + { + "name": "test", + "api_key": "2ac16688-34e6-4fa4-8983-a9bc34c912f6" + }, + { + "name": "test", + "api_key": "a55426db-04f0-40be-a146-1ced4558aa0c" + }, + { + "name": "test", + "api_key": "d73fc23b-59f0-4e6f-a739-f4de30995101" + } +] +``` + +### update_api_key +#### Edit api key + +##### Request +* __Method:__ PUT +* __Path:__ /api/account/v1/user/api_keys/2b1d400d-713e-4bfc-8ef0-710555693138 +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "name": "test_1" +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTdkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTIxQGJsb2Nrc2NvdXQuY29tZAACaWRh0WQABG5hbWVtAAAAC1VzZXIgVGVzdDE3ZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjE3ZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDE3ZAAMd2F0Y2hsaXN0X2lkYdE.bLJKM3-kFm04mMC-4-3b2mjrig_lmQYt5C2tg-9q9so; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2-0eR7T2BMAAG0B +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "name": "test_1", + "api_key": "2b1d400d-713e-4bfc-8ef0-710555693138" +} +``` + +### delete_api_key +#### Delete api key + +##### Request +* __Method:__ DELETE +* __Path:__ /api/account/v1/user/api_keys/3bd44c0d-290f-4dfc-9283-5f674080f8ef + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjBkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI0QGJsb2Nrc2NvdXQuY29tZAACaWRh1GQABG5hbWVtAAAAC1VzZXIgVGVzdDIwZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIwZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIwZAAMd2F0Y2hsaXN0X2lkYdQ.WgjMmOxwwBGcTZZscpLA8EXErwL8ITCvoIXPLIQAhtw; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3HQdpa0710AAHBi +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "message": "OK" +} +``` + +### create_custom_abi +#### Add custom abi + +##### Request +* __Method:__ POST +* __Path:__ /api/account/v1/user/custom_abis +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "name": "test25", + "contract_address_hash": "0x000000000000000000000000000000000000002c", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTJkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTE1QGJsb2Nrc2NvdXQuY29tZAACaWRhzGQABG5hbWVtAAAAC1VzZXIgVGVzdDEyZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjEyZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDEyZAAMd2F0Y2hsaXN0X2lkYcw.7cCOt6SVrOb5VLYplBzwZ03FWMo9jQpAV7cNroY4txY; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2iZJWbZgfgAAGwC +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "name": "test25", + "id": 143, + "contract_address_hash": "0x000000000000000000000000000000000000002c", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] +} +``` + +#### Example of error on creating custom abi + +##### Request +* __Method:__ POST +* __Path:__ /api/account/v1/user/custom_abis +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "name": "test21", + "contract_address_hash": "0x0000000000000000000000000000000000000028", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] +} +``` + +##### Response +* __Status__: 422 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyOWQABWVtYWlsbQAAABt0ZXN0X3VzZXItMTJAYmxvY2tzY291dC5jb21kAAJpZGHJZAAEbmFtZW0AAAAKVXNlciBUZXN0OWQACG5pY2tuYW1lbQAAAAp0ZXN0X3VzZXI5ZAADdWlkbQAAAA9ibG9ja3Njb3V0fDAwMDlkAAx3YXRjaGxpc3RfaWRhyQ.MCpJsS-nb95ccHRtzOk7DbIRjEcTG34ONq4PrC5hOcU; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2Ypm-ny0swAAGiB +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "errors": { + "name": [ + "Max 15 ABIs per account" + ] + } +} +``` + +### custom_abis +#### Get custom abis list + +##### Request +* __Method:__ GET +* __Path:__ /api/account/v1/user/custom_abis + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyOWQABWVtYWlsbQAAABt0ZXN0X3VzZXItMTJAYmxvY2tzY291dC5jb21kAAJpZGHJZAAEbmFtZW0AAAAKVXNlciBUZXN0OWQACG5pY2tuYW1lbQAAAAp0ZXN0X3VzZXI5ZAADdWlkbQAAAA9ibG9ja3Njb3V0fDAwMDlkAAx3YXRjaGxpc3RfaWRhyQ.MCpJsS-nb95ccHRtzOk7DbIRjEcTG34ONq4PrC5hOcU; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2Y-qjXy0swAAGnC +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +[ + { + "name": "test20", + "id": 141, + "contract_address_hash": "0x0000000000000000000000000000000000000027", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test19", + "id": 140, + "contract_address_hash": "0x0000000000000000000000000000000000000026", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test18", + "id": 139, + "contract_address_hash": "0x0000000000000000000000000000000000000025", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test17", + "id": 138, + "contract_address_hash": "0x0000000000000000000000000000000000000024", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test16", + "id": 137, + "contract_address_hash": "0x0000000000000000000000000000000000000023", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test15", + "id": 136, + "contract_address_hash": "0x0000000000000000000000000000000000000022", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test14", + "id": 135, + "contract_address_hash": "0x0000000000000000000000000000000000000021", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test13", + "id": 134, + "contract_address_hash": "0x0000000000000000000000000000000000000020", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test12", + "id": 133, + "contract_address_hash": "0x000000000000000000000000000000000000001f", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test11", + "id": 132, + "contract_address_hash": "0x000000000000000000000000000000000000001e", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test10", + "id": 131, + "contract_address_hash": "0x000000000000000000000000000000000000001d", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test9", + "id": 130, + "contract_address_hash": "0x000000000000000000000000000000000000001c", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test8", + "id": 129, + "contract_address_hash": "0x000000000000000000000000000000000000001b", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test7", + "id": 128, + "contract_address_hash": "0x000000000000000000000000000000000000001a", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + }, + { + "name": "test6", + "id": 127, + "contract_address_hash": "0x0000000000000000000000000000000000000019", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] + } +] +``` + +### update_custom_abi +#### Edit custom abi + +##### Request +* __Method:__ PUT +* __Path:__ /api/account/v1/user/custom_abis/144 +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "name": "test27", + "contract_address_hash": "0x000000000000000000000000000000000000004b", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjFkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI1QGJsb2Nrc2NvdXQuY29tZAACaWRh1WQABG5hbWVtAAAAC1VzZXIgVGVzdDIxZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIxZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIxZAAMd2F0Y2hsaXN0X2lkYdU.SEUqq9ZiSD79HIzwKvwTspmBKKU87m_Xwu5gw2pX1e0; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3JcHmB4X2AAAHDC +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "name": "test27", + "id": 144, + "contract_address_hash": "0x000000000000000000000000000000000000004b", + "abi": [ + { + "type": "function", + "stateMutability": "nonpayable", + "payable": false, + "outputs": [], + "name": "set", + "inputs": [ + { + "type": "uint256", + "name": "x" + } + ], + "constant": false + }, + { + "type": "function", + "stateMutability": "view", + "payable": false, + "outputs": [ + { + "type": "uint256", + "name": "" + } + ], + "name": "get", + "inputs": [], + "constant": true + } + ] +} +``` + +### delete_custom_abi +#### Delete custom abi + +##### Request +* __Method:__ DELETE +* __Path:__ /api/account/v1/user/custom_abis/142 + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMTBkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTEzQGJsb2Nrc2NvdXQuY29tZAACaWRhymQABG5hbWVtAAAAC1VzZXIgVGVzdDEwZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjEwZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDEwZAAMd2F0Y2hsaXN0X2lkYco.x_6dmEjpZ1o8_ct-M7pWWP0LkI66xhwl8gWeQt9XzHA; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2b1jJGBaO4AAGrC +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "message": "OK" +} +``` + +### create_public_tags_request +#### Submit request to add a public tag + +##### Request +* __Method:__ POST +* __Path:__ /api/account/v1/user/public_tags +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "website": "website0", + "tags": "Tag0", + "is_owner": true, + "full_name": "full name0", + "email": "test_user-6@blockscout.com", + "company": "company0", + "addresses": [ + "0x0000000000000000000000000000000000000009", + "0x000000000000000000000000000000000000000a", + "0x000000000000000000000000000000000000000b", + "0x000000000000000000000000000000000000000c", + "0x000000000000000000000000000000000000000d" + ], + "additional_comment": "additional_comment0" +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyNWQABWVtYWlsbQAAABp0ZXN0X3VzZXItNUBibG9ja3Njb3V0LmNvbWQAAmlkYcVkAARuYW1lbQAAAApVc2VyIFRlc3Q1ZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjVkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwNWQADHdhdGNobGlzdF9pZGHF.kXAMBaL9a7aYjPDgZ9Llxe1etUCPH3vEvQe9Fq2May4; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2BIESA-ecUAAGgB +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "website": "website0", + "tags": "Tag0", + "submission_date": "2022-09-03T21:00:07.156465Z", + "is_owner": true, + "id": 131, + "full_name": "full name0", + "email": "test_user-6@blockscout.com", + "company": "company0", + "addresses": [ + "0x0000000000000000000000000000000000000009", + "0x000000000000000000000000000000000000000a", + "0x000000000000000000000000000000000000000b", + "0x000000000000000000000000000000000000000c", + "0x000000000000000000000000000000000000000d" + ], + "additional_comment": "additional_comment0" +} +``` + +### public_tags_requests +#### Get list of requests to add a public tag + +##### Request +* __Method:__ GET +* __Path:__ /api/account/v1/user/public_tags + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjNkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI3QGJsb2Nrc2NvdXQuY29tZAACaWRh12QABG5hbWVtAAAAC1VzZXIgVGVzdDIzZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIzZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIzZAAMd2F0Y2hsaXN0X2lkYdc._6gJnvzjA6VEztgoIdpp7chhmhsdFrJImlcdrp4-pW0; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3SaPVCdkicAAHIi +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +[ + { + "website": "website13", + "tags": "Tag17", + "submission_date": "2022-09-03T21:00:07.000000Z", + "is_owner": false, + "id": 143, + "full_name": "full name13", + "email": "test_user-37@blockscout.com", + "company": "company13", + "addresses": [ + "0x000000000000000000000000000000000000007e", + "0x000000000000000000000000000000000000007f", + "0x0000000000000000000000000000000000000080", + "0x0000000000000000000000000000000000000081", + "0x0000000000000000000000000000000000000082", + "0x0000000000000000000000000000000000000083", + "0x0000000000000000000000000000000000000084" + ], + "additional_comment": "additional_comment13" + }, + { + "website": "website12", + "tags": "Tag16", + "submission_date": "2022-09-03T21:00:07.000000Z", + "is_owner": false, + "id": 142, + "full_name": "full name12", + "email": "test_user-36@blockscout.com", + "company": "company12", + "addresses": [ + "0x0000000000000000000000000000000000000075", + "0x0000000000000000000000000000000000000076", + "0x0000000000000000000000000000000000000077", + "0x0000000000000000000000000000000000000078", + "0x0000000000000000000000000000000000000079", + "0x000000000000000000000000000000000000007a", + "0x000000000000000000000000000000000000007b", + "0x000000000000000000000000000000000000007c", + "0x000000000000000000000000000000000000007d" + ], + "additional_comment": "additional_comment12" + }, + { + "website": "website11", + "tags": "Tag15", + "submission_date": "2022-09-03T21:00:07.000000Z", + "is_owner": false, + "id": 141, + "full_name": "full name11", + "email": "test_user-35@blockscout.com", + "company": "company11", + "addresses": [ + "0x000000000000000000000000000000000000006d", + "0x000000000000000000000000000000000000006e", + "0x000000000000000000000000000000000000006f", + "0x0000000000000000000000000000000000000070", + "0x0000000000000000000000000000000000000071", + "0x0000000000000000000000000000000000000072", + "0x0000000000000000000000000000000000000073", + "0x0000000000000000000000000000000000000074" + ], + "additional_comment": "additional_comment11" + }, + { + "website": "website10", + "tags": "Tag14", + "submission_date": "2022-09-03T21:00:07.000000Z", + "is_owner": false, + "id": 140, + "full_name": "full name10", + "email": "test_user-34@blockscout.com", + "company": "company10", + "addresses": [ + "0x0000000000000000000000000000000000000067", + "0x0000000000000000000000000000000000000068", + "0x0000000000000000000000000000000000000069", + "0x000000000000000000000000000000000000006a", + "0x000000000000000000000000000000000000006b", + "0x000000000000000000000000000000000000006c" + ], + "additional_comment": "additional_comment10" + }, + { + "website": "website9", + "tags": "Tag13", + "submission_date": "2022-09-03T21:00:07.000000Z", + "is_owner": true, + "id": 139, + "full_name": "full name9", + "email": "test_user-33@blockscout.com", + "company": "company9", + "addresses": [ + "0x0000000000000000000000000000000000000061", + "0x0000000000000000000000000000000000000062", + "0x0000000000000000000000000000000000000063", + "0x0000000000000000000000000000000000000064", + "0x0000000000000000000000000000000000000065", + "0x0000000000000000000000000000000000000066" + ], + "additional_comment": "additional_comment9" + }, + { + "website": "website8", + "tags": "Tag12", + "submission_date": "2022-09-03T21:00:07.000000Z", + "is_owner": false, + "id": 138, + "full_name": "full name8", + "email": "test_user-32@blockscout.com", + "company": "company8", + "addresses": [ + "0x0000000000000000000000000000000000000060" + ], + "additional_comment": "additional_comment8" + }, + { + "website": "website7", + "tags": "Tag11", + "submission_date": "2022-09-03T21:00:07.000000Z", + "is_owner": true, + "id": 137, + "full_name": "full name7", + "email": "test_user-31@blockscout.com", + "company": "company7", + "addresses": [ + "0x000000000000000000000000000000000000005f" + ], + "additional_comment": "additional_comment7" + }, + { + "website": "website6", + "tags": "Tag9;Tag10", + "submission_date": "2022-09-03T21:00:07.000000Z", + "is_owner": true, + "id": 136, + "full_name": "full name6", + "email": "test_user-30@blockscout.com", + "company": "company6", + "addresses": [ + "0x000000000000000000000000000000000000005a", + "0x000000000000000000000000000000000000005b", + "0x000000000000000000000000000000000000005c", + "0x000000000000000000000000000000000000005d", + "0x000000000000000000000000000000000000005e" + ], + "additional_comment": "additional_comment6" + }, + { + "website": "website5", + "tags": "Tag8", + "submission_date": "2022-09-03T21:00:07.000000Z", + "is_owner": false, + "id": 135, + "full_name": "full name5", + "email": "test_user-29@blockscout.com", + "company": "company5", + "addresses": [ + "0x0000000000000000000000000000000000000051", + "0x0000000000000000000000000000000000000052", + "0x0000000000000000000000000000000000000053", + "0x0000000000000000000000000000000000000054", + "0x0000000000000000000000000000000000000055", + "0x0000000000000000000000000000000000000056", + "0x0000000000000000000000000000000000000057", + "0x0000000000000000000000000000000000000058", + "0x0000000000000000000000000000000000000059" + ], + "additional_comment": "additional_comment5" + }, + { + "website": "website4", + "tags": "Tag6;Tag7", + "submission_date": "2022-09-03T21:00:07.000000Z", + "is_owner": true, + "id": 134, + "full_name": "full name4", + "email": "test_user-28@blockscout.com", + "company": "company4", + "addresses": [ + "0x000000000000000000000000000000000000004c", + "0x000000000000000000000000000000000000004d", + "0x000000000000000000000000000000000000004e", + "0x000000000000000000000000000000000000004f", + "0x0000000000000000000000000000000000000050" + ], + "additional_comment": "additional_comment4" + } +] +``` + +### delete_public_tags_request +#### Delete public tags request + +##### Request +* __Method:__ DELETE +* __Path:__ /api/account/v1/user/public_tags/143 +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "remove_reason": "reason" +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAmaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyMjNkAAVlbWFpbG0AAAAbdGVzdF91c2VyLTI3QGJsb2Nrc2NvdXQuY29tZAACaWRh12QABG5hbWVtAAAAC1VzZXIgVGVzdDIzZAAIbmlja25hbWVtAAAAC3Rlc3RfdXNlcjIzZAADdWlkbQAAABBibG9ja3Njb3V0fDAwMDIzZAAMd2F0Y2hsaXN0X2lkYdc._6gJnvzjA6VEztgoIdpp7chhmhsdFrJImlcdrp4-pW0; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y3SwObudkicAAHBB +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "message": "OK" +} +``` + +### update_public_tags_request +#### Edit request to add a public tag + +##### Request +* __Method:__ PUT +* __Path:__ /api/account/v1/user/public_tags/132 +* __Request headers:__ +``` +content-type: multipart/mixed; boundary=plug_conn_test +``` +* __Request body:__ +```json +{ + "website": "website2", + "tags": "Tag2;Tag3", + "is_owner": true, + "full_name": "full name2", + "email": "test_user-9@blockscout.com", + "company": "company2", + "addresses": [ + "0x000000000000000000000000000000000000000f", + "0x0000000000000000000000000000000000000010", + "0x0000000000000000000000000000000000000011", + "0x0000000000000000000000000000000000000012", + "0x0000000000000000000000000000000000000013", + "0x0000000000000000000000000000000000000014" + ], + "additional_comment": "additional_comment2" +} +``` + +##### Response +* __Status__: 200 +* __Response headers:__ +``` +set-cookie: _explorer_key=SFMyNTY.g3QAAAABbQAAAAxjdXJyZW50X3VzZXJ0AAAAB2QABmF2YXRhcm0AAAAlaHR0cHM6Ly9leGFtcGxlLmNvbS9hdmF0YXIvdGVzdF91c2VyNmQABWVtYWlsbQAAABp0ZXN0X3VzZXItN0BibG9ja3Njb3V0LmNvbWQAAmlkYcZkAARuYW1lbQAAAApVc2VyIFRlc3Q2ZAAIbmlja25hbWVtAAAACnRlc3RfdXNlcjZkAAN1aWRtAAAAD2Jsb2Nrc2NvdXR8MDAwNmQADHdhdGNobGlzdF9pZGHG.86gruprPiLE-Nf9xkOzjEcW2wfSnCCPly5fHTwHrF6c; path=/; HttpOnly +content-type: application/json; charset=utf-8 +cache-control: max-age=0, private, must-revalidate +x-request-id: FxF1Y2E03jhU4u4AAGSi +access-control-allow-credentials: true +access-control-allow-origin: * +access-control-expose-headers: +``` +* __Response body:__ +```json +{ + "website": "website2", + "tags": "Tag2;Tag3", + "submission_date": "2022-09-03T21:00:07.000000Z", + "is_owner": true, + "id": 132, + "full_name": "full name2", + "email": "test_user-9@blockscout.com", + "company": "company2", + "addresses": [ + "0x000000000000000000000000000000000000000f", + "0x0000000000000000000000000000000000000010", + "0x0000000000000000000000000000000000000011", + "0x0000000000000000000000000000000000000012", + "0x0000000000000000000000000000000000000013", + "0x0000000000000000000000000000000000000014" + ], + "additional_comment": "additional_comment2" +} +``` + diff --git a/apps/block_scout_web/README.md b/apps/block_scout_web/README.md new file mode 100644 index 0000000..8c1a622 --- /dev/null +++ b/apps/block_scout_web/README.md @@ -0,0 +1,43 @@ +# BlockScout Web + +This is a tool for inspecting and analyzing the POA Network blockchain from a web browser. + +## Machine Requirements + +* Erlang/OTP 21+ +* Elixir 1.9+ +* Postgres 10.3 + +## Required Accounts + +* Github for code storage + +## Setup Instructions + +### Development + +To get BlockScout Web interface up and running locally: + +* Setup `../explorer` +* Install Node.js dependencies with `$ cd assets && npm install && cd ..` +* Start Phoenix with `$ mix phx.server` (This can be run from this directory or the project root: the project root is recommended.) + +Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. + +You can also run IEx (Interactive Elixir): `$ iex -S mix phx.server` (This can be run from this directory or the project root: the project root is recommended.) + +### Testing + +* Build the assets: `cd assets && npm run build` +* Format the Elixir code: `mix format` +* Lint the Elixir code: `mix credo --strict` +* Run the dialyzer: `mix dialyzer --halt-exit-status` +* Check the Elixir code for vulnerabilities: `mix sobelow --config` +* Update translations templates and translations and check there are no uncommitted changes: `mix gettext.extract --merge` +* Lint the JavaScript code: `cd assets && npm run eslint` + +## Internationalization + +The app is currently internationalized. It is only localized to U.S. English. + +To translate new strings, run `$ mix gettext.extract --merge` and edit the new strings in `priv/gettext/en/LC_MESSAGES/default.po`. diff --git a/apps/block_scout_web/assets/.babelrc b/apps/block_scout_web/assets/.babelrc new file mode 100644 index 0000000..db967cb --- /dev/null +++ b/apps/block_scout_web/assets/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [["@babel/preset-env", { "useBuiltIns": "usage", "corejs": { "version": 3 } }]] +} diff --git a/apps/block_scout_web/assets/.eslintrc b/apps/block_scout_web/assets/.eslintrc new file mode 100644 index 0000000..535509b --- /dev/null +++ b/apps/block_scout_web/assets/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": "standard", + "env": { + "browser": true + } +} diff --git a/apps/block_scout_web/assets/README.md b/apps/block_scout_web/assets/README.md new file mode 100644 index 0000000..c4e098c --- /dev/null +++ b/apps/block_scout_web/assets/README.md @@ -0,0 +1,9 @@ +# Javascript structure files + +## lib + +* This folder is used to place `component` files, that may span in multiple pages. + +## pages + +* This folder is used to place `page` specific files, that won't be reusable in other locations. diff --git a/apps/block_scout_web/assets/__mocks__/css/app.scss.js b/apps/block_scout_web/assets/__mocks__/css/app.scss.js new file mode 100644 index 0000000..1a86a4f --- /dev/null +++ b/apps/block_scout_web/assets/__mocks__/css/app.scss.js @@ -0,0 +1,4 @@ +export default { + primary: "#4786ff", + secondary: "#ced4da" +} diff --git a/apps/block_scout_web/assets/__tests__/lib/async_listing_load.js b/apps/block_scout_web/assets/__tests__/lib/async_listing_load.js new file mode 100644 index 0000000..d6d187c --- /dev/null +++ b/apps/block_scout_web/assets/__tests__/lib/async_listing_load.js @@ -0,0 +1,82 @@ +/** + * @jest-environment jsdom + */ + +import { asyncReducer, asyncInitialState } from '../../js/lib/async_listing_load' + +describe('ELEMENTS_LOAD', () => { + test('sets only nextPagePath and ignores other keys', () => { + const state = Object.assign({}, asyncInitialState) + const action = { type: 'ELEMENTS_LOAD', nextPagePath: 'set', foo: 1 } + const output = asyncReducer(state, action) + + expect(output.foo).not.toEqual(1) + expect(output.nextPagePath).toEqual('set') + }) +}) + +describe('ADD_ITEM_KEY', () => { + test('sets itemKey to what was passed in the action', () => { + const expectedItemKey = 'expected.Key' + + const state = Object.assign({}, asyncInitialState) + const action = { type: 'ADD_ITEM_KEY', itemKey: expectedItemKey } + const output = asyncReducer(state, action) + + expect(output.itemKey).toEqual(expectedItemKey) + }) +}) + +describe('START_REQUEST', () => { + test('sets loading status to true', () => { + const state = Object.assign({}, asyncInitialState, { loading: false }) + const action = { type: 'START_REQUEST' } + const output = asyncReducer(state, action) + + expect(output.loading).toEqual(true) + }) +}) + +describe('REQUEST_ERROR', () => { + test('sets requestError to true', () => { + const state = Object.assign({}, asyncInitialState, { requestError: false }) + const action = { type: 'REQUEST_ERROR' } + const output = asyncReducer(state, action) + + expect(output.requestError).toEqual(true) + }) +}) + +describe('FINISH_REQUEST', () => { + test('sets loading status to false', () => { + const state = Object.assign({}, asyncInitialState, { + loading: true + }) + const action = { type: 'FINISH_REQUEST' } + const output = asyncReducer(state, action) + + expect(output.loading).toEqual(false) + }) +}) + +describe('ITEMS_FETCHED', () => { + test('sets the items to what was passed in the action', () => { + const expectedItems = [1, 2, 3] + + const state = Object.assign({}, asyncInitialState) + const action = { type: 'ITEMS_FETCHED', items: expectedItems } + const output = asyncReducer(state, action) + + expect(output.items).toEqual(expectedItems) + }) +}) + +describe('NAVIGATE_TO_OLDER', () => { + test('sets beyondPageOne to true', () => { + const state = Object.assign({}, asyncInitialState, { beyondPageOne: false }) + const action = { type: 'NAVIGATE_TO_OLDER' } + const output = asyncReducer(state, action) + + expect(output.beyondPageOne).toEqual(true) + }) +}) diff --git a/apps/block_scout_web/assets/__tests__/lib/autocomplete.js b/apps/block_scout_web/assets/__tests__/lib/autocomplete.js new file mode 100644 index 0000000..877474c --- /dev/null +++ b/apps/block_scout_web/assets/__tests__/lib/autocomplete.js @@ -0,0 +1,31 @@ +/** + * @jest-environment jsdom + */ + + import { searchEngine } from '../../js/lib/autocomplete' + + test('searchEngine', () => { + expect(searchEngine('qwe', { + 'name': 'Test', + 'symbol': 'TST', + 'address_hash': '0x000', + 'tx_hash': '0x000', + 'block_hash': '0x000' + })).toEqual(undefined) + + expect(searchEngine('tes', { + 'name': 'Test', + 'symbol': 'TST', + 'address_hash': '0x000', + 'tx_hash': '0x000', + 'block_hash': '0x000' + })).toEqual('
0x000
Test (TST)
') + + expect(searchEngine('qwe', { + 'name': 'qwe1\'">')) + $('#csv-iframe').attr('src', downloadUrl) + + const interval = setInterval(handleCSVDownloaded, 1000) + setTimeout(resetDownload, 60000) + + function handleCSVDownloaded () { + if (Cookies.get('csv-downloaded') === 'true') { + resetDownload() + } + } + + function resetDownload () { + $button.removeClass('spinner') + $button.prop('disabled', false) + clearInterval(interval) + Cookies.remove('csv-downloaded') + // eslint-disable-next-line + grecaptcha.reset() + } + } +}) + +function onSelect (date, paramToReplace) { + const formattedDate = moment(date).format(DATE_FORMAT) + + if (date) { + const csvExportPath = $button.data('link') + + const updatedCsvExportUrl = replaceUrlParam(csvExportPath, paramToReplace, formattedDate) + $button.data('link', updatedCsvExportUrl) + } +} + +function replaceUrlParam (url, paramName, paramValue) { + if (paramValue == null) { + paramValue = '' + } + const pattern = new RegExp('\\b(' + paramName + '=).*?(&|#|$)') + if (url.search(pattern) >= 0) { + return url.replace(pattern, '$1' + paramValue + '$2') + } + url = url.replace(/[?#]$/, '') + return url + (url.indexOf('?') > 0 ? '&' : '?') + paramName + '=' + paramValue +} diff --git a/apps/block_scout_web/assets/js/lib/currency.js b/apps/block_scout_web/assets/js/lib/currency.js new file mode 100644 index 0000000..02dd537 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/currency.js @@ -0,0 +1,72 @@ +import $ from 'jquery' +import numeral from 'numeral' +import { BigNumber } from 'bignumber.js' + +export function formatUsdValue (value) { + const formattedValue = formatCurrencyValue(value) + if (formattedValue === 'N/A') { + return formattedValue + } else { + return `${formattedValue} USD` + } +} + +function formatTokenUsdValue (value) { + return formatCurrencyValue(value, '@') +} + +function formatCurrencyValue (value, symbol) { + symbol = symbol || '$' + if (isNaN(value)) return 'N/A' + if (value === 0 || value === '0') return `${symbol}0.00` + if (value < 0.000001) return `${window.localized['Less than']} ${symbol}0.000001` + if (value < 1) return `${symbol}${numeral(value).format('0.000000')}` + if (value < 100000) return `${symbol}${numeral(value).format('0,0.00')}` + if (value > 1000000000000) return `${symbol}${numeral(value).format('0.000e+0')}` + return `${symbol}${numeral(value).format('0,0')}` +} + +function weiToEther (wei) { + return new BigNumber(wei).dividedBy('1000000000000000000').toNumber() +} + +function etherToUSD (ether, usdExchangeRate) { + return new BigNumber(ether).multipliedBy(usdExchangeRate).toNumber() +} + +export function formatAllUsdValues (root) { + root = root || $(':root') + + root.find('[data-usd-value]').each((i, el) => { + el.innerHTML = formatUsdValue(el.dataset.usdValue) + }) + root.find('[data-token-usd-value]').each((i, el) => { + el.innerHTML = formatTokenUsdValue(el.dataset.tokenUsdValue) + }) + + return root +} +formatAllUsdValues() + +function tryUpdateCalculatedUsdValues (el, usdExchangeRate = el.dataset.usdExchangeRate) { + // eslint-disable-next-line no-prototype-builtins + if (!el.dataset.hasOwnProperty('weiValue')) return + const ether = weiToEther(el.dataset.weiValue) + const usd = etherToUSD(ether, usdExchangeRate) + const formattedUsd = formatUsdValue(usd) + if (formattedUsd !== el.innerHTML) { + $(el).data('rawUsdValue', usd) + el.innerHTML = formattedUsd + } +} + +function tryUpdateUnitPriceValues (el, usdUnitPrice = el.dataset.usdUnitPrice) { + const formattedValue = formatCurrencyValue(usdUnitPrice) + if (formattedValue !== el.innerHTML) el.innerHTML = formattedValue +} + +export function updateAllCalculatedUsdValues (usdExchangeRate) { + $('[data-usd-exchange-rate]').each((i, el) => tryUpdateCalculatedUsdValues(el, usdExchangeRate)) + $('[data-usd-unit-price]').each((i, el) => tryUpdateUnitPriceValues(el, usdExchangeRate)) +} +updateAllCalculatedUsdValues() diff --git a/apps/block_scout_web/assets/js/lib/custom_ad.json b/apps/block_scout_web/assets/js/lib/custom_ad.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/custom_ad.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/apps/block_scout_web/assets/js/lib/dropzone.js b/apps/block_scout_web/assets/js/lib/dropzone.js new file mode 100644 index 0000000..645c4dd --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/dropzone.js @@ -0,0 +1 @@ +import 'dropzone' diff --git a/apps/block_scout_web/assets/js/lib/from_now.js b/apps/block_scout_web/assets/js/lib/from_now.js new file mode 100644 index 0000000..32dc90b --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/from_now.js @@ -0,0 +1,35 @@ +import $ from 'jquery' +import moment from 'moment' + +moment.relativeTimeThreshold('M', 12) +moment.relativeTimeThreshold('d', 30) +moment.relativeTimeThreshold('h', 24) +moment.relativeTimeThreshold('m', 60) +moment.relativeTimeThreshold('s', 60) +moment.relativeTimeThreshold('ss', 1) + +export function updateAllAges ($container = $(document)) { + $container.find('[data-from-now]').each((i, el) => tryUpdateAge(el)) + return $container +} +function tryUpdateAge (el) { + if (!el.dataset.fromNow) return + + const timestamp = moment(el.dataset.fromNow) + if (timestamp.isValid()) updateAge(el, timestamp) +} +function updateAge (el, timestamp) { + let fromNow = timestamp.fromNow() + // show the exact time only for transaction details page. Otherwise, short entry + const elInTile = el.hasAttribute('in-tile') + if ((window.location.pathname.includes('/tx/') || window.location.pathname.includes('/block/') || window.location.pathname.includes('/blocks/')) && !elInTile) { + const offset = moment().utcOffset() / 60 + const sign = offset && Math.sign(offset) ? '+' : '-' + const formatDate = `MMMM-DD-YYYY hh:mm:ss A ${sign}${offset} UTC` + fromNow = `${fromNow} | ${timestamp.format(formatDate)}` + } + if (fromNow !== el.innerHTML) el.innerHTML = fromNow +} +updateAllAges() + +setInterval(updateAllAges, 1000) diff --git a/apps/block_scout_web/assets/js/lib/history_chart.js b/apps/block_scout_web/assets/js/lib/history_chart.js new file mode 100644 index 0000000..c7c94cb --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/history_chart.js @@ -0,0 +1,342 @@ +import $ from 'jquery' +import { Chart, LineController, LineElement, PointElement, LinearScale, TimeScale, Title, Tooltip } from 'chart.js' +import 'chartjs-adapter-luxon' +import humps from 'humps' +import numeral from 'numeral' +import { DateTime } from 'luxon' +import { formatUsdValue } from '../lib/currency' +import sassVariables from '../../css/export-vars-to-js.module.scss' + +Chart.defaults.font.family = 'Nunito, "Helvetica Neue", Arial, sans-serif,"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"' +Chart.register(LineController, LineElement, PointElement, LinearScale, TimeScale, Title, Tooltip) + +const grid = { + display: false, + drawBorder: false, + drawOnChartArea: false +} + +function getTxChartColor () { + if (localStorage.getItem('current-color-mode') === 'dark') { + return sassVariables.dashboardLineColorTransactionsDarkTheme + } else { + return sassVariables.dashboardLineColorTransactions + } +} + +function getPriceChartColor () { + if (localStorage.getItem('current-color-mode') === 'dark') { + return sassVariables.dashboardLineColorPriceDarkTheme + } else { + return sassVariables.dashboardLineColorPrice + } +} + +function getMarketCapChartColor () { + if (localStorage.getItem('current-color-mode') === 'dark') { + return sassVariables.dashboardLineColorMarketDarkTheme + } else { + return sassVariables.dashboardLineColorMarket + } +} + +function xAxe (fontColor) { + return { + grid, + type: 'time', + time: { + unit: 'day', + tooltipFormat: 'DD', + stepSize: 14 + }, + ticks: { + color: fontColor + } + } +} + +const padding = { + left: 20, + right: 20 +} + +const legend = { + display: false +} + +function formatValue (val) { + return `${numeral(val).format('0,0')}` +} + +const config = { + type: 'line', + responsive: true, + data: { + datasets: [] + }, + options: { + layout: { + padding + }, + interaction: { + intersect: false, + mode: 'index' + }, + scales: { + x: xAxe(sassVariables.dashboardBannerChartAxisFontColor), + price: { + position: 'left', + grid, + ticks: { + beginAtZero: true, + callback: (value, _index, _values) => `$${numeral(value).format('0,0.00')}`, + maxTicksLimit: 4, + color: sassVariables.dashboardBannerChartAxisFontColor + } + }, + marketCap: { + position: 'right', + grid, + ticks: { + callback: (_value, _index, _values) => '', + maxTicksLimit: 6, + drawOnChartArea: false, + color: sassVariables.dashboardBannerChartAxisFontColor + } + }, + numTransactions: { + position: 'right', + grid, + ticks: { + beginAtZero: true, + callback: (value, _index, _values) => formatValue(value), + maxTicksLimit: 4, + color: sassVariables.dashboardBannerChartAxisFontColor + } + } + }, + plugins: { + legend, + title: { + display: true, + color: sassVariables.dashboardBannerChartAxisFontColor + }, + tooltip: { + mode: 'index', + intersect: false, + callbacks: { + label: (context) => { + const { label } = context.dataset + const { formattedValue, parsed } = context + if (context.dataset.yAxisID === 'price') { + return `${label}: ${formatUsdValue(parsed.y)}` + } else if (context.dataset.yAxisID === 'marketCap') { + return `${label}: ${formatUsdValue(parsed.y)}` + } else if (context.dataset.yAxisID === 'numTransactions') { + return `${label}: ${formattedValue}` + } else { + return formattedValue + } + } + } + } + } + } +} + +function getDataFromLocalStorage (key) { + const data = window.localStorage.getItem(key) + return data ? JSON.parse(data) : [] +} + +function setDataToLocalStorage (key, data) { + window.localStorage.setItem(key, JSON.stringify(data)) +} + +function getPriceData (marketHistoryData) { + if (marketHistoryData.length === 0) { + return getDataFromLocalStorage('priceData') + } + const data = marketHistoryData.map(({ date, closingPrice }) => ({ x: date, y: closingPrice })) + setDataToLocalStorage('priceData', data) + return data +} + +function getTxHistoryData (transactionHistory) { + if (transactionHistory.length === 0) { + return getDataFromLocalStorage('txHistoryData') + } + const data = transactionHistory.map(dataPoint => ({ x: dataPoint.date, y: dataPoint.number_of_transactions })) + + // it should be empty value for tx history the current day + const prevDayStr = data[0].x + const prevDay = DateTime.fromISO(prevDayStr) + let curDay = prevDay.plus({ days: 1 }) + curDay = curDay.toISODate() + data.unshift({ x: curDay, y: null }) + + setDataToLocalStorage('txHistoryData', data) + return data +} + +function getMarketCapData (marketHistoryData, availableSupply) { + if (marketHistoryData.length === 0) { + return getDataFromLocalStorage('marketCapData') + } + const data = marketHistoryData.map(({ date, closingPrice }) => { + const supply = (availableSupply !== null && typeof availableSupply === 'object') + ? availableSupply[date] + : availableSupply + return { x: date, y: closingPrice * supply } + }) + setDataToLocalStorage('marketCapData', data) + return data +} + +// colors for light and dark theme +const priceLineColor = getPriceChartColor() +const mcapLineColor = getMarketCapChartColor() + +class MarketHistoryChart { + constructor (el, availableSupply, _marketHistoryData, dataConfig) { + const axes = config.options.scales + + let priceActivated = true + let marketCapActivated = true + + this.price = { + label: window.localized.Price, + yAxisID: 'price', + data: [], + fill: false, + cubicInterpolationMode: 'monotone', + pointRadius: 0, + backgroundColor: priceLineColor, + borderColor: priceLineColor + // lineTension: 0 + } + if (dataConfig.market === undefined || dataConfig.market.indexOf('price') === -1) { + this.price.hidden = true + axes.price.display = false + priceActivated = false + } + + this.marketCap = { + label: window.localized['Market Cap'], + yAxisID: 'marketCap', + data: [], + fill: false, + cubicInterpolationMode: 'monotone', + pointRadius: 0, + backgroundColor: mcapLineColor, + borderColor: mcapLineColor + // lineTension: 0 + } + if (dataConfig.market === undefined || dataConfig.market.indexOf('market_cap') === -1) { + this.marketCap.hidden = true + axes.marketCap.display = false + this.price.hidden = true + axes.price.display = false + marketCapActivated = false + } + + this.numTransactions = { + label: window.localized['Tx/day'], + yAxisID: 'numTransactions', + data: [], + cubicInterpolationMode: 'monotone', + fill: false, + pointRadius: 0, + backgroundColor: getTxChartColor(), + borderColor: getTxChartColor() + // lineTension: 0 + } + + if (dataConfig.transactions === undefined || dataConfig.transactions.indexOf('transactions_per_day') === -1) { + this.numTransactions.hidden = true + axes.numTransactions.display = false + } else if (!priceActivated && !marketCapActivated) { + axes.numTransactions.position = 'left' + } + + this.availableSupply = availableSupply + + const txChartTitle = 'Daily transactions' + const marketChartTitle = 'Daily price and market cap' + let chartTitle = '' + if (Object.keys(dataConfig).join() === 'transactions') { + chartTitle = txChartTitle + } else if (Object.keys(dataConfig).join() === 'market') { + chartTitle = marketChartTitle + } + config.options.plugins.title.text = chartTitle + + config.data.datasets = [this.price, this.marketCap, this.numTransactions] + + const isChartLoadedKey = 'isChartLoaded' + const isChartLoaded = window.sessionStorage.getItem(isChartLoadedKey) === 'true' + if (isChartLoaded) { + config.options.animation = false + } else { + window.sessionStorage.setItem(isChartLoadedKey, true) + } + + this.chart = new Chart(el, config) + } + + updateMarketHistory (availableSupply, marketHistoryData) { + this.price.data = getPriceData(marketHistoryData) + if (this.availableSupply !== null && typeof this.availableSupply === 'object') { + const today = new Date().toJSON().slice(0, 10) + this.availableSupply[today] = availableSupply + this.marketCap.data = getMarketCapData(marketHistoryData, this.availableSupply) + } else { + this.marketCap.data = getMarketCapData(marketHistoryData, availableSupply) + } + this.chart.update() + } + + updateTransactionHistory (transactionHistory) { + this.numTransactions.data = getTxHistoryData(transactionHistory) + this.chart.update() + } +} + +export function createMarketHistoryChart (el) { + const dataPaths = $(el).data('history_chart_paths') + const dataConfig = $(el).data('history_chart_config') + + const $chartError = $('[data-chart-error-message]') + const chart = new MarketHistoryChart(el, 0, [], dataConfig) + Object.keys(dataPaths).forEach(function (historySource) { + $.getJSON(dataPaths[historySource], { type: 'JSON' }) + .done(data => { + switch (historySource) { + case 'market': { + const availableSupply = JSON.parse(data.supply_data) + const marketHistoryData = humps.camelizeKeys(JSON.parse(data.history_data)) + + $(el).show() + chart.updateMarketHistory(availableSupply, marketHistoryData) + break + } + case 'transaction': { + const txsHistoryData = JSON.parse(data.history_data) + + $(el).show() + chart.updateTransactionHistory(txsHistoryData) + break + } + } + }) + .fail(() => { + $chartError.show() + }) + }) + return chart +} + +$('[data-chart-error-message]').on('click', _event => { + $('[data-chart-error-message]').hide() + createMarketHistoryChart($('[data-chart="historyChart"]')[0]) +}) diff --git a/apps/block_scout_web/assets/js/lib/indexing.js b/apps/block_scout_web/assets/js/lib/indexing.js new file mode 100644 index 0000000..e406d35 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/indexing.js @@ -0,0 +1,50 @@ +import $ from 'jquery' +import humps from 'humps' +import numeral from 'numeral' +import socket from '../socket' + +function tryUpdateIndexedStatus (el, indexedRatioBlocks = el.dataset.indexedRatioBlocks, indexedRatio = el.dataset.indexedRatio, indexingFinished = false) { + if (indexingFinished) return $("[data-selector='indexed-status']").remove() + const indexedRatioFloat = parseFloat(indexedRatio) + const indexedRatioBlocksFloat = parseFloat(indexedRatioBlocks) + + if (!isNaN(indexedRatioBlocksFloat)) { + el.dataset.indexedRatioBlocks = indexedRatioBlocks + } else if (!isNaN(indexedRatioFloat)) { + el.dataset.indexedRatio = indexedRatio + } + + const blocksPercentComplete = numeral(el.dataset.indexedRatioBlocks).format('0%') + let indexedText + if (blocksPercentComplete === '100%') { + const intTxsPercentComplete = numeral(el.dataset.indexedRatio).format('0%') + indexedText = `${intTxsPercentComplete} ${window.localized['Blocks With Internal Transactions Indexed']}` + } else { + indexedText = `${blocksPercentComplete} ${window.localized['Blocks Indexed']}` + } + + if (indexedText !== el.innerHTML) { + el.innerHTML = indexedText + } +} + +export function updateIndexStatus (msg = {}, type) { + $('[data-indexed-ratio]').each((i, el) => { + if (type === 'blocks') { + tryUpdateIndexedStatus(el, msg.ratio, null, msg.finished) + } else if (type === 'internal_transactions') { + tryUpdateIndexedStatus(el, null, msg.ratio, msg.finished) + } else { + tryUpdateIndexedStatus(el, null, null, msg.finished) + } + }) +} +updateIndexStatus() + +const IndexingChannelBlocks = socket.channel('blocks:indexing') +IndexingChannelBlocks.join() +IndexingChannelBlocks.on('index_status', (msg) => updateIndexStatus(humps.camelizeKeys(msg), 'blocks')) + +const indexingChannelInternalTransactions = socket.channel('blocks:indexing_internal_transactions') +indexingChannelInternalTransactions.join() +indexingChannelInternalTransactions.on('index_status', (msg) => updateIndexStatus(humps.camelizeKeys(msg), 'internal_transactions')) diff --git a/apps/block_scout_web/assets/js/lib/infinite_scroll_helpers.js b/apps/block_scout_web/assets/js/lib/infinite_scroll_helpers.js new file mode 100644 index 0000000..d43b1d4 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/infinite_scroll_helpers.js @@ -0,0 +1,107 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import humps from 'humps' +import { connectElements } from './redux_helpers.js' + +const initialState = { + loadingNextPage: false, + pagingError: false, + nextPageUrl: null +} + +function infiniteScrollReducer (state = initialState, action) { + switch (action.type) { + case 'INFINITE_SCROLL_ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'LOADING_NEXT_PAGE': { + return Object.assign({}, state, { + loadingNextPage: true + }) + } + case 'PAGING_ERROR': { + return Object.assign({}, state, { + loadingNextPage: false, + pagingError: true + }) + } + case 'RECEIVED_NEXT_PAGE': { + return Object.assign({}, state, { + loadingNextPage: false, + nextPageUrl: action.msg.nextPageUrl + }) + } + default: + return state + } +} + +const elements = { + '[data-selector="next-page-button"]': { + load ($el) { + return { + nextPageUrl: `${$el.hide().attr('href')}&type=JSON` + } + } + }, + '[data-selector="loading-next-page"]': { + render ($el, state) { + if (state.loadingNextPage) { + $el.show() + } else { + $el.hide() + } + } + }, + '[data-selector="paging-error-message"]': { + render ($el, state) { + if (state.pagingError) { + $el.show() + } + } + } +} + +export function withInfiniteScroll (reducer) { + return (state, action) => { + return infiniteScrollReducer(reducer(state, action), action) + } +} + +export function connectInfiniteScroll (store) { + connectElements({ store, elements, action: 'INFINITE_SCROLL_ELEMENTS_LOAD' }) + + onScrollBottom(() => { + const { loadingNextPage, nextPageUrl, pagingError } = store.getState() + if (!loadingNextPage && nextPageUrl && !pagingError) { + store.dispatch({ + type: 'LOADING_NEXT_PAGE' + }) + $.get(nextPageUrl) + .done(msg => { + store.dispatch({ + type: 'RECEIVED_NEXT_PAGE', + msg: humps.camelizeKeys(msg) + }) + }) + .fail(() => { + store.dispatch({ + type: 'PAGING_ERROR' + }) + }) + } + }) +} + +function onScrollBottom (callback) { + const $window = $(window) + function infiniteScrollChecker () { + const scrollHeight = $(document).height() + const scrollPosition = $window.height() + $window.scrollTop() + if ((scrollHeight - scrollPosition) / scrollHeight === 0) { + callback() + } + } + infiniteScrollChecker() + $window.on('scroll', infiniteScrollChecker) +} diff --git a/apps/block_scout_web/assets/js/lib/list_morph.js b/apps/block_scout_web/assets/js/lib/list_morph.js new file mode 100644 index 0000000..08dba71 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/list_morph.js @@ -0,0 +1,130 @@ +import $ from 'jquery' +import map from 'lodash.map' +import get from 'lodash.get' +import noop from 'lodash.noop' +import find from 'lodash.find' +import intersectionBy from 'lodash.intersectionby' +import differenceBy from 'lodash.differenceby' +import morph from 'nanomorph' +import { updateAllAges } from './from_now' + +// The goal of this function is to DOM diff lists, so upon completion `container.innerHTML` should be +// equivalent to `newElements.join('')`. +// +// We could simply do `container.innerHTML = newElements.join('')` but that would not be efficient and +// it not animate appropriately. We could also simply use `morph` (or a similar library) on the entire +// list, however that doesn't give us the proper amount of control for animations. +// +// This function will walk though, remove items currently in `container` which are not in the new list. +// Then it will swap the contents of the items that are in both lists in case the items were updated or +// the order changed. Finally, it will add elements to `container` which are in the new list and didn't +// already exist in the DOM. +// +// Params: +// container: the DOM element which contents need replaced +// newElements: a list of elements that need to be put into the container +// options: +// key: the path to the unique identifier of each element +// horizontal: our horizontal animations are handled in CSS, so passing in `true` will not play JS +// animations +export default function (container, newElements, { key, horizontal } = {}) { + if (!container) return + const oldElements = $(container).children().not('.shrink-out').get() + let currentList = map(oldElements, (el) => ({ id: get(el, key), el })) + const newList = map(newElements, (el) => ({ id: get(el, key), el })) + const overlap = intersectionBy(newList, currentList, 'id').map(({ id, el }) => ({ id, el: updateAllAges($(el))[0] })) + // remove old items + const removals = differenceBy(currentList, newList, 'id') + let canAnimate = false && !horizontal && newList.length > 0 // disabled animation in order to speed up UI + removals.forEach(({ el }) => { + if (!canAnimate) return el.remove() + const $el = $(el) + $el.addClass('shrink-out') + setTimeout(() => { slideUpRemove($el) }, 400) + }) + currentList = differenceBy(currentList, removals, 'id') + + // update kept items + currentList = currentList.map(({ el }, i) => { + if (overlap[i]) { + return ({ + id: overlap[i].id, + el: el.outerHTML === overlap[i].el && overlap[i].el.outerHTML ? el : morph(el, overlap[i].el) + }) + } else { + return null + } + }) + .filter(el => el !== null) + + // add new items + const finalList = newList.map(({ id, el }) => get(find(currentList, { id }), 'el', el)).reverse() + canAnimate = false && !horizontal // disabled animation in order to speed up UI + finalList.forEach((el, i) => { + if (el.parentElement) return + if (!canAnimate) return container.insertBefore(el, get(finalList, `[${i - 1}]`)) + if (!get(finalList, `[${i - 1}]`)) return slideDownAppend($(container), el) + slideDownBefore($(get(finalList, `[${i - 1}]`)), el) + }) +} + +function slideDownAppend ($container, content) { + smarterSlideDown($(content), { + insert ($el) { + $container.append($el) + } + }) +} +function slideDownBefore ($container, content) { + smarterSlideDown($(content), { + insert ($el) { + $container.before($el) + } + }) +} +function slideUpRemove ($el) { + smarterSlideUp($el, { + complete () { + $el.remove() + } + }) +} + +function smarterSlideDown ($el, { insert = noop } = {}) { + if (!$el.length) return + const originalScrollHeight = document.body.scrollHeight + const scrollPosition = window.scrollY + + insert($el) + + const isAboveViewport = $el.offset().top < scrollPosition + + if (isAboveViewport) { + const heightDiffAfterInsert = document.body.scrollHeight - originalScrollHeight + const scrollPositionToMaintainViewport = scrollPosition + heightDiffAfterInsert + + $(window).scrollTop(scrollPositionToMaintainViewport) + } else { + $el.hide() + $el.slideDown({ easing: 'linear' }) + } +} + +function smarterSlideUp ($el, { complete = noop } = {}) { + if (!$el.length) return + const originalScrollHeight = document.body.scrollHeight + const scrollPosition = window.scrollY + const isAboveViewport = $el.offset().top + $el.outerHeight(true) < scrollPosition + + if (isAboveViewport) { + $el.hide() + + const heightDiffAfterHide = document.body.scrollHeight - originalScrollHeight + const scrollPositionToMaintainViewport = scrollPosition + heightDiffAfterHide + + $(window).scrollTop(scrollPositionToMaintainViewport) + complete() + } else { + $el.slideUp({ complete, easing: 'linear' }) + } +} diff --git a/apps/block_scout_web/assets/js/lib/loading_element.js b/apps/block_scout_web/assets/js/lib/loading_element.js new file mode 100644 index 0000000..532eb1c --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/loading_element.js @@ -0,0 +1,5 @@ +import $ from 'jquery' + +$('button[data-loading="animation"]').click(_event => { + $('#loading').removeClass('d-none') +}) diff --git a/apps/block_scout_web/assets/js/lib/modals.js b/apps/block_scout_web/assets/js/lib/modals.js new file mode 100644 index 0000000..97f3c20 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/modals.js @@ -0,0 +1,196 @@ +import $ from 'jquery' + +let $currentModal = null +let modalLocked = false + +const spinner = + ` + + + + + ` + +$(document.body).on('hide.bs.modal', e => { + if (modalLocked) { + e.preventDefault() + e.stopPropagation() + return false + } + + $currentModal = null +}) + +export function currentModal () { + return $currentModal +} + +export function openModal ($modal, unclosable) { + // Hide all tooltips before showing a modal, + // since they are sticking on top of modal + $('.tooltip').tooltip('hide') + + if (unclosable) { + $('.close-modal, .modal-status-button-wrapper', $modal).addClass('hidden') + $('.modal-status-text', $modal).addClass('m-b-0') + } + + if ($currentModal) { + modalLocked = false + + $currentModal + .one('hidden.bs.modal', () => { + $modal.modal('show') + $currentModal = $modal + if (unclosable) { + modalLocked = true + } + }) + .modal('hide') + } else { + $modal.modal('show') + $currentModal = $modal + if (unclosable) { + modalLocked = true + } + } +} + +export function openModalWithMessage ($modal, unclosable, message) { + $modal.find('.modal-message').text(message) + openModal($modal, unclosable) +} + +export function lockModal ($modal, $submitButton = null, spinnerText = '') { + $modal.find('.close-modal').attr('disabled', true) + + const $button = $submitButton || $modal.find('.btn-add-full') + + $button + .attr('data-text', $button.text()) + .attr('disabled', true) + + const $span = $('span', $button) + const waitHtml = spinner + (spinnerText ? ` ${spinnerText}` : '') + + if ($span.length) { + $('svg', $button).hide() + $span.html(waitHtml) + } else { + $button.html(waitHtml) + } + + modalLocked = true +} + +export function unlockModal ($modal, $submitButton = null) { + $modal.find('.close-modal').attr('disabled', false) + + const $button = $submitButton || $modal.find('.btn-add-full') + const buttonText = $button.attr('data-text') + + $button.attr('disabled', false) + + const $span = $('span', $button) + if ($span.length) { + $('svg', $button).show() + $span.text(buttonText) + } else { + $button.text(buttonText) + } + + modalLocked = false +} + +export function openErrorModal (title, text, unclosable) { + const $modal = $('#errorStatusModal') + $modal.find('.modal-status-title').text(title) + $modal.find('.modal-status-text').html(text) + openModal($modal, unclosable) +} + +export function openWarningModal (title, text) { + const $modal = $('#warningStatusModal') + $modal.find('.modal-status-title').text(title) + $modal.find('.modal-status-text').html(text) + openModal($modal) +} + +export function openSuccessModal (title, text) { + const $modal = $('#successStatusModal') + $modal.find('.modal-status-title').text(title) + $modal.find('.modal-status-text').html(text) + openModal($modal) +} + +export function openQuestionModal (title, text, acceptCallback = null, exceptCallback = null, acceptText = 'Yes', exceptText = 'No') { + const $modal = $('#questionStatusModal') + const $closeButton = $modal.find('.close-modal') + + $closeButton.attr('disabled', false) + + $modal.find('.modal-status-title').text(title) + $modal.find('.modal-status-text').text(text) + + const $accept = $modal.find('.btn-line.accept') + const $except = $modal.find('.btn-line.except') + + $accept + .removeAttr('data-dismiss') + .removeAttr('disabled') + .unbind('click') + .find('.btn-line-text').text(acceptText) + + $except + .removeAttr('data-dismiss') + .removeAttr('disabled') + .unbind('click') + .find('.btn-line-text').text(exceptText) + + if (acceptCallback) { + $accept.on('click', event => { + $closeButton.attr('disabled', true) + + $accept + .unbind('click') + .attr('disabled', true) + .find('.btn-line-text').html(spinner) + $except + .unbind('click') + .removeAttr('data-dismiss') + .attr('disabled', true) + + modalLocked = true + acceptCallback($modal, event) + }) + } else { + $accept.attr('data-dismiss', 'modal') + } + + if (exceptCallback) { + $except.on('click', event => { + $closeButton.attr('disabled', true) + + $except + .unbind('click') + .attr('disabled', true) + .find('.btn-line-text').html(spinner) + $accept + .unbind('click') + .attr('disabled', true) + .removeAttr('data-dismiss') + + modalLocked = true + exceptCallback($modal, event) + }) + } else { + $except.attr('data-dismiss', 'modal') + } + + openModal($modal) +} + +export function openQrModal () { + const $modal = $('#qrModal') + openModal($modal) +} diff --git a/apps/block_scout_web/assets/js/lib/pending_transactions_toggle.js b/apps/block_scout_web/assets/js/lib/pending_transactions_toggle.js new file mode 100644 index 0000000..26c2846 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/pending_transactions_toggle.js @@ -0,0 +1,20 @@ +import $ from 'jquery' + +const pendingTransactionToggle = (element) => { + const $element = $(element) + const $pendingTransactionsClose = $element.find("[data-selector='pending-transactions-close']") + const $pendingTransactionsOpen = $element.find("[data-selector='pending-transactions-open']") + + $element.on('show.bs.collapse', () => { + $pendingTransactionsOpen.addClass('d-none') + $pendingTransactionsClose.removeClass('d-none') + }) + + $element.on('hide.bs.collapse', () => { + $pendingTransactionsClose.addClass('d-none') + $pendingTransactionsOpen.removeClass('d-none') + }) +} + +// Initialize the script scoped for each instance. +$("[data-selector='pending-transactions-toggle']").each((_index, element) => pendingTransactionToggle(element)) diff --git a/apps/block_scout_web/assets/js/lib/pretty_json.js b/apps/block_scout_web/assets/js/lib/pretty_json.js new file mode 100644 index 0000000..33a921d --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/pretty_json.js @@ -0,0 +1,9 @@ +import $ from 'jquery' + +function prettyPrint (element) { + const jsonString = element.dataset.json + const pretty = JSON.stringify(JSON.parse(jsonString), undefined, 2) + element.innerHTML = pretty +} + +$('[data-json]').each((_index, element) => prettyPrint(element)) diff --git a/apps/block_scout_web/assets/js/lib/public_tags_request_form.js b/apps/block_scout_web/assets/js/lib/public_tags_request_form.js new file mode 100644 index 0000000..a1f88a3 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/public_tags_request_form.js @@ -0,0 +1,43 @@ +import $ from 'jquery' + +const $removeButton = $('.remove-form-field')[0] +const $container = $('#' + $removeButton.dataset.container) +const index = parseInt($container[0].dataset.index) + +if (index <= 1) { + $('.remove-form-field').hide() +} + +$('.add-form-field').on('click', (event) => { + event.preventDefault() + console.log(event) + const $container = $('#' + event.currentTarget.dataset.container) + const index = parseInt($container[0].dataset.index) + if (index < 10) { + $container.append($.parseHTML(event.currentTarget.dataset.prototype)) + $container[0].dataset.index = index + 1 + } + if (index >= 9) { + $('.add-form-field').hide() + } + if (index <= 1) { + $('.remove-form-field').show() + } +}) + +$('[data-multiple-input-field-container]').on('click', '.remove-form-field', (event) => { + event.preventDefault() + console.log(event) + const $container = $('#' + event.currentTarget.dataset.container) + const index = parseInt($container[0].dataset.index) + if (index > 1) { + $container[0].dataset.index = index - 1 + event.currentTarget.parentElement.remove() + } + if (index >= 10) { + $('.add-form-field').show() + } + if (index <= 2) { + $('.remove-form-field').hide() + } +}) diff --git a/apps/block_scout_web/assets/js/lib/queue.js b/apps/block_scout_web/assets/js/lib/queue.js new file mode 100644 index 0000000..beb5a61 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/queue.js @@ -0,0 +1,72 @@ +/* + +Queue.js + +A function to represent a queue + +Created by Kate Morley - http://code.iamkate.com/ - and released under the terms +of the CC0 1.0 Universal legal code: + +http://creativecommons.org/publicdomain/zero/1.0/legalcode + +*/ + +/* eslint-disable */ + +/* Creates a new queue. A queue is a first-in-first-out (FIFO) data structure - + * items are added to the end of the queue and removed from the front. + */ +export default function Queue(){ + + // initialise the queue and offset + var queue = []; + var offset = 0; + + // Returns the length of the queue. + this.getLength = function(){ + return (queue.length - offset); + } + + // Returns true if the queue is empty, and false otherwise. + this.isEmpty = function(){ + return (queue.length == 0); + } + + /* Enqueues the specified item. The parameter is: + * + * item - the item to enqueue + */ + this.enqueue = function(item){ + queue.push(item); + } + + /* Dequeues an item and returns it. If the queue is empty, the value + * 'undefined' is returned. + */ + this.dequeue = function(){ + + // if the queue is empty, return immediately + if (queue.length == 0) return undefined; + + // store the item at the front of the queue + var item = queue[offset]; + + // increment the offset and remove the free space if necessary + if (++ offset * 2 >= queue.length){ + queue = queue.slice(offset); + offset = 0; + } + + // return the dequeued item + return item; + + } + + /* Returns the item at the front of the queue (without dequeuing it). If the + * queue is empty then undefined is returned. + */ + this.peek = function(){ + return (queue.length > 0 ? queue[offset] : undefined); + } + +} diff --git a/apps/block_scout_web/assets/js/lib/random_access_pagination.js b/apps/block_scout_web/assets/js/lib/random_access_pagination.js new file mode 100644 index 0000000..bc36d26 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/random_access_pagination.js @@ -0,0 +1,369 @@ +import $ from 'jquery' +import map from 'lodash.map' +import merge from 'lodash.merge' +import humps from 'humps' +import URI from 'urijs' +import listMorph from '../lib/list_morph' +import reduceReducers from 'reduce-reducers' +import { createStore, connectElements } from '../lib/redux_helpers.js' +import '../app' + +const maxPageNumberInOneLine = 7 +const groupedPagesNumber = 3 + +/** + * + * This module is a clone of async_listing_load.js adapted for pagination with random access + * + */ + +let enableFirstLoading = true + +export const asyncInitialState = { + /* it will consider any query param in the current URI as paging */ + beyondPageOne: false, + /* will be sent along with { type: 'JSON' } to controller, useful for dynamically changing parameters */ + additionalParams: {}, + /* an array with every html element of the list being shown */ + items: [], + /* the key for diffing the elements in the items array */ + itemKey: null, + /* represents whether a request is happening or not */ + loading: false, + /* if there was an error fetching items */ + requestError: false, + /* if response has no items */ + emptyResponse: false, + /* current's page number */ + currentPageNumber: 0 +} + +export function asyncReducer (state = asyncInitialState, action) { + switch (action.type) { + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, { + nextPagePath: action.nextPagePath, + currentPagePath: action.nextPagePath + }) + } + case 'ADD_ITEM_KEY': { + return Object.assign({}, state, { itemKey: action.itemKey }) + } + case 'START_REQUEST': { + let pageNumber = state.currentPageNumber + if (action.pageNumber) { pageNumber = parseInt(action.pageNumber) } + + return Object.assign({}, state, { + loading: true, + requestError: false, + currentPagePath: action.path, + currentPageNumber: pageNumber, + items: generateStub(state.items.length) + }) + } + case 'REQUEST_ERROR': { + return Object.assign({}, state, { requestError: true }) + } + case 'FINISH_REQUEST': { + return Object.assign({}, state, { + loading: false + }) + } + case 'ITEMS_FETCHED': { + if (action.nextPageParams !== null) { + const pageNumber = parseInt(action.nextPageParams.pageNumber) + if (typeof action.path !== 'undefined') { + history.replaceState({}, null, URI(action.path).query(humps.decamelizeKeys(action.nextPageParams))) + } + delete action.nextPageParams.pageNumber + + return Object.assign({}, state, { + requestError: false, + emptyResponse: action.items.length === 0, + items: action.items, + nextPageParams: humps.decamelizeKeys(action.nextPageParams), + pagesLimit: parseInt(action.nextPageParams.pagesLimit), + currentPageNumber: pageNumber, + beyondPageOne: pageNumber !== 1 + }) + } + return Object.assign({}, state, { + requestError: false, + emptyResponse: action.items.length === 0, + items: action.items, + nextPageParams: humps.decamelizeKeys(action.nextPageParams), + pagesLimit: 1, + currentPageNumber: 1, + beyondPageOne: false + }) + } + default: + return state + } +} + +export const elements = { + '[data-async-listing]': { + load ($el) { + const nextPagePath = $el.data('async-listing') + + return { nextPagePath } + } + }, + '[data-async-listing] [data-loading-message]': { + render ($el, state) { + if (state.loading) return $el.show() + + $el.hide() + } + }, + '[data-async-listing] [data-empty-response-message]': { + render ($el, state) { + if ( + !state.requestError && + (!state.loading) && + state.items.length === 0 + ) { + return $el.show() + } + + $el.hide() + } + }, + '[data-async-listing] [data-error-message]': { + render ($el, state) { + if (state.requestError) return $el.show() + + $el.hide() + } + }, + '[data-async-listing] [data-items]': { + render ($el, state, oldState) { + if (state.items === oldState.items) return + + if (state.itemKey) { + const container = $el[0] + const newElements = map(state.items, (item) => $(item)[0]) + listMorph(container, newElements, { key: state.itemKey }) + return + } + + $el.html(state.items) + } + }, + '[data-async-listing] [data-next-page-button]': { + render ($el, state) { + if (state.emptyResponse) { + return $el.hide() + } + + $el.show() + if (state.requestError || state.currentPageNumber >= state.pagesLimit || state.loading) { + return $el.attr('disabled', 'disabled') + } + + $el.attr('disabled', false) + $el.attr('href', state.nextPagePath) + } + }, + '[data-async-listing] [data-prev-page-button]': { + render ($el, state) { + if (state.emptyResponse) { + return $el.hide() + } + + $el.show() + if (state.requestError || state.currentPageNumber <= 1 || state.loading) { + return $el.attr('disabled', 'disabled') + } + + $el.attr('disabled', false) + $el.attr('href', state.prevPagePath) + } + }, + '[data-async-listing] [pages-numbers-container]': { + render ($el, state) { + if (typeof state.pagesLimit !== 'undefined') { pagesNumbersGenerate(state.pagesLimit, $el, state.currentPageNumber, state.loading) } + } + }, + '[data-async-listing] [data-loading-button]': { + render ($el, state) { + if (state.loading) return $el.show() + + $el.hide() + } + }, + '[data-async-listing] [data-pagination-container]': { + render ($el, state) { + if (state.emptyResponse) { + return $el.hide() + } + + $el.show() + } + }, + '[csv-download]': { + render ($el, state) { + if (state.emptyResponse) { + return $el.hide() + } + return $el.show() + } + } +} + +/** + * Create a store combining the given reducer and initial state with the async reducer. + * + * reducer: The reducer that will be merged with the asyncReducer to add async + * loading capabilities to a page. Any state changes in the reducer passed will be + * applied AFTER the asyncReducer. + * + * initialState: The initial state to be merged with the async state. Any state + * values passed here will overwrite the values on asyncInitialState. + * + * itemKey: it will be added to the state as the key for diffing the elements and + * adding or removing with the correct animation. Check list_morph.js for more informantion. + */ +export function createAsyncLoadStore (reducer, initialState, itemKey) { + const state = merge(asyncInitialState, initialState) + const store = createStore(reduceReducers(state, asyncReducer, reducer)) + + if (typeof itemKey !== 'undefined') { + store.dispatch({ + type: 'ADD_ITEM_KEY', + itemKey + }) + } + + connectElements({ store, elements }) + firstPageLoad(store) + return store +} + +export function refreshPage (store) { + loadPageByNumber(store, store.getState().currentPageNumber) +} + +export function loadPageByNumber (store, pageNumber) { + const path = $('[data-async-listing]').data('async-listing') + store.dispatch({ type: 'START_REQUEST', path, pageNumber }) + if (URI(path).query() !== '' && typeof store.getState().nextPageParams === 'undefined') { + $.getJSON(path, { type: 'JSON' }) + .done(response => store.dispatch(Object.assign({ type: 'ITEMS_FETCHED', path }, humps.camelizeKeys(response)))) + .fail(() => store.dispatch({ type: 'REQUEST_ERROR' })) + .always(() => store.dispatch({ type: 'FINISH_REQUEST' })) + } else { + $.getJSON(URI(path).path(), merge({ type: 'JSON', page_number: pageNumber }, store.getState().nextPageParams)) + .done(response => store.dispatch(Object.assign({ type: 'ITEMS_FETCHED', path }, humps.camelizeKeys(response)))) + .fail(() => store.dispatch({ type: 'REQUEST_ERROR' })) + .always(() => store.dispatch({ type: 'FINISH_REQUEST' })) + } +} + +function firstPageLoad (store) { + const $element = $('[data-async-listing]') + function loadItemsNext () { + loadPageByNumber(store, store.getState().currentPageNumber + 1) + } + + function loadItemsPrev () { + loadPageByNumber(store, store.getState().currentPageNumber - 1) + } + + if (enableFirstLoading) { + loadItemsNext() + } + + $element.on('click', '[data-error-message]', (event) => { + event.preventDefault() + loadItemsNext() + }) + + $element.on('click', '[data-next-page-button]', (event) => { + event.preventDefault() + loadItemsNext() + }) + + $element.on('click', '[data-prev-page-button]', (event) => { + event.preventDefault() + loadItemsPrev() + }) + + $element.on('click', '[data-page-number]', (event) => { + event.preventDefault() + loadPageByNumber(store, event.target.dataset.pageNumber) + }) + + $element.on('submit', '[input-page-number-form]', (event) => { + event.preventDefault() + const $input = event.target.querySelector('#page-number') + const input = parseInt($input.value) + const loading = store.getState().loading + const pagesLimit = store.getState().pagesLimit + if (!isNaN(input) && input <= pagesLimit && !loading) { loadPageByNumber(store, input) } + if (!loading || isNaN(input) || input > pagesLimit) { $input.value = '' } + return false + }) +} + +const $element = $('[data-async-load]') +if ($element.length) { + if (Object.prototype.hasOwnProperty.call($element.data(), 'noFirstLoading')) { + enableFirstLoading = false + } + if (enableFirstLoading) { + const store = createStore(asyncReducer) + connectElements({ store, elements }) + firstPageLoad(store) + } +} + +function pagesNumbersGenerate (pagesLimit, $container, currentPageNumber, loading) { + let resultHTML = '' + if (pagesLimit < 1) { return } + if (pagesLimit <= maxPageNumberInOneLine) { + resultHTML = renderPaginationElements(1, pagesLimit, currentPageNumber, loading) + } else if (currentPageNumber < groupedPagesNumber) { + resultHTML += renderPaginationElements(1, groupedPagesNumber, currentPageNumber, loading) + resultHTML += renderPaginationElement('...', false, loading) + resultHTML += renderPaginationElement(pagesLimit, currentPageNumber === pagesLimit, loading) + } else if (currentPageNumber > pagesLimit - groupedPagesNumber) { + resultHTML += renderPaginationElement(1, currentPageNumber === 1, loading) + resultHTML += renderPaginationElement('...', false, loading) + resultHTML += renderPaginationElements(pagesLimit - groupedPagesNumber, pagesLimit, currentPageNumber, loading) + } else { + resultHTML += renderPaginationElement(1, currentPageNumber === 1, loading) + const step = parseInt(groupedPagesNumber / 2) + if (currentPageNumber - step - 1 === 2) { + resultHTML += renderPaginationElement(2, currentPageNumber === 2, loading) + } else if (currentPageNumber - step > 2) { + resultHTML += renderPaginationElement('...', false, loading) + } + resultHTML += renderPaginationElements(currentPageNumber - step, currentPageNumber + step, currentPageNumber, loading) + if (currentPageNumber + step + 1 === pagesLimit - 1) { + resultHTML += renderPaginationElement(pagesLimit - 1, pagesLimit - 1 === currentPageNumber, loading) + } else if (currentPageNumber + step < pagesLimit - 1) { + resultHTML += renderPaginationElement('...', false, loading) + } + resultHTML += renderPaginationElement(pagesLimit, currentPageNumber === pagesLimit, loading) + } + $container.html(resultHTML) +} + +function renderPaginationElements (start, end, currentPageNumber, loading) { + let resultHTML = '' + for (let i = start; i <= end; i++) { + resultHTML += renderPaginationElement(i, i === currentPageNumber, loading) + } + return resultHTML +} + +function renderPaginationElement (text, active, loading) { + return '
  • ' + text + '
  • ' +} + +function generateStub (size) { + const stub = '
    ' + return Array.from(Array(size > 10 ? 10 : 10), () => stub) // I decided to always put 10 lines in order to make page lighter +} diff --git a/apps/block_scout_web/assets/js/lib/redux_helpers.js b/apps/block_scout_web/assets/js/lib/redux_helpers.js new file mode 100644 index 0000000..93af7f8 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/redux_helpers.js @@ -0,0 +1,128 @@ +import $ from 'jquery' +import reduce from 'lodash.reduce' +import isObject from 'lodash.isobject' +import forIn from 'lodash.forin' +import { createStore as reduxCreateStore } from 'redux' + +/** + * Create a redux store given the reducer. It also enables the Redux dev tools. + */ +export function createStore (reducer) { + return reduxCreateStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()) +} + +/** + * Connect elements with the redux store. It must receive an object with the following attributes: + * + * elements: It is an object with elements that are going to react to the redux state or add something + * to the initial state. + * + * ```javascript + * const elements = { + * // The JQuery selector for finding elements in the page. + * '[data-counter]': { + * // Useful to put things from the page to the redux state. + * load ($element) {...}, + * // Check for state changes and manipulates the DOM accordingly. + * render ($el, state, oldState) {...} + * } + * } + * ``` + * + * The load and render functions are optional, you can have both or just one of them. It depends + * on if you want to load something to the state in the first render and/or that the element should + * react to the redux state. Notice that you can include more elements if you want to since elements + * also is an object. + * + * store: It is the redux store that the elements should be connected with. + * ```javascript + * const store = createStore(reducer) + * ``` + * + * action: The first action that the store is going to dispatch. Optional, by default 'ELEMENTS_LOAD' + * is going to be dispatched. + * + * ### Examples + * + * Given the markup: + * ```HTML + *
    + * 1 + *
    + * ``` + * + * The reducer: + * ```javascript + * function reducer (state = { number: null }, action) { + * switch (action.type) { + * case 'ELEMENTS_LOAD': { + * return Object.assign({}, state, { number: action.number }) + * } + * case 'INCREMENT': { + * return Object.assign({}, state, { number: state.number + 1 }) + * } + * default: + * return state + * } + * } + * ``` + * + * The elements: + * ```javascript + * const elements = { + * // '[data-counter]' is the element that will be connected to the redux store. + * '[data-counter]': { + * // Find the number within data-counter and add to the state. + * load ($el) { + * return { number: $el.find('.number').val() } + * }, + * // React to redux state. Case the number in the state changes, it is going to render the + * // new number. + * render ($el, state, oldState) { + * if (state.number === oldState.number) return + * + * $el.html(state.number) + * } + * } + * } + * + * All we need to do is connecting the store and the elements using this function. + * ```javascript + * connectElements({store, elements}) + * ``` + * + * Now, if we dispatch the `INCREMENT` action, the state is going to change and the [data-counter] + * element is going to re-render since they are connected. + * ```javascript + * store.dispatch({type: 'INCREMENT'}) + * ``` + */ +export function connectElements ({ elements, store, action = 'ELEMENTS_LOAD' }) { + function loadElements () { + return reduce(elements, (pageLoadParams, { load }, selector) => { + if (!load) return pageLoadParams + const $el = $(selector) + if (!$el.length) return pageLoadParams + const morePageLoadParams = load($el, store) + return isObject(morePageLoadParams) ? Object.assign(pageLoadParams, morePageLoadParams) : pageLoadParams + }, {}) + } + + function renderElements (state, oldState) { + forIn(elements, ({ render }, selector) => { + if (!render) return + const $el = $(selector) + if (!$el.length) return + render($el, state, oldState) + }) + } + + let oldState = store.getState() + store.subscribe(() => { + const state = store.getState() + renderElements(state, oldState) + oldState = state + }) + + store.dispatch(Object.assign(loadElements(), { type: action })) +} diff --git a/apps/block_scout_web/assets/js/lib/reload_button.js b/apps/block_scout_web/assets/js/lib/reload_button.js new file mode 100644 index 0000000..1cb71be --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/reload_button.js @@ -0,0 +1,3 @@ +import $ from 'jquery' + +$('[data-selector="reload-button"]').click(() => window.location.reload()) diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js b/apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js new file mode 100644 index 0000000..9b191e1 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/smart_contract/common_helpers.js @@ -0,0 +1,312 @@ +import Web3 from 'web3' +import $ from 'jquery' +import { props } from 'eth-net-props' + +export const connectSelector = '[connect-wallet]' +export const disconnectSelector = '[disconnect-wallet]' +const connectToSelector = '[connect-to]' +const connectedToSelector = '[connected-to]' + +export function getContractABI ($form) { + const implementationAbi = $form.data('implementation-abi') + const parentAbi = $form.data('contract-abi') + const $parent = $('[data-smart-contract-functions]') + const contractType = $parent.data('type') + const contractAbi = contractType === 'proxy' ? implementationAbi : parentAbi + return contractAbi +} + +export function getMethodInputs (contractAbi, functionName) { + const functionAbi = contractAbi.find(abi => + abi.name === functionName + ) + return functionAbi && functionAbi.inputs +} + +export function prepareMethodArgs ($functionInputs, inputs) { + return $.map($functionInputs, (element, ind) => { + const inputValue = $(element).val() + const inputType = inputs[ind] && inputs[ind].type + const inputComponents = inputs[ind] && inputs[ind].components + let sanitizedInputValue + sanitizedInputValue = replaceDoubleQuotes(inputValue, inputType, inputComponents) + sanitizedInputValue = replaceSpaces(sanitizedInputValue, inputType, inputComponents) + + if (isArrayInputType(inputType) || isTupleInputType(inputType)) { + if (sanitizedInputValue === '' || sanitizedInputValue === '[]') { + return [[]] + } else { + if (isArrayOfTuple(inputType)) { + const sanitizedInputValueElements = JSON.parse(sanitizedInputValue).map((elementValue, index) => { + return sanitizeMutipleInputValues(elementValue, inputType, inputComponents) + }) + return [sanitizedInputValueElements] + } else { + if (sanitizedInputValue.startsWith('[') && sanitizedInputValue.endsWith(']')) { + sanitizedInputValue = sanitizedInputValue.substring(1, sanitizedInputValue.length - 1) + } + const inputValueElements = sanitizedInputValue.split(',') + const sanitizedInputValueElements = sanitizeMutipleInputValues(inputValueElements, inputType, inputComponents) + return [sanitizedInputValueElements] + } + } + } else { return convertToBool(sanitizedInputValue, inputType) } + }) +} + +function sanitizeMutipleInputValues (inputValueElements, inputType, inputComponents) { + return inputValueElements.map((elementValue, index) => { + let elementInputType + if (inputType.includes('tuple')) { + elementInputType = inputComponents[index].type + } else { + elementInputType = inputType.split('[')[0] + } + + let sanitizedElementValue = replaceDoubleQuotes(elementValue, elementInputType) + sanitizedElementValue = replaceSpaces(sanitizedElementValue, elementInputType) + sanitizedElementValue = convertToBool(sanitizedElementValue, elementInputType) + + return sanitizedElementValue + }) +} + +export function compareChainIDs (explorerChainId, walletChainIdHex) { + if (explorerChainId !== parseInt(walletChainIdHex)) { + const networkDisplayNameFromWallet = props.getNetworkDisplayName(walletChainIdHex) + const networkDisplayName = props.getNetworkDisplayName(explorerChainId) + const errorMsg = `You connected to ${networkDisplayNameFromWallet} chain in the wallet, but the current instance of Blockscout is for ${networkDisplayName} chain` + return Promise.reject(new Error(errorMsg)) + } else { + return Promise.resolve() + } +} + +export const formatError = (error) => { + let { message } = error + message = message && message.split('Error: ').length > 1 ? message.split('Error: ')[1] : message + return message +} + +export const formatTitleAndError = (error) => { + let { message } = error + let title = message && message.split('Error: ').length > 1 ? message.split('Error: ')[1] : message + title = title && title.split('{').length > 1 ? title.split('{')[0].replace(':', '') : title + let txHash = '' + let errorMap = '' + try { + errorMap = message && message.indexOf('{') >= 0 ? JSON.parse(message && message.slice(message.indexOf('{'))) : '' + message = errorMap.error || '' + txHash = errorMap.transactionHash || '' + } catch (exception) { + message = '' + } + return { title, message, txHash } +} + +export const getCurrentAccountPromise = (provider) => { + return new Promise((resolve, reject) => { + if (provider && provider.wc) { + getCurrentAccountFromWCPromise(provider) + .then(account => resolve(account)) + .catch(err => { + reject(err) + }) + } else { + getCurrentAccountFromMMPromise() + .then(account => resolve(account)) + .catch(err => { + reject(err) + }) + } + }) +} + +export const getCurrentAccountFromWCPromise = (provider) => { + return new Promise((resolve, reject) => { + // Get a Web3 instance for the wallet + const web3 = new Web3(provider) + + // Get list of accounts of the connected wallet + web3.eth.getAccounts() + .then(accounts => { + // MetaMask does not give you all accounts, only the selected account + resolve(accounts[0]) + }) + .catch(err => { + reject(err) + }) + }) +} + +export const getCurrentAccountFromMMPromise = () => { + return new Promise((resolve, reject) => { + window.ethereum.request({ method: 'eth_accounts' }) + .then(accounts => { + const account = accounts[0] ? accounts[0].toLowerCase() : null + resolve(account) + }) + .catch(err => { + reject(err) + }) + }) +} + +function hideConnectedToContainer () { + document.querySelector(connectedToSelector) && document.querySelector(connectedToSelector).classList.add('hidden') +} + +function showConnectedToContainer () { + document.querySelector(connectedToSelector) && document.querySelector(connectedToSelector).classList.remove('hidden') +} + +function hideConnectContainer () { + document.querySelector(connectSelector) && document.querySelector(connectSelector).classList.add('hidden') +} + +function showConnectContainer () { + document.querySelector(connectSelector) && document.querySelector(connectSelector).classList.remove('hidden') +} + +function hideConnectToContainer () { + document.querySelector(connectToSelector) && document.querySelector(connectToSelector).classList.add('hidden') +} + +function showConnectToContainer () { + document.querySelector(connectToSelector) && document.querySelector(connectToSelector).classList.remove('hidden') +} + +export function showHideDisconnectButton () { + // Show disconnect button only in case of Wallet Connect + if (window.web3 && window.web3.currentProvider && window.web3.currentProvider.wc) { + document.querySelector(disconnectSelector) && document.querySelector(disconnectSelector).classList.remove('hidden') + } else { + document.querySelector(disconnectSelector) && document.querySelector(disconnectSelector).classList.add('hidden') + } +} + +export function showConnectedToElements (account) { + hideConnectToContainer() + showConnectContainer() + showConnectedToContainer() + showHideDisconnectButton() + setConnectToAddress(account) +} + +export function showConnectElements () { + showConnectToContainer() + showConnectContainer() + hideConnectedToContainer() +} + +export function hideConnectButton () { + showConnectToContainer() + hideConnectContainer() + hideConnectedToContainer() +} + +function setConnectToAddress (account) { + if (document.querySelector('[connected-to-address]')) { + document.querySelector('[connected-to-address]').innerHTML = `${trimmedAddressHash(account)}` + } +} + +function trimmedAddressHash (account) { + if ($(window).width() < 544) { + return `${account.slice(0, 7)}–${account.slice(-6)}` + } else { + return account + } +} + +function convertToBool (value, type) { + if (isBoolInputType(type)) { + const boolVal = (value === 'true' || value === '1' || value === 1) + + return boolVal + } else { + return value + } +} + +function isArrayInputType (inputType) { + return inputType && inputType.includes('[') && inputType.includes(']') +} + +function isTupleInputType (inputType) { + return inputType && inputType.includes('tuple') && !isArrayInputType(inputType) +} + +function isArrayOfTuple (inputType) { + return inputType && inputType.includes('tuple') && isArrayInputType(inputType) +} + +function isAddressInputType (inputType) { + return inputType && inputType.includes('address') && !isArrayInputType(inputType) +} + +function isUintInputType (inputType) { + return inputType && inputType.includes('int') && !isArrayInputType(inputType) +} + +function isStringInputType (inputType) { + return inputType && inputType.includes('string') && !isArrayInputType(inputType) +} + +function isBytesInputType (inputType) { + return inputType && inputType.includes('bytes') && !isArrayInputType(inputType) +} + +function isBoolInputType (inputType) { + return inputType && inputType.includes('bool') && !isArrayInputType(inputType) +} + +function isNonSpaceInputType (inputType) { + return isAddressInputType(inputType) || isBytesInputType(inputType) || inputType.includes('int') || inputType.includes('bool') +} + +function replaceSpaces (value, type, components) { + if (isNonSpaceInputType(type) && isFunction(value.replace)) { + return value.replace(/\s/g, '') + } else if (isTupleInputType(type) && isFunction(value.split)) { + return value + .split(',') + .map((itemValue, itemIndex) => { + const itemType = components && components[itemIndex] && components[itemIndex].type + + return replaceSpaces(itemValue, itemType) + }) + .join(',') + } else { + if (typeof value.trim === 'function') { + return value.trim() + } + return value + } +} + +function replaceDoubleQuotes (value, type, components) { + if (isAddressInputType(type) || isUintInputType(type) || isStringInputType(type) || isBytesInputType(type)) { + if (isFunction(value.replaceAll)) { + return value.replaceAll('"', '') + } else if (isFunction(value.replace)) { + return value.replace(/"/g, '') + } + return value + } else if (isTupleInputType(type) && isFunction(value.split)) { + return value + .split(',') + .map((itemValue, itemIndex) => { + const itemType = components && components[itemIndex] && components[itemIndex].type + + return replaceDoubleQuotes(itemValue, itemType) + }) + .join(',') + } else { + return value + } +} + +function isFunction (param) { + return typeof param === 'function' +} diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/connect.js b/apps/block_scout_web/assets/js/lib/smart_contract/connect.js new file mode 100644 index 0000000..ca896ef --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/smart_contract/connect.js @@ -0,0 +1,179 @@ +import Web3 from 'web3' +import Web3Modal from 'web3modal' +import WalletConnectProvider from '@walletconnect/web3-provider' +import { compareChainIDs, formatError, showConnectElements, showConnectedToElements } from './common_helpers' +import { openWarningModal } from '../modals' + +const instanceChainIdStr = document.getElementById('js-chain-id').value +const instanceChainId = parseInt(instanceChainIdStr, 10) +const walletConnectOptions = { rpc: {}, chainId: instanceChainId } +const jsonRPC = document.getElementById('js-json-rpc').value +walletConnectOptions.rpc[instanceChainId] = jsonRPC + +// Chosen wallet provider given by the dialog window +let provider + +// Web3modal instance +let web3Modal + +/** + * Setup the orchestra + */ +export async function web3ModalInit (connectToWallet, ...args) { + return new Promise((resolve) => { + // Tell Web3modal what providers we have available. + // Built-in web browser provider (only one can exist as a time) + // like MetaMask, Brave or Opera is added automatically by Web3modal + const providerOptions = { + walletconnect: { + package: WalletConnectProvider, + options: walletConnectOptions + } + } + + web3Modal = new Web3Modal({ + cacheProvider: true, + providerOptions, + disableInjectedProvider: false + }) + + if (web3Modal.cachedProvider) { + connectToWallet(...args) + } + + resolve(web3Modal) + }) +} + +export const walletEnabled = () => { + return new Promise((resolve) => { + if (window.web3 && window.web3.currentProvider && window.web3.currentProvider.wc) { + resolve(true) + } else { + if (window.ethereum) { + window.web3 = new Web3(window.ethereum) + window.ethereum._metamask.isUnlocked() + .then(isUnlocked => { + if (isUnlocked && window.ethereum.isNiftyWallet) { // Nifty Wallet + window.web3 = new Web3(window.web3.currentProvider) + resolve(true) + } else if (isUnlocked === false && window.ethereum.isNiftyWallet) { // Nifty Wallet + window.ethereum.enable() + resolve(false) + } else { + if (window.ethereum.isNiftyWallet) { + window.ethereum.enable() + window.web3 = new Web3(window.web3.currentProvider) + resolve(true) + } else { + return window.ethereum.request({ method: 'eth_requestAccounts' }) + .then((_res) => { + window.web3 = new Web3(window.web3.currentProvider) + resolve(true) + }) + .catch(_error => { + resolve(false) + }) + } + } + }) + .catch(_error => { + resolve(false) + }) + } else if (window.web3) { + window.web3 = new Web3(window.web3.currentProvider) + resolve(true) + } else { + resolve(false) + } + } + }) +} + +export async function disconnect () { + if (provider && provider.close) { + await provider.close() + } + + provider = null + + window.web3 = null + + // If the cached provider is not cleared, + // WalletConnect will default to the existing session + // and does not allow to re-scan the QR code with a new wallet. + // Depending on your use case you may want or want not his behavir. + await web3Modal.clearCachedProvider() +} + +/** + * Disconnect wallet button pressed. + */ +export async function disconnectWallet () { + await disconnect() + + showConnectElements() +} + +export const connectToProvider = () => { + return new Promise((resolve, reject) => { + try { + web3Modal + .connect() + .then((connectedProvider) => { + provider = connectedProvider + window.web3 = new Web3(provider) + resolve(provider) + }) + } catch (e) { + reject(e) + } + }) +} + +export const connectToWallet = async () => { + await connectToProvider() + + // Subscribe to accounts change + provider.on('accountsChanged', async (accs) => { + const newAccount = accs && accs.length > 0 ? accs[0].toLowerCase() : null + + if (!newAccount) { + await disconnectWallet() + } + + fetchAccountData(showConnectedToElements, []) + }) + + // Subscribe to chainId change + provider.on('chainChanged', (chainId) => { + compareChainIDs(instanceChainId, chainId) + .then(() => fetchAccountData(showConnectedToElements, [])) + .catch(error => { + openWarningModal('Unauthorized', formatError(error)) + }) + }) + + provider.on('disconnect', async () => { + await disconnectWallet() + }) + + await fetchAccountData(showConnectedToElements, []) +} + +export async function fetchAccountData (setAccount, args) { + // Get a Web3 instance for the wallet + if (provider) { + window.web3 = new Web3(provider) + } + + // Get list of accounts of the connected wallet + const accounts = window.web3 && await window.web3.eth.getAccounts() + + // MetaMask does not give you all accounts, only the selected account + if (accounts && accounts.length > 0) { + const selectedAccount = accounts[0] + + setAccount(selectedAccount, ...args) + } +} diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/functions.js b/apps/block_scout_web/assets/js/lib/smart_contract/functions.js new file mode 100644 index 0000000..6230972 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/smart_contract/functions.js @@ -0,0 +1,106 @@ +import $ from 'jquery' +import { connectSelector, disconnectSelector, getContractABI, getMethodInputs, prepareMethodArgs } from './common_helpers' +import { queryMethod, callMethod } from './interact' +import { walletEnabled, connectToWallet, disconnectWallet, web3ModalInit } from './connect.js' +import '../../pages/address' + +const loadFunctions = (element, isCustomABI) => { + const $element = $(element) + const url = $element.data('url') + const hash = $element.data('hash') + const type = $element.data('type') + const action = $element.data('action') + + $.get( + url, + { hash, type, action, is_custom_abi: isCustomABI }, + response => $element.html(response) + ) + .done(function () { + document.querySelector(connectSelector) && document.querySelector(connectSelector).addEventListener('click', connectToWallet) + document.querySelector(disconnectSelector) && document.querySelector(disconnectSelector).addEventListener('click', disconnectWallet) + web3ModalInit(connectToWallet) + + const selector = isCustomABI ? '[data-function-custom]' : '[data-function]' + + $(selector).each((_, element) => { + readWriteFunction(element) + }) + + $('.contract-exponentiation-btn').on('click', (event) => { + const $customPower = $(event.currentTarget).find('[name=custom_power]') + let power + if ($customPower.length > 0) { + power = parseInt($customPower.val(), 10) + } else { + power = parseInt($(event.currentTarget).data('power'), 10) + } + const $input = $(event.currentTarget).parent().parent().parent().find('[name=function_input]') + const currentInputVal = parseInt($input.val(), 10) || 1 + const newInputVal = (currentInputVal * Math.pow(10, power)).toString() + $input.val(newInputVal.toString()) + }) + + $('[name=custom_power]').on('click', (event) => { + $(event.currentTarget).parent().parent().toggleClass('show') + }) + }) + .fail(function (response) { + $element.html(response.statusText) + }) +} + +const readWriteFunction = (element) => { + const $element = $(element) + const $form = $element.find('[data-function-form]') + + const $responseContainer = $element.find('[data-function-response]') + + $form.on('submit', (event) => { + event.preventDefault() + const action = $form.data('action') + const $errorContainer = $form.parent().find('[input-parse-error-container]') + + $errorContainer.hide() + + const $functionInputs = $form.find('input[name=function_input]') + const $functionName = $form.find('input[name=function_name]') + const functionName = $functionName && $functionName.val() + + if (action === 'read') { + const url = $form.data('url') + + const contractAbi = getContractABI($form) + const inputs = getMethodInputs(contractAbi, functionName) + const $methodId = $form.find('input[name=method_id]') + try { + var args = prepareMethodArgs($functionInputs, inputs) + } catch (exception) { + $errorContainer.show() + $errorContainer.text(exception) + return + } + const type = $('[data-smart-contract-functions]').data('type') + const isCustomABI = $form.data('custom-abi') + + walletEnabled() + .then((isWalletEnabled) => queryMethod(isWalletEnabled, url, $methodId, args, type, functionName, $responseContainer, isCustomABI)) + } else if (action === 'write') { + const explorerChainId = $form.data('chainId') + walletEnabled() + .then((isWalletEnabled) => callMethod(isWalletEnabled, $functionInputs, explorerChainId, $form, functionName, $element)) + } + }) +} + +const container = $('[data-smart-contract-functions]') + +if (container.length) { + loadFunctions(container, false) +} + +const customABIContainer = $('[data-smart-contract-functions-custom]') + +if (customABIContainer.length) { + loadFunctions(customABIContainer, true) +} diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/index.js b/apps/block_scout_web/assets/js/lib/smart_contract/index.js new file mode 100644 index 0000000..86458b8 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/smart_contract/index.js @@ -0,0 +1,3 @@ +import './functions' +import './wei_ether_converter' +import '../../app' diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/interact.js b/apps/block_scout_web/assets/js/lib/smart_contract/interact.js new file mode 100644 index 0000000..eaacbfe --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/smart_contract/interact.js @@ -0,0 +1,115 @@ +import $ from 'jquery' +import { openErrorModal, openWarningModal, openSuccessModal, openModalWithMessage } from '../modals' +import { compareChainIDs, formatError, formatTitleAndError, getContractABI, getCurrentAccountPromise, getMethodInputs, prepareMethodArgs } from './common_helpers' +import BigNumber from 'bignumber.js' + +export const queryMethod = (isWalletEnabled, url, $methodId, args, type, functionName, $responseContainer, isCustomABI) => { + const data = { + function_name: functionName, + method_id: $methodId.val(), + type, + is_custom_abi: isCustomABI + } + + data.args_count = args.length + let i = args.length + + while (i--) { + data['arg_' + i] = args[i] + } + + if (isWalletEnabled) { + getCurrentAccountPromise(window.web3 && window.web3.currentProvider) + .then((currentAccount) => { + data.from = currentAccount + $.get(url, data, response => $responseContainer.html(response)) + } + ) + } else { + $.get(url, data, response => $responseContainer.html(response)) + } +} + +export const callMethod = (isWalletEnabled, $functionInputs, explorerChainId, $form, functionName, $element) => { + if (!isWalletEnabled) { + const warningMsg = 'Wallet is not connected.' + return openWarningModal('Unauthorized', warningMsg) + } + const contractAbi = getContractABI($form) + const inputs = getMethodInputs(contractAbi, functionName) + + const $functionInputsExceptTxValue = $functionInputs.filter(':not([tx-value])') + const args = prepareMethodArgs($functionInputsExceptTxValue, inputs) + + const txValue = getTxValue($functionInputs) + const contractAddress = $form.data('contract-address') + + window.web3.eth.getChainId() + .then((walletChainId) => { + compareChainIDs(explorerChainId, walletChainId) + .then(() => getCurrentAccountPromise(window.web3.currentProvider)) + .catch(error => { + openWarningModal('Unauthorized', formatError(error)) + }) + .then((currentAccount) => { + if (functionName) { + const TargetContract = new window.web3.eth.Contract(contractAbi, contractAddress) + const sendParams = { from: currentAccount, value: txValue || 0 } + const methodToCall = TargetContract.methods[functionName](...args).send(sendParams) + methodToCall + .on('error', function (error) { + const titleAndError = formatTitleAndError(error) + const message = titleAndError.message + (titleAndError.txHash ? `
    More info` : '') + openErrorModal(titleAndError.title.length ? titleAndError.title : `Error in sending transaction for method "${functionName}"`, message, false) + }) + .on('transactionHash', function (txHash) { + onTransactionHash(txHash, functionName) + }) + } else { + const txParams = { + from: currentAccount, + to: contractAddress, + value: txValue || 0 + } + window.ethereum.request({ + method: 'eth_sendTransaction', + params: [txParams] + }) + .then(function (txHash) { + onTransactionHash(txHash, functionName) + }) + .catch(function (error) { + openErrorModal('Error in sending transaction for fallback method', formatError(error), false) + }) + } + }) + .catch(error => { + openWarningModal('Unauthorized', formatError(error)) + }) + }) +} + +function onTransactionHash (txHash, functionName) { + openModalWithMessage($('#pending-contract-write'), true, txHash) + const getTxReceipt = (txHash) => { + window.ethereum.request({ + method: 'eth_getTransactionReceipt', + params: [txHash] + }) + .then(txReceipt => { + if (txReceipt) { + const successMsg = `Successfully sent transaction for method "${functionName}"` + openSuccessModal('Success', successMsg) + clearInterval(txReceiptPollingIntervalId) + } + }) + } + const txReceiptPollingIntervalId = setInterval(() => { getTxReceipt(txHash) }, 5 * 1000) +} + +const ethStrToWeiBn = ethStr => BigNumber(ethStr).multipliedBy(10 ** 18) + +function getTxValue ($functionInputs) { + const txValueEth = $functionInputs.filter('[tx-value]:first')?.val() || '0' + return `0x${ethStrToWeiBn(txValueEth).toString(16)}` +} diff --git a/apps/block_scout_web/assets/js/lib/smart_contract/wei_ether_converter.js b/apps/block_scout_web/assets/js/lib/smart_contract/wei_ether_converter.js new file mode 100644 index 0000000..29865a7 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/smart_contract/wei_ether_converter.js @@ -0,0 +1,28 @@ +import $ from 'jquery' +import { BigNumber } from 'bignumber.js' +import numeral from 'numeral' + +const weiToEtherConverter = (element, event) => { + const weiUnit = new BigNumber('1000000000000000000') + const $element = $(element) + const $conversionTextWei = $element.find('[data-conversion-text-wei]') + const $conversionTextEth = $element.find('[data-conversion-text-eth]') + const $conversionUnit = $element.find('[data-conversion-unit]') + const originalValueStr = $conversionUnit.data('original-value') + const unitVal = new BigNumber(numeral(originalValueStr).value()) + const weiVal = unitVal.dividedBy(weiUnit) + + if (event.target.checked) { + $conversionTextWei.removeClass('d-inline-block').addClass('d-none') + $conversionTextEth.removeClass('d-none').addClass('d-inline-block') + $conversionUnit.html(weiVal.toFixed() > 0 ? String(weiVal.toFixed()) : numeral(weiVal).format('0[.000000000000000000]')) + } else { + $conversionTextWei.removeClass('d-none').addClass('d-inline-block') + $conversionTextEth.removeClass('d-inline-block').addClass('d-none') + $conversionUnit.html(originalValueStr) + } +} + +$('[data-smart-contract-functions]').on('change', '[data-wei-ether-converter]', function (event) { + weiToEtherConverter(this, event) +}) diff --git a/apps/block_scout_web/assets/js/lib/stop_propagation.js b/apps/block_scout_web/assets/js/lib/stop_propagation.js new file mode 100644 index 0000000..c32fb6b --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/stop_propagation.js @@ -0,0 +1,3 @@ +import $ from 'jquery' + +$('[data-selector="stop-propagation"]').click((event) => event.stopPropagation()) diff --git a/apps/block_scout_web/assets/js/lib/text_ad.js b/apps/block_scout_web/assets/js/lib/text_ad.js new file mode 100644 index 0000000..94bd22a --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/text_ad.js @@ -0,0 +1,8 @@ +import $ from 'jquery' +import { showAd, fetchTextAdData } from './ad.js' + +$(function () { + if (showAd()) { + fetchTextAdData() + } +}) diff --git a/apps/block_scout_web/assets/js/lib/token_balance_dropdown.js b/apps/block_scout_web/assets/js/lib/token_balance_dropdown.js new file mode 100644 index 0000000..1a64f54 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/token_balance_dropdown.js @@ -0,0 +1,41 @@ +import $ from 'jquery' +import { formatAllUsdValues, formatUsdValue } from './currency' +import { TokenBalanceDropdownSearch } from './token_balance_dropdown_search' + +const tokenBalanceDropdown = (element) => { + const $element = $(element) + const $loading = $element.find('[data-loading]') + const $errorMessage = $element.find('[data-error-message]') + const apiPath = element.dataset.api_path + + $.get(apiPath) + .done(response => { + const responseHtml = formatAllUsdValues($(response)) + $element.html(responseHtml) + const tokensCount = $('[data-dropdown-token-balance-test]').length + const $addressTokenWorth = $('[data-test="address-tokens-worth"]') + const tokensDsName = (tokensCount > 1) ? ' tokens' : ' token' + $('[data-test="address-tokens-panel-tokens-worth"]').text(`${$addressTokenWorth.text()} | ${tokensCount} ${tokensDsName}`) + const $addressTokensPanelNativeWorth = $('[data-test="address-tokens-panel-native-worth"]') + const rawUsdValue = $addressTokensPanelNativeWorth.children('span').data('raw-usd-value') + const rawUsdTokensValue = $addressTokenWorth.data('usd-value') + const formattedFullUsdValue = formatUsdValue(parseFloat(rawUsdValue) + parseFloat(rawUsdTokensValue)) + $('[data-test="address-tokens-panel-net-worth"]').text(formattedFullUsdValue) + }) + .fail(() => { + $loading.hide() + $errorMessage.show() + }) +} + +export function loadTokenBalanceDropdown () { + $('[data-token-balance-dropdown]').each((_index, element) => tokenBalanceDropdown(element)) + + $('[data-token-balance-dropdown]').on('hidden.bs.dropdown', _event => { + $('[data-filter-dropdown-tokens]').val('').trigger('input') + }) + + $('[data-token-balance-dropdown]').on('input', function (event) { + TokenBalanceDropdownSearch(this, event) + }) +} diff --git a/apps/block_scout_web/assets/js/lib/token_balance_dropdown_search.js b/apps/block_scout_web/assets/js/lib/token_balance_dropdown_search.js new file mode 100644 index 0000000..3a98e30 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/token_balance_dropdown_search.js @@ -0,0 +1,43 @@ +import $ from 'jquery' + +const stringContains = (query, string) => { + return string.toLowerCase().search(query) === -1 +} + +const hideUnmatchedToken = (query, token) => { + const $token = $(token) + const tokenName = $token.data('token-name') + const tokenSymbol = $token.data('token-symbol') + + if (stringContains(query, tokenName) && stringContains(query, tokenSymbol)) { + $token.addClass('d-none') + } else { + $token.removeClass('d-none') + } +} + +const hideEmptyType = (container) => { + const $container = $(container) + const type = $container.data('token-type') + const countVisibleTokens = $container.children('[data-token-name]:not(.d-none)').length + + if (countVisibleTokens === 0) { + $container.addClass('d-none') + } else { + $(`[data-number-of-tokens-by-type='${type}']`).empty().append(countVisibleTokens) + $container.removeClass('d-none') + } +} + +export function TokenBalanceDropdownSearch (element, event) { + const $element = $(element) + const $tokensCount = $element.find('[data-tokens-count]') + const $tokens = $element.find('[data-token-name]') + const $tokenTypes = $element.find('[data-token-type]') + const query = event.target.value.toLowerCase() + + $tokens.each((_index, token) => hideUnmatchedToken(query, token)) + $tokenTypes.each((_index, container) => hideEmptyType(container)) + + $tokensCount.html($tokensCount.html().replace(/\d+/g, $tokens.not('.d-none').length)) +} diff --git a/apps/block_scout_web/assets/js/lib/token_icon.js b/apps/block_scout_web/assets/js/lib/token_icon.js new file mode 100644 index 0000000..6f283cb --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/token_icon.js @@ -0,0 +1,55 @@ +function getTokenIconUrl (chainID, addressHash) { + let chainName = null + switch (chainID) { + case '1': + chainName = 'ethereum' + break + case '99': + chainName = 'poa' + break + case '100': + chainName = 'xdai' + break + default: + chainName = null + break + } + if (chainName) { + return `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/${chainName}/assets/${addressHash}/logo.png` + } else { + return null + } +} + +function appendTokenIcon ($tokenIconContainer, chainID, addressHash, displayTokenIcons, size) { + const iconSize = size || 20 + const tokenIconURL = getTokenIconUrl(chainID.toString(), addressHash) + if (displayTokenIcons) { + checkLink(tokenIconURL) + .then(checkTokenIconLink => { + if (checkTokenIconLink) { + if ($tokenIconContainer) { + const img = new Image(iconSize, iconSize) + img.src = tokenIconURL + img.className = 'mr-1' + $tokenIconContainer.append(img) + } + } + }) + } +} + +async function checkLink (url) { + if (url) { + try { + const res = await fetch(url) + return res.ok + } catch (_error) { + return false + } + } else { + return false + } +} + +export { appendTokenIcon, checkLink, getTokenIconUrl } diff --git a/apps/block_scout_web/assets/js/lib/token_transfers_toggle.js b/apps/block_scout_web/assets/js/lib/token_transfers_toggle.js new file mode 100644 index 0000000..b2f029c --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/token_transfers_toggle.js @@ -0,0 +1,17 @@ +import $ from 'jquery' + +$(document.body).on('click', '[data-selector="token-transfer-open"]', event => { + const $tokenTransferOpen = event.target + const $parent = $tokenTransferOpen.parentElement + const $tokenTransferClose = $parent.querySelector("[data-selector='token-transfer-close']") + $tokenTransferOpen.classList.add('d-none') + $tokenTransferClose.classList.remove('d-none') +}) + +$(document.body).on('click', '[data-selector="token-transfer-close"]', event => { + const $tokenTransferClose = event.target + const $parent = $tokenTransferClose.parentElement + const $tokenTransferOpen = $parent.querySelector("[data-selector='token-transfer-open']") + $tokenTransferClose.classList.add('d-none') + $tokenTransferOpen.classList.remove('d-none') +}) diff --git a/apps/block_scout_web/assets/js/lib/tooltip.js b/apps/block_scout_web/assets/js/lib/tooltip.js new file mode 100644 index 0000000..0c29a92 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/tooltip.js @@ -0,0 +1,5 @@ +import $ from 'jquery' + +$(function () { + $('body').tooltip({ selector: '[data-toggle="tooltip"]' }) +}) diff --git a/apps/block_scout_web/assets/js/lib/transaction_input_dropdown.js b/apps/block_scout_web/assets/js/lib/transaction_input_dropdown.js new file mode 100644 index 0000000..75ed91f --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/transaction_input_dropdown.js @@ -0,0 +1,13 @@ +import $ from 'jquery' + +$('.tx-input-dropdown').click(function (e) { + e.preventDefault() + + const el = $(e.currentTarget) + const target = $(el.data('target')) + const targetToHide = $(el.data('target-to-hide')) + + target.show() + targetToHide.hide() + $('#tx-input-decoding-button').text(el.text()) +}) diff --git a/apps/block_scout_web/assets/js/lib/try_api.js b/apps/block_scout_web/assets/js/lib/try_api.js new file mode 100644 index 0000000..7f531c7 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/try_api.js @@ -0,0 +1,137 @@ +import $ from 'jquery' +import '../app' +import { escapeHtml } from './utils' + +// This file adds event handlers responsible for the 'Try it out' UI in the +// Etherscan-compatible API documentation page. + +function composeQuery (module, action, inputs) { + const parameters = queryParametersFromInputs(inputs) + return `?module=${module}&action=${action}` + parameters.join('') +} + +function queryParametersFromInputs (inputs) { + return $.map(inputs, queryParameterFromInput) +} + +function queryParameterFromInput (input) { + const key = $(input).attr('data-parameter-key') + const value = $(input).val() + + if (value === '') { + return '' + } else { + return `&${key}=${value}` + } +} + +function composeRequestUrl (query) { + const url = $('[data-endpoint-url]').attr('data-endpoint-url') + return `${url}${query}` +} + +function composeCurlCommand (requestUrl) { + return `curl -X GET "${requestUrl}" -H "accept: application/json"` +} + +function isResultVisible (module, action) { + return $(`[data-selector="${module}-${action}-try-api-ui-result"]`).is(':visible') +} + +function handleSuccess (query, xhr, clickedButton) { + const module = clickedButton.attr('data-module') + const action = clickedButton.attr('data-action') + const curl = $(`[data-selector="${module}-${action}-curl"]`)[0] + const requestUrl = $(`[data-selector="${module}-${action}-request-url"]`)[0] + const code = $(`[data-selector="${module}-${action}-server-response-code"]`)[0] + const body = $(`[data-selector="${module}-${action}-server-response-body"]`)[0] + const url = composeRequestUrl(escapeHtml(query)) + + curl.innerHTML = composeCurlCommand(url) + requestUrl.innerHTML = url + code.innerHTML = xhr.status + body.innerHTML = escapeHtml(JSON.stringify(xhr.responseJSON, undefined, 2)) + $(`[data-selector="${module}-${action}-try-api-ui-result"]`).show() + $(`[data-selector="${module}-${action}-btn-try-api-clear"]`).show() + clickedButton.html(clickedButton.data('original-text')) + clickedButton.prop('disabled', false) +} + +// Show 'Try it out' UI for a module/action. +$('button[data-selector*="btn-try-api"]').click(event => { + const clickedButton = $(event.target) + const module = clickedButton.attr('data-module') + const action = clickedButton.attr('data-action') + clickedButton.hide() + $(`button[data-selector="${module}-${action}-btn-try-api-cancel"]`).show() + $(`[data-selector="${module}-${action}-try-api-ui"]`).show() + + if (isResultVisible(module, action)) { + $(`[data-selector="${module}-${action}-btn-try-api-clear"]`).show() + } +}) + +// Hide 'Try it out' UI for a module/action. +$('button[data-selector*="btn-try-api-cancel"]').click(event => { + const clickedButton = $(event.target) + const module = clickedButton.attr('data-module') + const action = clickedButton.attr('data-action') + clickedButton.hide() + $(`[data-selector="${module}-${action}-try-api-ui"]`).hide() + $(`[data-selector="${module}-${action}-btn-try-api-clear"]`).hide() + $(`button[data-selector="${module}-${action}-btn-try-api"]`).show() +}) + +// Clear API server response/result, curl command, and request URL +$('button[data-selector*="btn-try-api-clear"]').click(event => { + const clickedButton = $(event.target) + const module = clickedButton.attr('data-module') + const action = clickedButton.attr('data-action') + clickedButton.hide() + $(`[data-selector="${module}-${action}-try-api-ui-result"]`).hide() +}) + +// Remove invalid class from required fields if not empty +$('input[data-selector*="try-api-ui"][data-required="true"]').on('keyup', (event) => { + if (event.target.value !== '') { + event.target.classList.remove('is-invalid') + } else { + event.target.classList.add('is-invalid') + } +}) + +// Execute API call +// +// Makes a request to the Explorer API with a given set of user defined +// parameters. The following related information is subsequently rendered below +// the execute button: +// +// * curl command +// * request URL +// * server response +// +$('button[data-try-api-ui-button-type="execute"]').click(event => { + const clickedButton = $(event.target) + const module = clickedButton.attr('data-module') + const action = clickedButton.attr('data-action') + const inputs = $(`input[data-selector="${module}-${action}-try-api-ui"]`) + const query = composeQuery(module, action, inputs) + const loadingText = ' Loading...' + + clickedButton.prop('disabled', true) + clickedButton.data('original-text', clickedButton.html()) + + if (clickedButton.html() !== loadingText) { + clickedButton.html(loadingText) + } + + $.ajax({ + url: composeRequestUrl(query), + success: (_data, _status, xhr) => { + handleSuccess(query, xhr, clickedButton) + }, + error: (xhr) => { + handleSuccess(query, xhr, clickedButton) + } + }) +}) diff --git a/apps/block_scout_web/assets/js/lib/try_eth_api.js b/apps/block_scout_web/assets/js/lib/try_eth_api.js new file mode 100644 index 0000000..3f00750 --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/try_eth_api.js @@ -0,0 +1,82 @@ +import $ from 'jquery' +import './try_api' + +function composeCurlCommand (data) { + const url = $('[data-endpoint-url]').attr('data-endpoint-url') + return `curl -H "content-type: application/json" -X POST --data '${JSON.stringify(data)}' ${url}` +} + +function handleResponse (data, xhr, clickedButton) { + const module = clickedButton.attr('data-module') + const action = clickedButton.attr('data-action') + const curl = $(`[data-selector="${module}-${action}-curl"]`)[0] + const code = $(`[data-selector="${module}-${action}-server-response-code"]`)[0] + const body = $(`[data-selector="${module}-${action}-server-response-body"]`)[0] + + curl.innerHTML = composeCurlCommand(data) + code.innerHTML = xhr.status + body.innerHTML = JSON.stringify(xhr.responseJSON, undefined, 2) + $(`[data-selector="${module}-${action}-try-api-ui-result"]`).show() + $(`[data-selector="${module}-${action}-btn-try-api-clear"]`).show() + clickedButton.html(clickedButton.data('original-text')) + clickedButton.prop('disabled', false) +} + +function wrapJsonRpc (method, params) { + return { + id: 0, + jsonrpc: '2.0', + method, + params + } +} + +function parseInput (input) { + const type = $(input).attr('data-parameter-type') + const value = $(input).val() + + switch (type) { + case 'string': + return value + case 'json': + try { + return JSON.parse(value) + } catch (e) { + return {} + } + default: + return value + } +} + +function composeRequestUrl () { + const url = $('[data-endpoint-url]').attr('data-endpoint-url') + return url +} + +$('button[data-try-eth-api-ui-button-type="execute"]').click(event => { + const clickedButton = $(event.target) + const module = clickedButton.attr('data-module') + const action = clickedButton.attr('data-action') + const inputs = $(`input[data-selector="${module}-${action}-try-api-ui"]`) + const params = $.map(inputs, parseInput) + const formData = wrapJsonRpc(action, params) + const loadingText = ' Loading...' + + clickedButton.prop('disabled', true) + clickedButton.data('original-text', clickedButton.html()) + + if (clickedButton.html() !== loadingText) { + clickedButton.html(loadingText) + } + + $.ajax({ + url: composeRequestUrl(), + type: 'POST', + data: JSON.stringify(formData), + dataType: 'json', + contentType: 'application/json; charset=utf-8' + }) + .then((_data, _status, xhr) => handleResponse(formData, xhr, clickedButton)) + .fail((xhr) => handleResponse(formData, xhr, clickedButton)) +}) diff --git a/apps/block_scout_web/assets/js/lib/utils.js b/apps/block_scout_web/assets/js/lib/utils.js new file mode 100644 index 0000000..388813c --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/utils.js @@ -0,0 +1,38 @@ +import debounce from 'lodash.debounce' + +export function batchChannel (func) { + let msgs = [] + const debouncedFunc = debounce(() => { + func.apply(this, [msgs]) + msgs = [] + }, 1000, { maxWait: 5000 }) + return (msg) => { + msgs.push(msg) + debouncedFunc() + } +} + +export function showLoader (isTimeout, loader) { + if (isTimeout) { + const timeout = setTimeout(function () { + loader.removeAttr('hidden') + loader.show() + }, 100) + return timeout + } else { + loader.hide() + return null + } +} + +export function escapeHtml (text) { + const map = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + } + + return text.replace(/[&<>"']/g, function (m) { return map[m] }) +} diff --git a/apps/block_scout_web/assets/js/lib/validation.js b/apps/block_scout_web/assets/js/lib/validation.js new file mode 100644 index 0000000..a73511e --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/validation.js @@ -0,0 +1,64 @@ +import $ from 'jquery' + +export function setupValidation ($form, validators, $submit) { + const errors = {} + + updateSubmit($submit, errors) + + for (const [key, callback] of Object.entries(validators)) { + const $input = $form.find('[' + key + ']') + errors[key] = null + + $input + .ready(() => { + validateInput($input, callback, errors) + updateSubmit($submit, errors) + if (errors[key]) { + displayInputError($input, errors[key]) + } + }) + .blur(() => { + if (errors[key]) { + displayInputError($input, errors[key]) + } + }) + .on('input', () => { + hideInputError($input) + validateInput($input, callback, errors) + updateSubmit($submit, errors) + }) + } +} + +function validateInput ($input, callback, errors) { + if (!$input.val()) { + errors[$input.prop('id')] = null + return + } + + const validation = callback($input.val()) + if (validation === true) { + delete errors[$input.prop('id')] + return + } + + errors[$input.prop('id')] = validation +} + +function updateSubmit ($submit, errors) { + $submit.prop('disabled', !$.isEmptyObject(errors)) +} + +export function displayInputError ($input, message) { + const group = $input.parent('.input-group') + + group.addClass('input-status-error') + group.find('.input-group-message').html(message) +} + +export function hideInputError ($input) { + const group = $input.parent('.input-group') + + group.removeClass('input-status-error') + group.find('.input-group-message').html('') +} diff --git a/apps/block_scout_web/assets/js/locale.js b/apps/block_scout_web/assets/js/locale.js new file mode 100644 index 0000000..94f198c --- /dev/null +++ b/apps/block_scout_web/assets/js/locale.js @@ -0,0 +1,8 @@ +import moment from 'moment' +import numeral from 'numeral' +import 'numeral/locales' + +export const locale = 'en' + +moment.locale(locale) +numeral.locale(locale) diff --git a/apps/block_scout_web/assets/js/pages/account/delete_item_handler.js b/apps/block_scout_web/assets/js/pages/account/delete_item_handler.js new file mode 100644 index 0000000..0e357e0 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/account/delete_item_handler.js @@ -0,0 +1,19 @@ +import $ from 'jquery' + +$('[data-delete-item]').on('click', (event) => { + event.preventDefault() + + if (confirm('Are you sure you want to delete item?')) { + $(event.currentTarget.parentElement).find('form').trigger('submit') + } +}) + +$('[data-delete-request]').on('click', (event) => { + event.preventDefault() + + const result = prompt('Public tags: "' + event.currentTarget.dataset.tags.replace(';', '" and "') + '" will be removed.\nWhy do you want to remove tags?') + if (result) { + $(event.currentTarget.parentElement).find('[name="remove_reason"]').val(result) + $(event.currentTarget.parentElement).find('form').trigger('submit') + } +}) diff --git a/apps/block_scout_web/assets/js/pages/address.js b/apps/block_scout_web/assets/js/pages/address.js new file mode 100644 index 0000000..47f1389 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/address.js @@ -0,0 +1,321 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import URI from 'urijs' +import humps from 'humps' +import numeral from 'numeral' +import socket, { subscribeChannel } from '../socket' +import { createStore, connectElements } from '../lib/redux_helpers.js' +import { updateAllCalculatedUsdValues } from '../lib/currency.js' +import { loadTokenBalanceDropdown } from '../lib/token_balance_dropdown' +import '../lib/token_balance_dropdown_search' +import '../lib/async_listing_load' +import '../app' +import { + openQrModal +} from '../lib/modals' + +export const initialState = { + channelDisconnected: false, + + addressHash: null, + filter: null, + + balance: null, + balanceCard: null, + fetchedCoinBalanceBlockNumber: null, + transactionCount: null, + tokenTransferCount: null, + gasUsageCount: null, + validationCount: null, + countersFetched: false +} + +export function reducer (state = initialState, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + if (state.beyondPageOne) return state + + return Object.assign({}, state, { + channelDisconnected: true + }) + } + case 'COUNTERS_FETCHED': { + return Object.assign({}, state, { + transactionCount: action.transactionCount, + tokenTransferCount: action.tokenTransferCount, + gasUsageCount: action.gasUsageCount, + validationCount: action.validationCount, + crcTotalWorth: action.crcTotalWorth, + countersFetched: true + }) + } + case 'RECEIVED_NEW_BLOCK': { + if (state.channelDisconnected) return state + + const validationCount = state.validationCount + 1 + return Object.assign({}, state, { validationCount }) + } + case 'RECEIVED_NEW_TRANSACTION': { + if (state.channelDisconnected) return state + + const transactionCount = (action.msg.fromAddressHash === state.addressHash) ? state.transactionCount + 1 : state.transactionCount + + return Object.assign({}, state, { transactionCount }) + } + case 'RECEIVED_NEW_TOKEN_TRANSFER': { + if (state.channelDisconnected) return state + + const tokenTransferCount = (action.msg.fromAddressHash === state.addressHash) ? state.tokenTransferCount + 1 : state.tokenTransferCount + + return Object.assign({}, state, { tokenTransferCount }) + } + case 'RECEIVED_UPDATED_BALANCE': { + return Object.assign({}, state, { + balanceCard: action.msg.balanceCard, + balance: parseFloat(action.msg.balance), + fetchedCoinBalanceBlockNumber: action.msg.fetchedCoinBalanceBlockNumber + }) + } + case 'RECEIVED_NEW_CURRENT_COIN_BALANCE': { + if (state.initialBlockNumber && action.msg.currentCoinBalanceBlockNumber < state.initialBlockNumber) return + return Object.assign({}, state, { + currentCoinBalance: action.msg.currentCoinBalanceHtml, + currentCoinBalanceBlockNumber: action.msg.currentCoinBalanceBlockNumberHtml, + initialBlockNumber: state.newBlockNumber, + newBlockNumber: action.msg.currentCoinBalanceBlockNumber + }) + } + default: + return state + } +} + +let fetchedTokenBalanceBlockNumber = 0 +function loadTokenBalance (blockNumber) { + if (blockNumber > fetchedTokenBalanceBlockNumber) { + fetchedTokenBalanceBlockNumber = blockNumber + setTimeout(loadTokenBalanceDropdown, 1000) + } else if (fetchedTokenBalanceBlockNumber === 0 && blockNumber === null) { + setTimeout(loadTokenBalanceDropdown, 1000) + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected && !window.loading) $el.show() + } + }, + '[data-selector="balance-card"]': { + load ($el) { + return { balanceCard: $el.html(), balance: parseFloat($el.find('.current-balance-in-wei').attr('data-wei-value')) } + }, + render ($el, state, oldState) { + if (oldState.balance === state.balance || (isNaN(oldState.balance) && isNaN(state.balance))) return + $el.empty().append(state.balanceCard) + loadTokenBalance(state.fetchedCoinBalanceBlockNumber) + updateAllCalculatedUsdValues() + } + }, + '[data-selector="transaction-count"]': { + load ($el) { + return { transactionCount: numeral($el.text()).value() } + }, + render ($el, state, oldState) { + if (state.countersFetched) { + if (oldState.transactionCount === state.transactionCount) return + const transactionsDSName = (state.transactionCount === 1) ? ' Transaction' : ' Transactions' + $el.empty().append(numeral(state.transactionCount).format() + transactionsDSName) + } + } + }, + '[data-selector="transfer-count"]': { + load ($el) { + return { tokenTransferCount: numeral($el.text()).value() } + }, + render ($el, state, oldState) { + if (state.countersFetched) { + if (oldState.tokenTransferCount === state.tokenTransferCount) return + const transfersDSName = (state.tokenTransferCount === 1) ? ' Transfer' : ' Transfers' + $el.empty().append(numeral(state.tokenTransferCount).format() + transfersDSName) + } + } + }, + '[data-selector="gas-usage-count"]': { + load ($el) { + return { gasUsageCount: numeral($el.text()).value() } + }, + render ($el, state, oldState) { + if (state.countersFetched) { + if (oldState.gasUsageCount === state.gasUsageCount) return + $el.empty().append(numeral(state.gasUsageCount).format()) + } + } + }, + '[data-selector="fetched-coin-balance-block-number"]': { + load ($el) { + return { fetchedCoinBalanceBlockNumber: numeral($el.text()).value() } + }, + render ($el, state, oldState) { + if (oldState.fetchedCoinBalanceBlockNumber === state.fetchedCoinBalanceBlockNumber) return + $el.empty().append(numeral(state.fetchedCoinBalanceBlockNumber).format()) + } + }, + '[data-selector="validation-count"]': { + load ($el) { + return { validationCount: numeral($el.text()).value() } + }, + render ($el, state, oldState) { + if (state.countersFetched && state.validationCount) { + if (oldState.validationCount === state.validationCount) return + $el.empty().append(numeral(state.validationCount).format()) + $('.address-validation-count-item').removeAttr('style') + } else { + $('.address-validation-count-item').css('display', 'none') + } + } + }, + '[data-test="address-tokens-panel-crc-total-worth"]': { + load ($el) { + return { countersFetched: numeral($el.text()).value() } + }, + render ($el, state, oldState) { + if (state.countersFetched && state.crcTotalWorth) { + if (oldState.crcTotalWorth === state.crcTotalWorth) return + $el.empty().append(`${state.crcTotalWorth} CRC`) + if (state.crcTotalWorth !== '0') { + $('[data-test="address-tokens-panel-crc-total-worth-container"]').removeClass('d-none') + } else { + $('[data-test="address-tokens-panel-crc-total-worth-container"]').addClass('d-none') + } + } else { + $('[data-test="address-tokens-panel-crc-total-worth-container"]').addClass('d-none') + } + } + }, + '[data-selector="current-coin-balance"]': { + render ($el, state, oldState) { + if (!state.newBlockNumber || state.newBlockNumber <= oldState.newBlockNumber) return + $el.empty().append(state.currentCoinBalance) + updateAllCalculatedUsdValues() + } + }, + '[data-selector="last-balance-update"]': { + render ($el, state, oldState) { + if (!state.newBlockNumber || state.newBlockNumber <= oldState.newBlockNumber) return + $el.empty().append(state.currentCoinBalanceBlockNumber) + } + }, + '[data-last-balance-update]': { + load ($el) { + return { initialBlockNumber: numeral($el.data('last-balance-update')).value() } + } + } +} + +function loadCounters (store) { + const $element = $('[data-async-counters]') + const path = $element.data().asyncCounters + + function fetchCounters () { + $.getJSON(path) + .done(response => store.dispatch(Object.assign({ type: 'COUNTERS_FETCHED' }, humps.camelizeKeys(response)))) + } + + fetchCounters() +} + +const $addressDetailsPage = $('[data-page="address-details"]') +if ($addressDetailsPage.length) { + const pathParts = window.location.pathname.split('/') + const shouldScroll = pathParts.includes('transactions') || + pathParts.includes('token-transfers') || + pathParts.includes('tokens') || + pathParts.includes('internal-transactions') || + pathParts.includes('coin-balances') || + pathParts.includes('logs') || + pathParts.includes('validations') || + pathParts.includes('contracts') || + pathParts.includes('decompiled-contracts') || + pathParts.includes('read-contract') || + pathParts.includes('read-proxy') || + pathParts.includes('write-contract') || + pathParts.includes('write-proxy') + + if (shouldScroll) { + location.href = '#address-tabs' + } + + window.onbeforeunload = () => { + window.loading = true + } + + const store = createStore(reducer) + const addressHash = $addressDetailsPage[0].dataset.pageAddressHash + const { filter, blockNumber } = humps.camelizeKeys(URI(window.location).query(true)) + store.dispatch({ + type: 'PAGE_LOAD', + addressHash, + filter, + beyondPageOne: !!blockNumber + }) + connectElements({ store, elements }) + + const addressChannel = subscribeChannel(`addresses:${addressHash}`) + + addressChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + addressChannel.on('balance', (msg) => store.dispatch({ + type: 'RECEIVED_UPDATED_BALANCE', + msg: humps.camelizeKeys(msg) + })) + addressChannel.on('token_balance', (msg) => loadTokenBalance( + msg.block_number + )) + addressChannel.on('transaction', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_TRANSACTION', + msg: humps.camelizeKeys(msg) + }) + }) + addressChannel.on('transfer', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_TOKEN_TRANSFER', + msg: humps.camelizeKeys(msg) + }) + }) + addressChannel.on('current_coin_balance', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_CURRENT_COIN_BALANCE', + msg: humps.camelizeKeys(msg) + }) + }) + + const blocksChannel = socket.channel(`blocks:${addressHash}`, {}) + blocksChannel.join() + blocksChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + blocksChannel.on('new_block', (msg) => store.dispatch({ + type: 'RECEIVED_NEW_BLOCK', + msg: humps.camelizeKeys(msg) + })) + + // following lines causes double /token-balances request + // addressChannel.push('get_balance', {}) + // .receive('ok', (msg) => store.dispatch({ + // type: 'RECEIVED_UPDATED_BALANCE', + // msg: humps.camelizeKeys(msg) + // })) + + loadCounters(store) + + $('.btn-qr-icon').click(_event => { + openQrModal() + }) +} diff --git a/apps/block_scout_web/assets/js/pages/address/coin_balances.js b/apps/block_scout_web/assets/js/pages/address/coin_balances.js new file mode 100644 index 0000000..f85ade9 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/address/coin_balances.js @@ -0,0 +1,68 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import humps from 'humps' +import socket from '../../socket' +import { connectElements } from '../../lib/redux_helpers.js' +import { createAsyncLoadStore } from '../../lib/async_listing_load' +import '../address' + +export const initialState = { + channelDisconnected: false +} + +export function reducer (state, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + if (state.beyondPageOne) return state + + return Object.assign({}, state, { + channelDisconnected: true + }) + } + case 'RECEIVED_NEW_COIN_BALANCE': { + if (state.channelDisconnected || state.beyondPageOne) return state + + return Object.assign({}, state, { + items: [action.msg.coinBalanceHtml, ...state.items] + }) + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected && !window.loading) $el.show() + } + } +} + +if ($('[data-page="coin-balance-history"]').length) { + window.onbeforeunload = () => { + window.loading = true + } + + const store = createAsyncLoadStore(reducer, initialState, 'dataset.blockNumber') + const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash + + store.dispatch({ type: 'PAGE_LOAD', addressHash }) + connectElements({ store, elements }) + + const addressChannel = socket.channel(`addresses:${addressHash}`, {}) + addressChannel.join() + addressChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + addressChannel.on('coin_balance', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_COIN_BALANCE', + msg: humps.camelizeKeys(msg) + }) + }) +} diff --git a/apps/block_scout_web/assets/js/pages/address/internal_transactions.js b/apps/block_scout_web/assets/js/pages/address/internal_transactions.js new file mode 100644 index 0000000..5380f12 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/address/internal_transactions.js @@ -0,0 +1,117 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import humps from 'humps' +import numeral from 'numeral' +import socket from '../../socket' +import { batchChannel } from '../../lib/utils' +import { connectElements } from '../../lib/redux_helpers.js' +import { createAsyncLoadStore } from '../../lib/async_listing_load' +import '../address' +import { isFiltered } from './utils' + +const BATCH_THRESHOLD = 10 + +export const initialState = { + channelDisconnected: false, + addressHash: null, + filter: null, + internalTransactionsBatch: [] +} + +export function reducer (state, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + if (state.beyondPageOne) return state + + return Object.assign({}, state, { + channelDisconnected: true, + internalTransactionsBatch: [] + }) + } + case 'RECEIVED_NEW_INTERNAL_TRANSACTION_BATCH': { + if (state.channelDisconnected || state.beyondPageOne) return state + + const incomingInternalTransactions = action.msgs + .filter(({ toAddressHash, fromAddressHash }) => ( + !state.filter || + (state.filter === 'to' && toAddressHash === state.addressHash) || + (state.filter === 'from' && fromAddressHash === state.addressHash) + )).map(msg => msg.internalTransactionHtml) + + if (!state.internalTransactionsBatch.length && incomingInternalTransactions.length < BATCH_THRESHOLD) { + return Object.assign({}, state, { + items: [ + ...incomingInternalTransactions.reverse(), + ...state.items + ] + }) + } else { + return Object.assign({}, state, { + internalTransactionsBatch: [ + ...incomingInternalTransactions.reverse(), + ...state.internalTransactionsBatch + ] + }) + } + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected && !window.loading) $el.show() + } + }, + '[data-selector="channel-batching-count"]': { + render ($el, state) { + const $channelBatching = $('[data-selector="channel-batching-message"]') + if (!state.internalTransactionsBatch.length) return $channelBatching.hide() + $channelBatching.show() + $el[0].innerHTML = numeral(state.internalTransactionsBatch.length).format() + } + }, + '[data-test="filter_dropdown"]': { + render ($el, state) { + if (state.emptyResponse && !state.isSearch) { + if (isFiltered(state.filter)) { + $el.addClass('no-rm') + } else { + return $el.hide() + } + } else { + $el.removeClass('no-rm') + } + + return $el.show() + } + } +} + +if ($('[data-page="address-internal-transactions"]').length) { + window.onbeforeunload = () => { + window.loading = true + } + + const store = createAsyncLoadStore(reducer, initialState, 'dataset.key') + const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash + + store.dispatch({ type: 'PAGE_LOAD', addressHash }) + connectElements({ store, elements }) + + const addressChannel = socket.channel(`addresses:${addressHash}`, {}) + addressChannel.join() + addressChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + addressChannel.on('internal_transaction', batchChannel((msgs) => store.dispatch({ + type: 'RECEIVED_NEW_INTERNAL_TRANSACTION_BATCH', + msgs: humps.camelizeKeys(msgs) + }))) +} diff --git a/apps/block_scout_web/assets/js/pages/address/logs.js b/apps/block_scout_web/assets/js/pages/address/logs.js new file mode 100644 index 0000000..fe99a6e --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/address/logs.js @@ -0,0 +1,104 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import { connectElements } from '../../lib/redux_helpers.js' +import { createAsyncLoadStore, loadPage } from '../../lib/async_listing_load' +import '../address' +import { utils } from 'web3' + +export const initialState = { + addressHash: null, + isSearch: false +} + +export function reducer (state, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'START_SEARCH': { + return Object.assign({}, state, { pagesStack: [], isSearch: true }) + } + default: + return state + } +} + +const elements = { + '[data-search-field]': { + render ($el, state) { + return $el + } + }, + '[data-search-button]': { + render ($el, state) { + return $el + } + }, + '[data-cancel-search-button]': { + render ($el, state) { + if (!state.isSearch) { + return $el.hide() + } + + return $el.show() + } + }, + '[data-search]': { + render ($el, state) { + if (state.emptyResponse && !state.isSearch) { + return $el.hide() + } + + return $el.show() + } + } +} + +if ($('[data-page="address-logs"]').length) { + let timer + const waitTime = 500 + + const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierLog') + const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash + const $element = $('[data-async-listing]') + + connectElements({ store, elements }) + + const searchFunc = (_event) => { + store.dispatch({ + type: 'START_SEARCH', + addressHash + }) + const topic = $('[data-search-field]').val() + const addressHashPlain = store.getState().addressHash + const addressHashChecksum = addressHashPlain && utils.toChecksumAddress(addressHashPlain) + const path = `/search-logs?topic=${topic}&address_id=${addressHashChecksum}` + loadPage(store, path) + } + + store.dispatch({ + type: 'PAGE_LOAD', + addressHash + }) + + $element.on('click', '[data-search-button]', searchFunc) + + $element.on('click', '[data-cancel-search-button]', (_event) => { + $('[data-search-field]').val('') + loadPage(store, window.location.pathname) + }) + + $element.on('input keyup', '[data-search-field]', (event) => { + if (event.type === 'input') { + clearTimeout(timer) + timer = setTimeout(() => { + searchFunc(event) + }, waitTime) + } + if (event.type === 'keyup' && event.keyCode === 13) { + clearTimeout(timer) + searchFunc(event) + } + }) +} diff --git a/apps/block_scout_web/assets/js/pages/address/token_transfers.js b/apps/block_scout_web/assets/js/pages/address/token_transfers.js new file mode 100644 index 0000000..b09e32f --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/address/token_transfers.js @@ -0,0 +1,107 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import URI from 'urijs' +import humps from 'humps' +import { subscribeChannel } from '../../socket' +import { connectElements } from '../../lib/redux_helpers.js' +import { createAsyncLoadStore } from '../../lib/async_listing_load' +import '../address' +import { isFiltered } from './utils' + +export const initialState = { + addressHash: null, + channelDisconnected: false, + filter: null +} + +export function reducer (state, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + if (state.beyondPageOne) return state + + return Object.assign({}, state, { channelDisconnected: true }) + } + case 'RECEIVED_NEW_TOKEN_TRANSFER': { + if (state.channelDisconnected) return state + + if (state.beyondPageOne || + (state.filter === 'to' && action.msg.toAddressHash !== state.addressHash) || + (state.filter === 'from' && action.msg.fromAddressHash !== state.addressHash)) { + return state + } + + return Object.assign({}, state, { items: [action.msg.tokenTransferHtml, ...state.items] }) + } + case 'RECEIVED_NEW_REWARD': { + if (state.channelDisconnected) return state + + return Object.assign({}, state, { items: [action.msg.rewardHtml, ...state.items] }) + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected && !window.loading) $el.show() + } + }, + '[data-test="filter_dropdown"]': { + render ($el, state) { + if (state.emptyResponse && !state.isSearch) { + if (isFiltered(state.filter)) { + $el.addClass('no-rm') + } else { + return $el.hide() + } + } else { + $el.removeClass('no-rm') + } + + return $el.show() + } + } +} + +if ($('[data-page="address-token-transfers"]').length) { + window.onbeforeunload = () => { + window.loading = true + } + + const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierHash') + const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash + const { filter, blockNumber } = humps.camelizeKeys(URI(window.location).query(true)) + + connectElements({ store, elements }) + + store.dispatch({ + type: 'PAGE_LOAD', + addressHash, + filter, + beyondPageOne: !!blockNumber + }) + + const addressChannel = subscribeChannel(`addresses:${addressHash}`) + addressChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) + addressChannel.on('token_transfer', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_TOKEN_TRANSFER', + msg: humps.camelizeKeys(msg) + }) + }) + + const rewardsChannel = subscribeChannel(`rewards:${addressHash}`) + rewardsChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) + rewardsChannel.on('new_reward', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_REWARD', + msg: humps.camelizeKeys(msg) + }) + }) +} diff --git a/apps/block_scout_web/assets/js/pages/address/transactions.js b/apps/block_scout_web/assets/js/pages/address/transactions.js new file mode 100644 index 0000000..9343d0c --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/address/transactions.js @@ -0,0 +1,181 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import URI from 'urijs' +import humps from 'humps' +import numeral from 'numeral' +import { subscribeChannel } from '../../socket' +import { connectElements } from '../../lib/redux_helpers.js' +import { createAsyncLoadStore } from '../../lib/async_listing_load' +import { batchChannel } from '../../lib/utils' +import '../address' +import { isFiltered } from './utils' + +const BATCH_THRESHOLD = 6 + +export const initialState = { + addressHash: null, + channelDisconnected: false, + filter: null, + transactionsBatch: [] +} + +export function reducer (state, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + if (state.beyondPageOne) return state + + return Object.assign({}, state, { channelDisconnected: true }) + } + case 'RECEIVED_NEW_TRANSACTION': { + if (state.channelDisconnected) return state + + if (state.beyondPageOne || + (state.filter === 'to' && action.msg.toAddressHash !== state.addressHash) || + (state.filter === 'from' && action.msg.fromAddressHash !== state.addressHash)) { + return state + } + + return Object.assign({}, state, { items: [action.msg.transactionHtml, ...state.items] }) + } + case 'RECEIVED_NEW_TRANSACTION_BATCH': { + if (state.channelDisconnected || state.beyondPageOne) return state + + const transactionCount = state.transactionCount + action.msgs.length + + if (!state.transactionsBatch.length && action.msgs.length < BATCH_THRESHOLD) { + return Object.assign({}, state, { + items: [ + ...action.msgs.map(msg => msg.transactionHtml).reverse(), + ...state.items + ], + transactionCount + }) + } else { + return Object.assign({}, state, { + transactionsBatch: [ + ...action.msgs.reverse(), + ...state.transactionsBatch + ], + transactionCount + }) + } + } + case 'RECEIVED_NEW_REWARD': { + if (state.channelDisconnected) return state + + return Object.assign({}, state, { items: [action.msg.rewardHtml, ...state.items] }) + } + case 'TRANSACTION_BATCH_EXPANDED': { + return Object.assign({}, state, { + transactionsBatch: [] + }) + } + case 'TRANSACTIONS_FETCHED': + return Object.assign({}, state, { items: [...action.msg.items] }) + case 'TRANSACTIONS_FETCH_ERROR': { + const $channelBatching = $('[data-selector="channel-batching-message"]') + $channelBatching.show() + return state + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected && !window.loading) $el.show() + } + }, + '[data-test="filter_dropdown"]': { + render ($el, state) { + if (state.emptyResponse && !state.isSearch) { + if (isFiltered(state.filter)) { + $el.addClass('no-rm') + } else { + return $el.hide() + } + } else { + $el.removeClass('no-rm') + } + + return $el.show() + } + }, + '[data-selector="channel-batching-count"]': { + render ($el, state, _oldState) { + const $channelBatching = $('[data-selector="channel-batching-message"]') + if (!state.transactionsBatch.length) return $channelBatching.hide() + $channelBatching.show() + $el[0].innerHTML = numeral(state.transactionsBatch.length).format() + } + } +} + +if ($('[data-page="address-transactions"]').length) { + window.onbeforeunload = () => { + window.loading = true + } + + const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierHash') + const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash + const { filter, blockNumber } = humps.camelizeKeys(URI(window.location).query(true)) + + connectElements({ store, elements }) + + store.dispatch({ + type: 'PAGE_LOAD', + addressHash, + filter, + beyondPageOne: !!blockNumber + }) + + const addressChannel = subscribeChannel(`addresses:${addressHash}`) + addressChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) + addressChannel.on('transaction', batchChannel((msgs) => + store.dispatch({ + type: 'RECEIVED_NEW_TRANSACTION_BATCH', + msgs: humps.camelizeKeys(msgs) + }) + )) + addressChannel.on('pending_transaction', batchChannel((msgs) => + store.dispatch({ + type: 'RECEIVED_NEW_TRANSACTION_BATCH', + msgs: humps.camelizeKeys(msgs) + }) + )) + + const rewardsChannel = subscribeChannel(`rewards:${addressHash}`) + rewardsChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) + rewardsChannel.on('new_reward', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_REWARD', + msg: humps.camelizeKeys(msg) + }) + }) + + const $txReloadButton = $('[data-selector="reload-transactions-button"]') + const $channelBatching = $('[data-selector="channel-batching-message"]') + $txReloadButton.on('click', (event) => { + event.preventDefault() + loadTransactions(store) + $channelBatching.hide() + store.dispatch({ + type: 'TRANSACTION_BATCH_EXPANDED' + }) + }) +} + +function loadTransactions (store) { + const path = $('[class="card-body"]')[0].dataset.asyncListing + store.dispatch({ type: 'START_TRANSACTIONS_FETCH' }) + $.getJSON(path, { type: 'JSON' }) + .done(response => store.dispatch({ type: 'TRANSACTIONS_FETCHED', msg: humps.camelizeKeys(response) })) + .fail(() => store.dispatch({ type: 'TRANSACTIONS_FETCH_ERROR' })) + .always(() => store.dispatch({ type: 'FINISH_TRANSACTIONS_FETCH' })) +} diff --git a/apps/block_scout_web/assets/js/pages/address/utils.js b/apps/block_scout_web/assets/js/pages/address/utils.js new file mode 100644 index 0000000..1e00dfa --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/address/utils.js @@ -0,0 +1,5 @@ +function isFiltered (filter) { + return (filter === 'to' || filter === 'from') +} + +export { isFiltered } diff --git a/apps/block_scout_web/assets/js/pages/address/validations.js b/apps/block_scout_web/assets/js/pages/address/validations.js new file mode 100644 index 0000000..1289239 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/address/validations.js @@ -0,0 +1,69 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import humps from 'humps' +import socket from '../../socket' +import { connectElements } from '../../lib/redux_helpers.js' +import { createAsyncLoadStore } from '../../lib/async_listing_load.js' +import '../address' + +export const initialState = { + addressHash: null, + channelDisconnected: false +} + +export function reducer (state = initialState, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + return Object.assign({}, state, { channelDisconnected: true }) + } + case 'RECEIVED_NEW_BLOCK': { + if (state.channelDisconnected) return state + if (state.beyondPageOne) return state + + return Object.assign({}, state, { + items: [ + action.blockHtml, + ...state.items + ] + }) + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected && !window.loading) $el.show() + } + } +} + +if ($('[data-page="blocks-validated"]').length) { + window.onbeforeunload = () => { + window.loading = true + } + + const store = createAsyncLoadStore(reducer, initialState, 'dataset.blockNumber') + connectElements({ store, elements }) + const addressHash = $('[data-page="address-details"]')[0].dataset.pageAddressHash + store.dispatch({ + type: 'PAGE_LOAD', + addressHash + }) + + const blocksChannel = socket.channel(`blocks:${addressHash}`, {}) + blocksChannel.join() + blocksChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + blocksChannel.on('new_block', (msg) => store.dispatch({ + type: 'RECEIVED_NEW_BLOCK', + blockHtml: humps.camelizeKeys(msg).blockHtml + })) +} diff --git a/apps/block_scout_web/assets/js/pages/admin/tasks.js b/apps/block_scout_web/assets/js/pages/admin/tasks.js new file mode 100644 index 0000000..146333a --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/admin/tasks.js @@ -0,0 +1,27 @@ +import $ from 'jquery' +import '../../app' + +const runTask = (event) => { + const element = event.currentTarget + const $element = $(element) + const $loading = $element.find('[data-loading-message]') + const $errorMessage = $element.find('[data-error-message]') + const $successMessage = $element.find('[data-success-message]') + const apiPath = element.dataset.api_path + + $errorMessage.hide() + $successMessage.hide() + $loading.show() + + $.get(apiPath) + .done(_response => { + $successMessage.show() + $loading.hide() + }) + .fail(() => { + $loading.hide() + $errorMessage.show() + }) +} + +$('#run-create-contract-methods').click(runTask) diff --git a/apps/block_scout_web/assets/js/pages/blocks.js b/apps/block_scout_web/assets/js/pages/blocks.js new file mode 100644 index 0000000..734b9d9 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/blocks.js @@ -0,0 +1,129 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import last from 'lodash.last' +import min from 'lodash.min' +import max from 'lodash.max' +import keys from 'lodash.keys' +import rangeRight from 'lodash.rangeright' +import humps from 'humps' +import socket from '../socket' +import { connectElements } from '../lib/redux_helpers.js' +import { createAsyncLoadStore } from '../lib/async_listing_load' +import '../app' + +export const initialState = { + channelDisconnected: false +} + +export const blockReducer = withMissingBlocks(baseReducer) + +function baseReducer (state = initialState, action) { + switch (action.type) { + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + return Object.assign({}, state, { + channelDisconnected: true + }) + } + case 'RECEIVED_NEW_BLOCK': { + if (state.channelDisconnected || state.beyondPageOne || state.blockType !== 'block') return state + + const blockNumber = getBlockNumber(action.msg.blockHtml) + const minBlock = getBlockNumber(last(state.items)) + + if (state.items.length && blockNumber < minBlock) return state + + return Object.assign({}, state, { + items: [action.msg.blockHtml, ...state.items] + }) + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected && !window.loading) $el.show() + } + } +} + +function getBlockNumber (blockHtml) { + return $(blockHtml).data('blockNumber') +} + +function withMissingBlocks (reducer) { + return (...args) => { + const result = reducer(...args) + + if (result.items.length < 2) return result + + const blockNumbersToItems = result.items.reduce((acc, item) => { + const blockNumber = getBlockNumber(item) + acc[blockNumber] = acc[blockNumber] || item + return acc + }, {}) + + const blockNumbers = keys(blockNumbersToItems).map(x => parseInt(x, 10)) + const minBlock = min(blockNumbers) + const maxBlock = max(blockNumbers) + if (maxBlock - minBlock > 100) return result + + return Object.assign({}, result, { + items: rangeRight(minBlock, maxBlock + 1) + .map((blockNumber) => blockNumbersToItems[blockNumber] || placeHolderBlock(blockNumber)) + }) + } +} + +const $blockListPage = $('[data-page="block-list"]') +const $uncleListPage = $('[data-page="uncle-list"]') +const $reorgListPage = $('[data-page="reorg-list"]') +if ($blockListPage.length || $uncleListPage.length || $reorgListPage.length) { + window.onbeforeunload = () => { + window.loading = true + } + + const blockType = $blockListPage.length ? 'block' : $uncleListPage.length ? 'uncle' : 'reorg' + + const store = createAsyncLoadStore( + $blockListPage.length ? blockReducer : baseReducer, + Object.assign({}, initialState, { blockType }), + 'dataset.blockNumber' + ) + connectElements({ store, elements }) + + const blocksChannel = socket.channel('blocks:new_block', {}) + blocksChannel.join() + blocksChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + blocksChannel.on('new_block', (msg) => store.dispatch({ + type: 'RECEIVED_NEW_BLOCK', + msg: humps.camelizeKeys(msg) + })) +} + +export function placeHolderBlock (blockNumber) { + return ` +
    +
    + + + + +
    + ${blockNumber} +
    ${window.localized['Block Processing']}
    +
    +
    +
    + ` +} diff --git a/apps/block_scout_web/assets/js/pages/chain.js b/apps/block_scout_web/assets/js/pages/chain.js new file mode 100644 index 0000000..7d8b64f --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/chain.js @@ -0,0 +1,412 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import first from 'lodash.first' +import rangeRight from 'lodash.rangeright' +import find from 'lodash.find' +import map from 'lodash.map' +import humps from 'humps' +import numeral from 'numeral' +import socket from '../socket' +import { updateAllCalculatedUsdValues, formatUsdValue } from '../lib/currency' +import { createStore, connectElements } from '../lib/redux_helpers.js' +import { batchChannel, showLoader } from '../lib/utils' +import listMorph from '../lib/list_morph' +import '../app' + +const BATCH_THRESHOLD = 6 +const BLOCKS_PER_PAGE = 4 + +export const initialState = { + addressCount: null, + availableSupply: null, + averageBlockTime: null, + marketHistoryData: null, + blocks: [], + blocksLoading: true, + blocksError: false, + transactions: [], + transactionsBatch: [], + transactionsError: false, + transactionsLoading: true, + transactionCount: null, + totalGasUsageCount: null, + usdMarketCap: null, + blockCount: null +} + +export const reducer = withMissingBlocks(baseReducer) + +function baseReducer (state = initialState, action) { + switch (action.type) { + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'RECEIVED_NEW_ADDRESS_COUNT': { + return Object.assign({}, state, { + addressCount: action.msg.count + }) + } + case 'RECEIVED_NEW_BLOCK': { + if (!state.blocks.length || state.blocks[0].blockNumber < action.msg.blockNumber) { + let pastBlocks + if (state.blocks.length < BLOCKS_PER_PAGE) { + pastBlocks = state.blocks + } else { + $('.miner-address-tooltip').tooltip('hide') + pastBlocks = state.blocks.slice(0, -1) + } + return Object.assign({}, state, { + averageBlockTime: action.msg.averageBlockTime, + blocks: [ + action.msg, + ...pastBlocks + ], + blockCount: action.msg.blockNumber + 1 + }) + } else { + return Object.assign({}, state, { + blocks: state.blocks.map((block) => block.blockNumber === action.msg.blockNumber ? action.msg : block), + blockCount: action.msg.blockNumber + 1 + }) + } + } + case 'START_BLOCKS_FETCH': { + return Object.assign({}, state, { blocksError: false, blocksLoading: true }) + } + case 'BLOCKS_FINISH_REQUEST': { + return Object.assign({}, state, { blocksLoading: false }) + } + case 'BLOCKS_FETCHED': { + return Object.assign({}, state, { blocks: [...action.msg.blocks], blocksLoading: false }) + } + case 'BLOCKS_REQUEST_ERROR': { + return Object.assign({}, state, { blocksError: true, blocksLoading: false }) + } + case 'RECEIVED_NEW_EXCHANGE_RATE': { + return Object.assign({}, state, { + availableSupply: action.msg.exchangeRate.availableSupply, + marketHistoryData: action.msg.marketHistoryData, + usdMarketCap: action.msg.exchangeRate.marketCapUsd + }) + } + case 'RECEIVED_NEW_TRANSACTION_BATCH': { + if (state.channelDisconnected) return state + + const transactionCount = state.transactionCount + action.msgs.length + + if (state.transactionsLoading || state.transactionsError) { + return Object.assign({}, state, { transactionCount }) + } + + const transactionsLength = state.transactions.length + action.msgs.length + if (transactionsLength < BATCH_THRESHOLD) { + return Object.assign({}, state, { + transactions: [ + ...action.msgs.reverse(), + ...state.transactions + ], + transactionCount + }) + } else if (!state.transactionsBatch.length && action.msgs.length < BATCH_THRESHOLD) { + return Object.assign({}, state, { + transactions: [ + ...action.msgs.reverse(), + ...state.transactions.slice(0, -1 * action.msgs.length) + ], + transactionCount + }) + } else { + return Object.assign({}, state, { + transactionsBatch: [ + ...action.msgs.reverse(), + ...state.transactionsBatch + ], + transactionCount + }) + } + } + case 'TRANSACTION_BATCH_EXPANDED': { + return Object.assign({}, state, { + transactionsBatch: [] + }) + } + case 'RECEIVED_UPDATED_TRANSACTION_STATS': { + return Object.assign({}, state, { + transactionStats: action.msg.stats + }) + } + case 'START_TRANSACTIONS_FETCH': + return Object.assign({}, state, { transactionsError: false, transactionsLoading: true }) + case 'TRANSACTIONS_FETCHED': + return Object.assign({}, state, { transactions: [...action.msg.transactions] }) + case 'TRANSACTIONS_FETCH_ERROR': + return Object.assign({}, state, { transactionsError: true }) + case 'FINISH_TRANSACTIONS_FETCH': + return Object.assign({}, state, { transactionsLoading: false }) + default: + return state + } +} + +function withMissingBlocks (reducer) { + return (...args) => { + const result = reducer(...args) + + if (!result.blocks || result.blocks.length < 2) return result + + const maxBlock = first(result.blocks).blockNumber + const minBlock = maxBlock - (result.blocks.length - 1) + + return Object.assign({}, result, { + blocks: rangeRight(minBlock, maxBlock + 1) + .map((blockNumber) => find(result.blocks, ['blockNumber', blockNumber]) || { + blockNumber, + chainBlockHtml: placeHolderBlock(blockNumber) + }) + }) + } +} + +let chart +const elements = { + '[data-chart="historyChart"]': { + load () { + chart = window.dashboardChart + }, + render (_$el, state, oldState) { + if (!chart || (oldState.availableSupply === state.availableSupply && oldState.marketHistoryData === state.marketHistoryData) || !state.availableSupply) return + + chart.updateMarketHistory(state.availableSupply, state.marketHistoryData) + + if (!chart || (JSON.stringify(oldState.transactionStats) === JSON.stringify(state.transactionStats))) return + + chart.updateTransactionHistory(state.transactionStats) + } + }, + '[data-selector="transaction-count"]': { + load ($el) { + return { transactionCount: numeral($el.text()).value() } + }, + render ($el, state, oldState) { + if (oldState.transactionCount === state.transactionCount) return + $el.empty().append(numeral(state.transactionCount).format()) + } + }, + '[data-selector="total-gas-usage"]': { + load ($el) { + return { totalGasUsageCount: numeral($el.text()).value() } + }, + render ($el, state, oldState) { + if (oldState.totalGasUsageCount === state.totalGasUsageCount) return + $el.empty().append(numeral(state.totalGasUsageCount).format()) + } + }, + '[data-selector="block-count"]': { + load ($el) { + return { blockCount: numeral($el.text()).value() } + }, + render ($el, state, oldState) { + if (oldState.blockCount === state.blockCount) return + $el.empty().append(numeral(state.blockCount).format()) + } + }, + '[data-selector="address-count"]': { + render ($el, state, oldState) { + if (oldState.addressCount === state.addressCount) return + $el.empty().append(state.addressCount) + } + }, + '[data-selector="average-block-time"]': { + render ($el, state, oldState) { + if (oldState.averageBlockTime === state.averageBlockTime) return + $el.empty().append(state.averageBlockTime) + } + }, + '[data-selector="market-cap"]': { + render ($el, state, oldState) { + if (oldState.usdMarketCap === state.usdMarketCap) return + $el.empty().append(formatUsdValue(state.usdMarketCap)) + } + }, + '[data-selector="tx_per_day"]': { + render ($el, state, oldState) { + if (!(JSON.stringify(oldState.transactionStats) === JSON.stringify(state.transactionStats))) { + $el.empty().append(numeral(state.transactionStats[0].number_of_transactions).format('0,0')) + } + } + }, + '[data-selector="chain-block-list"]': { + load ($el) { + return { + blocksPath: $el[0].dataset.url + } + }, + render ($el, state, oldState) { + if (oldState.blocks === state.blocks) return + + const container = $el[0] + + if (state.blocksLoading === false) { + const blocks = map(state.blocks, ({ chainBlockHtml }) => $(chainBlockHtml)[0]) + listMorph(container, blocks, { key: 'dataset.blockNumber', horizontal: true }) + } + } + }, + '[data-selector="chain-block-list"] [data-selector="error-message"]': { + render ($el, state, _oldState) { + if (state.blocksError) { + $el.show() + } else { + $el.hide() + } + } + }, + '[data-selector="chain-block-list"] [data-selector="loading-message"]': { + render ($el, state, _oldState) { + showLoader(state.blocksLoading, $el) + } + }, + '[data-selector="transactions-list"] [data-selector="error-message"]': { + render ($el, state, _oldState) { + $el.toggle(state.transactionsError) + } + }, + '[data-selector="transactions-list"] [data-selector="loading-message"]': { + render ($el, state, _oldState) { + showLoader(state.transactionsLoading, $el) + } + }, + '[data-selector="transactions-list"]': { + load ($el) { + return { transactionsPath: $el[0].dataset.transactionsPath } + }, + render ($el, state, oldState) { + if (oldState.transactions === state.transactions) return + const container = $el[0] + const newElements = map(state.transactions, ({ transactionHtml }) => $(transactionHtml)[0]) + listMorph(container, newElements, { key: 'dataset.identifierHash' }) + } + }, + '[data-selector="channel-batching-count"]': { + render ($el, state, _oldState) { + const $channelBatching = $('[data-selector="channel-batching-message"]') + if (!state.transactionsBatch.length) return $channelBatching.hide() + $channelBatching.show() + $el[0].innerHTML = numeral(state.transactionsBatch.length).format() + } + } +} + +const $chainDetailsPage = $('[data-page="chain-details"]') +if ($chainDetailsPage.length) { + const store = createStore(reducer) + connectElements({ store, elements }) + + loadTransactions(store) + bindTransactionErrorMessage(store) + + loadBlocks(store) + bindBlockErrorMessage(store) + + const exchangeRateChannel = socket.channel('exchange_rate:new_rate') + exchangeRateChannel.join() + exchangeRateChannel.on('new_rate', (msg) => { + updateAllCalculatedUsdValues(humps.camelizeKeys(msg).exchangeRate.usdValue) + store.dispatch({ + type: 'RECEIVED_NEW_EXCHANGE_RATE', + msg: humps.camelizeKeys(msg) + }) + }) + + const addressesChannel = socket.channel('addresses:new_address') + addressesChannel.join() + addressesChannel.on('count', msg => store.dispatch({ + type: 'RECEIVED_NEW_ADDRESS_COUNT', + msg: humps.camelizeKeys(msg) + })) + + const blocksChannel = socket.channel('blocks:new_block') + blocksChannel.join() + blocksChannel.on('new_block', msg => store.dispatch({ + type: 'RECEIVED_NEW_BLOCK', + msg: humps.camelizeKeys(msg) + })) + + const transactionsChannel = socket.channel('transactions:new_transaction') + transactionsChannel.join() + transactionsChannel.on('transaction', batchChannel((msgs) => store.dispatch({ + type: 'RECEIVED_NEW_TRANSACTION_BATCH', + msgs: humps.camelizeKeys(msgs) + }))) + + const transactionStatsChannel = socket.channel('transactions:stats') + transactionStatsChannel.join() + transactionStatsChannel.on('update', msg => store.dispatch({ + type: 'RECEIVED_UPDATED_TRANSACTION_STATS', + msg + })) + + const $txReloadButton = $('[data-selector="reload-transactions-button"]') + const $channelBatching = $('[data-selector="channel-batching-message"]') + $txReloadButton.on('click', (event) => { + event.preventDefault() + loadTransactions(store) + $channelBatching.hide() + store.dispatch({ + type: 'TRANSACTION_BATCH_EXPANDED' + }) + }) +} + +function loadTransactions (store) { + const path = store.getState().transactionsPath + store.dispatch({ type: 'START_TRANSACTIONS_FETCH' }) + $.getJSON(path) + .done(response => store.dispatch({ type: 'TRANSACTIONS_FETCHED', msg: humps.camelizeKeys(response) })) + .fail(() => store.dispatch({ type: 'TRANSACTIONS_FETCH_ERROR' })) + .always(() => store.dispatch({ type: 'FINISH_TRANSACTIONS_FETCH' })) +} + +function bindTransactionErrorMessage (store) { + $('[data-selector="transactions-list"] [data-selector="error-message"]').on('click', _event => loadTransactions(store)) +} + +export function placeHolderBlock (blockNumber) { + return ` +
    +
    + + + + +
    + ${blockNumber} +
    ${window.localized['Block Processing']}
    +
    +
    +
    + ` +} + +function loadBlocks (store) { + const url = store.getState().blocksPath + + store.dispatch({ type: 'START_BLOCKS_FETCH' }) + + $.getJSON(url) + .done(response => { + store.dispatch({ type: 'BLOCKS_FETCHED', msg: humps.camelizeKeys(response) }) + }) + .fail(() => store.dispatch({ type: 'BLOCKS_REQUEST_ERROR' })) + .always(() => store.dispatch({ type: 'BLOCKS_FINISH_REQUEST' })) +} + +function bindBlockErrorMessage (store) { + $('[data-selector="chain-block-list"] [data-selector="error-message"]').on('click', _event => loadBlocks(store)) +} diff --git a/apps/block_scout_web/assets/js/pages/dark-mode-switcher.js b/apps/block_scout_web/assets/js/pages/dark-mode-switcher.js new file mode 100644 index 0000000..e9e0bdc --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/dark-mode-switcher.js @@ -0,0 +1,11 @@ +import $ from 'jquery' + +$('.dark-mode-changer').click(function () { + if (localStorage.getItem('current-color-mode') === 'dark') { + localStorage.setItem('current-color-mode', 'light') + } else { + localStorage.setItem('current-color-mode', 'dark') + } + // reload each theme switch + document.location.reload(true) +}) diff --git a/apps/block_scout_web/assets/js/pages/layout.js b/apps/block_scout_web/assets/js/pages/layout.js new file mode 100644 index 0000000..43fb5a2 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/layout.js @@ -0,0 +1,56 @@ +import $ from 'jquery' +import { addChainToMM } from '../lib/add_chain_to_mm' + +$(document).click(function (event) { + const clickover = $(event.target) + const _opened = $('.navbar-collapse').hasClass('show') + if (_opened === true && $('.navbar').find(clickover).length < 1) { + $('.navbar-toggler').click() + } +}) + +const search = (value) => { + if (value) { + window.location.href = `/search?q=${value}` + } +} + +$(document) + .on('keyup', function (event) { + if (event.key === '/') { + $('.main-search-autocomplete').trigger('focus') + } + }) + .on('click', '.js-btn-add-chain-to-mm', event => { + const $btn = $(event.target) + addChainToMM({ btn: $btn }) + }) + +$('.main-search-autocomplete').on('keyup', function (event) { + if (event.key === 'Enter') { + let selected = false + $('li[id^="autoComplete_result_"]').each(function () { + if ($(this).attr('aria-selected')) { + selected = true + } + }) + if (!selected) { + search(event.target.value) + } + } +}) + +$('#search-icon').on('click', function (event) { + const value = $('.main-search-autocomplete').val() || $('.main-search-autocomplete-mobile').val() + search(value) +}) + +$('.main-search-autocomplete').on('focus', function (_event) { + $('#slash-icon').hide() + $('.search-control').addClass('focused-field') +}) + +$('.main-search-autocomplete').on('focusout', function (_event) { + $('#slash-icon').show() + $('.search-control').removeClass('focused-field') +}) diff --git a/apps/block_scout_web/assets/js/pages/pending_transactions.js b/apps/block_scout_web/assets/js/pages/pending_transactions.js new file mode 100644 index 0000000..beec314 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/pending_transactions.js @@ -0,0 +1,135 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import humps from 'humps' +import numeral from 'numeral' +import socket from '../socket' +import { connectElements } from '../lib/redux_helpers.js' +import { batchChannel } from '../lib/utils' +import { createAsyncLoadStore } from '../lib/async_listing_load' +import '../app' + +const BATCH_THRESHOLD = 10 + +export const initialState = { + channelDisconnected: false, + + pendingTransactionCount: null, + + pendingTransactionsBatch: [] +} + +export function reducer (state = initialState, action) { + switch (action.type) { + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + return Object.assign({}, state, { + channelDisconnected: true + }) + } + case 'RECEIVED_NEW_TRANSACTION': { + if (state.channelDisconnected) return state + return Object.assign({}, state, { + items: state.items.map((item) => item.includes(action.msg.transactionHash) ? action.msg.transactionHtml : item), + pendingTransactionsBatch: state.pendingTransactionsBatch.filter(transactionHtml => !transactionHtml.includes(action.msg.transactionHash)), + pendingTransactionCount: state.pendingTransactionCount - 1 + }) + } + case 'RECEIVED_NEW_PENDING_TRANSACTION_BATCH': { + if (state.channelDisconnected) return state + + const pendingTransactionCount = state.pendingTransactionCount + action.msgs.length + const pendingTransactionHtml = action.msgs.map(message => message.transactionHtml) + + if (!state.pendingTransactionsBatch.length && action.msgs.length < BATCH_THRESHOLD) { + return Object.assign({}, state, { + items: [ + ...pendingTransactionHtml.reverse(), + ...state.items + ], + pendingTransactionCount + }) + } else { + return Object.assign({}, state, { + pendingTransactionsBatch: [ + ...pendingTransactionHtml.reverse(), + ...state.pendingTransactionsBatch + ], + pendingTransactionCount + }) + } + } + case 'REMOVE_PENDING_TRANSACTION': { + return Object.assign({}, state, { + items: state.items.filter(transactionHtml => !transactionHtml.includes(action.msg.transactionHash)) + }) + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected && !window.loading) $el.show() + } + }, + '[data-selector="channel-batching-count"]': { + render ($el, state, oldState) { + const $channelBatching = $('[data-selector="channel-batching-message"]') + if (state.pendingTransactionsBatch.length) { + $channelBatching.show() + $el[0].innerHTML = numeral(state.pendingTransactionsBatch.length).format() + } else { + $channelBatching.hide() + } + } + }, + '[data-selector="transaction-pending-count"]': { + load ($el) { + return { pendingTransactionCount: numeral($el.text()).value() } + }, + render ($el, state, oldState) { + if (oldState.transactionCount === state.transactionCount) return + $el.empty().append(numeral(state.transactionCount).format()) + } + } +} + +const $transactionPendingListPage = $('[data-page="transaction-pending-list"]') +if ($transactionPendingListPage.length) { + window.onbeforeunload = () => { + window.loading = true + } + + const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierHash') + connectElements({ store, elements }) + + const transactionsChannel = socket.channel('transactions:new_transaction') + transactionsChannel.join() + transactionsChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + transactionsChannel.on('transaction', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_TRANSACTION', + msg: humps.camelizeKeys(msg) + }) + setTimeout(() => store.dispatch({ + type: 'REMOVE_PENDING_TRANSACTION', + msg: humps.camelizeKeys(msg) + }), 1000) + }) + + const pendingTransactionsChannel = socket.channel('transactions:new_pending_transaction') + pendingTransactionsChannel.join() + pendingTransactionsChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + pendingTransactionsChannel.on('pending_transaction', batchChannel((msgs) => store.dispatch({ + type: 'RECEIVED_NEW_PENDING_TRANSACTION_BATCH', + msgs: humps.camelizeKeys(msgs) + }))) +} diff --git a/apps/block_scout_web/assets/js/pages/search-results/search.js b/apps/block_scout_web/assets/js/pages/search-results/search.js new file mode 100644 index 0000000..6f7ff44 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/search-results/search.js @@ -0,0 +1,53 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import humps from 'humps' +import { createAsyncLoadStore } from '../../lib/async_listing_load' + +const $searchInput = $('.search-input') + +export const initialState = { + isSearch: false +} + +export function reducer (state, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'START_SEARCH': { + return Object.assign({}, state, { pagesStack: [], isSearch: true }) + } + default: + return state + } +} + +if ($('[data-page="search-results"]').length) { + const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierHash') + + store.dispatch({ + type: 'PAGE_LOAD' + }) + + $searchInput.on('input', (event) => { + const value = $(event.target).val() + + $('.js-search-results-query-display').text(value) + + const loc = window.location.pathname + + if (value.length >= 3 || value === '') { + store.dispatch({ type: 'START_SEARCH' }) + store.dispatch({ type: 'START_REQUEST' }) + $.ajax({ + url: `${loc}?q=${value}&type=JSON`, + type: 'GET', + dataType: 'json', + contentType: 'application/json; charset=utf-8' + }).done(response => store.dispatch(Object.assign({ type: 'ITEMS_FETCHED' }, humps.camelizeKeys(response)))) + .fail(() => store.dispatch({ type: 'REQUEST_ERROR' })) + .always(() => store.dispatch({ type: 'FINISH_REQUEST' })) + } + }) +} diff --git a/apps/block_scout_web/assets/js/pages/token/overview.js b/apps/block_scout_web/assets/js/pages/token/overview.js new file mode 100644 index 0000000..9f7fa4f --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/token/overview.js @@ -0,0 +1,11 @@ +import $ from 'jquery' +import { appendTokenIcon } from '../../lib/token_icon' + +if ($('[data-page="token-details"]').length) { + const $tokenIconContainer = $('#token-icon') + const chainID = $tokenIconContainer.data('chain-id') + const addressHash = $tokenIconContainer.data('address-hash') + const displayTokenIcons = $tokenIconContainer.data('display-token-icons') + + appendTokenIcon($tokenIconContainer, chainID, addressHash, displayTokenIcons) +} diff --git a/apps/block_scout_web/assets/js/pages/token/search.js b/apps/block_scout_web/assets/js/pages/token/search.js new file mode 100644 index 0000000..a3949f2 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/token/search.js @@ -0,0 +1,58 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import humps from 'humps' +import { createAsyncLoadStore } from '../../lib/async_listing_load' +import '../address' + +const $searchInput = $('.tokens-list-search-input') + +export const initialState = { + isSearch: false +} + +export function reducer (state, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'START_SEARCH': { + return Object.assign({}, state, { pagesStack: [], isSearch: true }) + } + default: + return state + } +} + +if ($('[data-page="tokens"]').length) { + let timer + const waitTime = 500 + + const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierHash') + + store.dispatch({ + type: 'PAGE_LOAD' + }) + + $searchInput.on('input', (event) => { + clearTimeout(timer) + timer = setTimeout(() => { + const value = $(event.target).val() + + const loc = window.location.pathname + + if (value.length >= 3 || value === '') { + store.dispatch({ type: 'START_SEARCH' }) + store.dispatch({ type: 'START_REQUEST' }) + $.ajax({ + url: `${loc}?type=JSON&filter=${value}`, + type: 'GET', + dataType: 'json', + contentType: 'application/json; charset=utf-8' + }).done(response => store.dispatch(Object.assign({ type: 'ITEMS_FETCHED' }, humps.camelizeKeys(response)))) + .fail(() => store.dispatch({ type: 'REQUEST_ERROR' })) + .always(() => store.dispatch({ type: 'FINISH_REQUEST' })) + } + }, waitTime) + }) +} diff --git a/apps/block_scout_web/assets/js/pages/token/token_transfers.js b/apps/block_scout_web/assets/js/pages/token/token_transfers.js new file mode 100644 index 0000000..bbdcf7e --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/token/token_transfers.js @@ -0,0 +1,87 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import URI from 'urijs' +import humps from 'humps' +import { subscribeChannel } from '../../socket' +import { connectElements } from '../../lib/redux_helpers.js' +import { createAsyncLoadStore } from '../../lib/async_listing_load' +import '../token_counters' + +export const initialState = { + addressHash: null, + channelDisconnected: false +} + +export function reducer (state, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + if (state.beyondPageOne) return state + + return Object.assign({}, state, { channelDisconnected: true }) + } + case 'RECEIVED_NEW_TOKEN_TRANSFER': { + if (state.channelDisconnected) return state + + if (state.beyondPageOne) { + return state + } + + return Object.assign({}, state, { items: [action.msg.tokenTransferHtml, ...state.items] }) + } + case 'RECEIVED_NEW_REWARD': { + if (state.channelDisconnected) return state + + return Object.assign({}, state, { items: [action.msg.rewardHtml, ...state.items] }) + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected && !window.loading) $el.show() + } + } +} + +if ($('[data-page="token-transfer-list"]')) { + window.onbeforeunload = () => { + window.loading = true + } + + const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierHash') + const addressHash = $('[data-page="token-details"]')[0].dataset.pageAddressHash + const { blockNumber } = humps.camelizeKeys(URI(window.location).query(true)) + + connectElements({ store, elements }) + + store.dispatch({ + type: 'PAGE_LOAD', + addressHash, + beyondPageOne: !!blockNumber + }) + + const tokensChannel = subscribeChannel(`tokens:${addressHash}`) + tokensChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) + tokensChannel.on('token_transfer', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_TOKEN_TRANSFER', + msg: humps.camelizeKeys(msg) + }) + }) + + const rewardsChannel = subscribeChannel(`rewards:${addressHash}`) + rewardsChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' })) + rewardsChannel.on('new_reward', (msg) => { + store.dispatch({ + type: 'RECEIVED_NEW_REWARD', + msg: humps.camelizeKeys(msg) + }) + }) +} diff --git a/apps/block_scout_web/assets/js/pages/token_contract.js b/apps/block_scout_web/assets/js/pages/token_contract.js new file mode 100644 index 0000000..e539dec --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/token_contract.js @@ -0,0 +1,2 @@ +import '../lib/smart_contract/index' +import './token_counters' diff --git a/apps/block_scout_web/assets/js/pages/token_counters.js b/apps/block_scout_web/assets/js/pages/token_counters.js new file mode 100644 index 0000000..62b51e0 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/token_counters.js @@ -0,0 +1,97 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import humps from 'humps' +import { createStore, connectElements } from '../lib/redux_helpers.js' +import { createAsyncLoadStore } from '../lib/async_listing_load' +import '../app' +import { + openQrModal +} from '../lib/modals' + +export const initialState = { + channelDisconnected: false, + transferCount: null, + tokenHolderCount: null +} + +export function reducer (state = initialState, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'COUNTERS_FETCHED': { + return Object.assign({}, state, { + transferCount: action.transferCount, + tokenHolderCount: action.tokenHolderCount + }) + } + default: + return state + } +} + +const elements = { + '[data-page="counters"]': { + render ($el, state) { + if (state.counters) { + return $el + } + return $el + } + }, + '[token-transfer-count]': { + render ($el, state) { + if (state.transferCount) { + $el.empty().text(state.transferCount + ' Transfers') + return $el.show() + } + } + }, + '[token-holder-count]': { + render ($el, state) { + if (state.tokenHolderCount) { + $el.empty().text(state.tokenHolderCount + ' Addresses') + return $el.show() + } + } + } +} + +function loadCounters (store) { + const $element = $('[data-async-counters]') + const path = $element.data() && $element.data().asyncCounters + function fetchCounters () { + store.dispatch({ type: 'START_REQUEST' }) + $.getJSON(path) + .done(response => store.dispatch(Object.assign({ type: 'COUNTERS_FETCHED' }, humps.camelizeKeys(response)))) + .fail(() => store.dispatch({ type: 'REQUEST_ERROR' })) + .always(() => store.dispatch({ type: 'FINISH_REQUEST' })) + } + + fetchCounters() +} + +const $tokenPage = $('[token-page]') + +if ($tokenPage.length) { + updateCounters() +} + +function updateCounters () { + const store = createStore(reducer) + connectElements({ store, elements }) + loadCounters(store) +} + +if ($('[data-page="token-holders-list"]').length) { + window.onbeforeunload = () => { + window.loading = true + } + + createAsyncLoadStore(reducer, initialState, null) +} + +$('.btn-qr-icon').click(_event => { + openQrModal() +}) diff --git a/apps/block_scout_web/assets/js/pages/transaction.js b/apps/block_scout_web/assets/js/pages/transaction.js new file mode 100644 index 0000000..373ef05 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/transaction.js @@ -0,0 +1,167 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import humps from 'humps' +import numeral from 'numeral' +import socket from '../socket' +import { createStore, connectElements } from '../lib/redux_helpers.js' +import '../lib/transaction_input_dropdown' +import '../lib/async_listing_load' +import '../app' +import Swal from 'sweetalert2' +import { compareChainIDs, formatError } from '../lib/smart_contract/common_helpers' + +export const initialState = { + blockNumber: null, + confirmations: null +} + +export function reducer (state = initialState, action) { + switch (action.type) { + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'RECEIVED_NEW_BLOCK': { + if ((action.msg.blockNumber - state.blockNumber) > state.confirmations) { + return Object.assign({}, state, { + confirmations: action.msg.blockNumber - state.blockNumber + }) + } else return state + } + default: + return state + } +} + +const elements = { + '[data-selector="block-number"]': { + load ($el) { + return { blockNumber: parseInt($el.text(), 10) } + } + }, + '[data-selector="block-confirmations"]': { + render ($el, state, oldState) { + if (oldState.confirmations !== state.confirmations) { + $el.empty().append(numeral(state.confirmations).format()) + } + } + } +} + +const $transactionDetailsPage = $('[data-page="transaction-details"]') +if ($transactionDetailsPage.length) { + const store = createStore(reducer) + connectElements({ store, elements }) + + const pathParts = window.location.pathname.split('/') + const shouldScroll = pathParts.includes('internal-transactions') || + pathParts.includes('token-transfers') || + pathParts.includes('logs') || + pathParts.includes('token-transfers') || + pathParts.includes('raw-trace') || + pathParts.includes('state') + if (shouldScroll) { + document.getElementById('transaction-tabs').scrollIntoView() + } + + const blocksChannel = socket.channel('blocks:new_block', {}) + blocksChannel.join() + blocksChannel.on('new_block', (msg) => store.dispatch({ + type: 'RECEIVED_NEW_BLOCK', + msg: humps.camelizeKeys(msg) + })) + + const transactionHash = $transactionDetailsPage[0].dataset.pageTransactionHash + const transactionChannel = socket.channel(`transactions:${transactionHash}`, {}) + transactionChannel.join() + transactionChannel.on('collated', () => window.location.reload()) + + $('.js-cancel-transaction').on('click', (event) => { + const btn = $(event.target) + if (!window.ethereum) { + btn + .attr('data-original-title', `Please unlock ${btn.data('from')} account in Metamask`) + .tooltip('show') + + setTimeout(() => { + btn + .attr('data-original-title', null) + .tooltip('dispose') + }, 3000) + return + } + const { chainId: walletChainIdHex } = window.ethereum + compareChainIDs(btn.data('chainId'), walletChainIdHex) + .then(() => { + const txParams = { + from: btn.data('from'), + to: btn.data('from'), + value: 0, + nonce: btn.data('nonce').toString() + } + window.ethereum.request({ + method: 'eth_sendTransaction', + params: [txParams] + }) + .then(function (txHash) { + const successMsg = `Canceling transaction successfully sent to the network. The current one will change the status once canceling transaction will be confirmed.` + Swal.fire({ + title: 'Success', + html: successMsg, + icon: 'success' + }) + .then(() => { + window.location.reload() + }) + }) + .catch(_error => { + btn + .attr('data-original-title', `Please unlock ${btn.data('from')} account in Metamask`) + .tooltip('show') + + setTimeout(() => { + btn + .attr('data-original-title', null) + .tooltip('dispose') + }, 3000) + }) + }) + .catch((error) => { + Swal.fire({ + title: 'Warning', + html: formatError(error), + icon: 'warning' + }) + }) + }) +} + +$(function () { + const $collapseButton = $('[button-collapse-input]') + const $expandButton = $('[button-expand-input]') + + $collapseButton.on('click', event => { + const $button = event.target + const $parent = $button.parentElement + const $collapseButton = $parent.querySelector('[button-collapse-input]') + const $expandButton = $parent.querySelector('[button-expand-input]') + const $hiddenText = $parent.querySelector('[data-hidden-text]') + const $placeHolder = $parent.querySelector('[data-placeholder-dots]') + $collapseButton.classList.add('d-none') + $expandButton.classList.remove('d-none') + $hiddenText.classList.add('d-none') + $placeHolder.classList.remove('d-none') + }) + + $expandButton.on('click', event => { + const $button = event.target + const $parent = $button.parentElement + const $collapseButton = $parent.querySelector('[button-collapse-input]') + const $expandButton = $parent.querySelector('[button-expand-input]') + const $hiddenText = $parent.querySelector('[data-hidden-text]') + const $placeHolder = $parent.querySelector('[data-placeholder-dots]') + $expandButton.classList.add('d-none') + $collapseButton.classList.remove('d-none') + $hiddenText.classList.remove('d-none') + $placeHolder.classList.add('d-none') + }) +}) diff --git a/apps/block_scout_web/assets/js/pages/transactions.js b/apps/block_scout_web/assets/js/pages/transactions.js new file mode 100644 index 0000000..dc306eb --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/transactions.js @@ -0,0 +1,106 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import humps from 'humps' +import numeral from 'numeral' +import socket from '../socket' +import { connectElements } from '../lib/redux_helpers' +import { createAsyncLoadStore } from '../lib/random_access_pagination' +import { batchChannel } from '../lib/utils' +import '../app' + +const BATCH_THRESHOLD = 10 + +export const initialState = { + channelDisconnected: false, + transactionCount: null, + transactionsBatch: [] +} + +export function reducer (state = initialState, action) { + switch (action.type) { + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + return Object.assign({}, state, { + channelDisconnected: true, + transactionsBatch: [] + }) + } + case 'RECEIVED_NEW_TRANSACTION_BATCH': { + if (state.channelDisconnected || state.beyondPageOne) return state + + const transactionCount = state.transactionCount + action.msgs.length + + if (!state.transactionsBatch.length && action.msgs.length < BATCH_THRESHOLD) { + return Object.assign({}, state, { + items: [ + ...action.msgs.map(msg => msg.transactionHtml).reverse(), + ...state.items + ], + transactionCount + }) + } else { + return Object.assign({}, state, { + transactionsBatch: [ + ...action.msgs.reverse(), + ...state.transactionsBatch + ], + transactionCount + }) + } + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected && !window.loading) $el.show() + } + }, + '[data-selector="channel-batching-count"]': { + render ($el, state, _oldState) { + const $channelBatching = $('[data-selector="channel-batching-message"]') + if (!state.transactionsBatch.length) return $channelBatching.hide() + $channelBatching.show() + $el[0].innerHTML = numeral(state.transactionsBatch.length).format() + } + }, + '[data-selector="transaction-count"]': { + load ($el) { + return { transactionCount: numeral($el.text()).value() } + }, + render ($el, state, oldState) { + if (oldState.transactionCount === state.transactionCount) return + $el.empty().append(numeral(state.transactionCount).format()) + } + } +} + +const $transactionListPage = $('[data-page="transaction-list"]') +if ($transactionListPage.length) { + window.onbeforeunload = () => { + window.loading = true + } + + const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierHash') + + connectElements({ store, elements }) + + const transactionsChannel = socket.channel('transactions:new_transaction') + transactionsChannel.join() + transactionsChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + transactionsChannel.on('transaction', batchChannel((msgs) => { + if (!store.getState().beyondPageOne && !store.getState().loading) { + store.dispatch({ + type: 'RECEIVED_NEW_TRANSACTION_BATCH', + msgs: humps.camelizeKeys(msgs) + }) + } + })) +} diff --git a/apps/block_scout_web/assets/js/pages/verification_form.js b/apps/block_scout_web/assets/js/pages/verification_form.js new file mode 100644 index 0000000..687eced --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/verification_form.js @@ -0,0 +1,367 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import humps from 'humps' +import { subscribeChannel } from '../socket' +import { createStore, connectElements } from '../lib/redux_helpers.js' +import '../app' +import Dropzone from 'dropzone' + +export const initialState = { + channelDisconnected: false, + addressHash: null, + newForm: null +} + +export function reducer (state = initialState, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'CHANNEL_DISCONNECTED': { + return Object.assign({}, state, { + channelDisconnected: true + }) + } + case 'RECEIVED_VERIFICATION_RESULT': { + if (action.msg.verificationResult === 'ok') { + return window.location.replace(window.location.href.split('/contract_verifications')[0].split('/verify')[0] + '/contracts') + } else { + return Object.assign({}, state, { + newForm: action.msg.verificationResult + }) + } + } + default: + return state + } +} + +const elements = { + '[data-selector="channel-disconnected-message"]': { + render ($el, state) { + if (state.channelDisconnected && !window.loading) $el.show() + } + }, + '[data-page="contract-verification"]': { + render ($el, state) { + if (state.newForm) { + $el.replaceWith(state.newForm) + + if ($('.nightly-builds-true').prop('checked')) { filterNightlyBuilds(false, false) } + if ($('.nightly-builds-false').prop('checked')) { filterNightlyBuilds(true, false) } + + initializeDropzone() + state.newForm = null + + return $el + } + return $el + } + } +} + +const $contractVerificationPage = $('[data-page="contract-verification"]') +const $contractVerificationChooseTypePage = $('[data-page="contract-verification-choose-type"]') + +function filterNightlyBuilds (filter, selectFirstNonNightly_) { + const select = document.getElementById('smart_contract_compiler_version') + const options = select.getElementsByTagName('option') + let selectFirstNonNightly = selectFirstNonNightly_ + + for (const option of options) { + const txtValue = option.textContent || option.innerText + if (filter) { + if (txtValue.toLowerCase().indexOf('nightly') > -1) { + option.style.display = 'none' + } else { + if (selectFirstNonNightly) { + option.selected = 'selected' + selectFirstNonNightly = false + } + option.style.display = '' + } + } else { + if (txtValue.toLowerCase().indexOf('nightly') > -1) { + option.style.display = '' + } + } + } +} + +let dropzone + +if ($contractVerificationPage.length) { + window.onbeforeunload = () => { + window.loading = true + } + + const store = createStore(reducer) + const addressHash = $('#smart_contract_address_hash').val() + + store.dispatch({ + type: 'PAGE_LOAD', + addressHash + }) + connectElements({ store, elements }) + + const addressChannel = subscribeChannel(`addresses:${addressHash}`) + + addressChannel.onError(() => store.dispatch({ + type: 'CHANNEL_DISCONNECTED' + })) + addressChannel.on('verification', (msg) => store.dispatch({ + type: 'RECEIVED_VERIFICATION_RESULT', + msg: humps.camelizeKeys(msg) + })) + + $('body').on('click', 'button[data-button-loading="animation"]', function () { + $('#loading').removeClass('d-none') + }) + + $(function () { + initializeDropzone() + + setTimeout(function () { + $('.nightly-builds-false').trigger('click') + }, 10) + + $('body').on('click', '.js-btn-add-contract-libraries', function () { + $('.js-smart-contract-libraries-wrapper').show() + $(this).hide() + }) + + $('body').on('click', '.autodetectfalse', function () { + if ($(this).prop('checked')) { $('.constructor-arguments').show() } + }) + + $('body').on('click', '.autodetecttrue', function () { + if ($(this).prop('checked')) { $('.constructor-arguments').hide() } + }) + + $('body').on('click', '.nightly-builds-true', function () { + if ($(this).prop('checked')) { filterNightlyBuilds(false, true) } + }) + + $('body').on('click', '.nightly-builds-false', function () { + if ($(this).prop('checked')) { filterNightlyBuilds(true, true) } + }) + + $('body').on('click', '.optimization-false', function () { + if ($(this).prop('checked')) { $('.optimization-runs').hide() } + }) + + $('body').on('click', '.optimization-true', function () { + if ($(this).prop('checked')) { $('.optimization-runs').show() } + }) + + $('body').on('click', '.js-smart-contract-form-reset', function () { + $('.js-contract-library-form-group').removeClass('active') + $('.js-contract-library-form-group').first().addClass('active') + $('.js-smart-contract-libraries-wrapper').hide() + $('.js-btn-add-contract-libraries').show() + $('.js-add-contract-library-wrapper').show() + }) + + $('body').on('click', '.js-btn-add-contract-library', (event) => { + const nextContractLibrary = $('.js-contract-library-form-group.active').next('.js-contract-library-form-group') + + if (nextContractLibrary) { + nextContractLibrary.addClass('active') + } + + if ($('.js-contract-library-form-group.active').length === $('.js-contract-library-form-group').length) { + $('.js-add-contract-library-wrapper').hide() + } + }) + + $('body').on('click', '#verify-via-standard-json-input-submit', (event) => { + event.preventDefault() + if (dropzone.files.length > 0) { + dropzone.processQueue() + } else { + $('#loading').addClass('d-none') + } + }) + + $('body').on('click', '[data-submit-button]', (event) => { + // submit form without page updating in order to avoid websocket reconnecting + event.preventDefault() + const $form = $('form')[0] + $.post($form.action, convertFormToJSON($form)) + }) + + $('body').on('click', '#verify-via-metadata-json-submit', (event) => { + event.preventDefault() + if (dropzone.files.length > 0) { + dropzone.processQueue() + } else { + $('#loading').addClass('d-none') + } + }) + }) +} else if ($contractVerificationChooseTypePage.length) { + $('.verify-via-flattened-code').on('click', function () { + if ($(this).prop('checked')) { + $('#verify_via_flattened_code_button').show() + $('#verify_via_sourcify_button').hide() + $('#verify_vyper_contract_button').hide() + $('#verify_via_standard_json_input_button').hide() + $('#verify_via_multi_part_files_button').hide() + } + }) + + $('.verify-via-sourcify').on('click', function () { + if ($(this).prop('checked')) { + $('#verify_via_flattened_code_button').hide() + $('#verify_via_sourcify_button').show() + $('#verify_vyper_contract_button').hide() + $('#verify_via_standard_json_input_button').hide() + $('#verify_via_multi_part_files_button').hide() + } + }) + + $('.verify-vyper-contract').on('click', function () { + if ($(this).prop('checked')) { + $('#verify_via_flattened_code_button').hide() + $('#verify_via_sourcify_button').hide() + $('#verify_vyper_contract_button').show() + $('#verify_via_standard_json_input_button').hide() + $('#verify_via_multi_part_files_button').hide() + } + }) + + $('.verify-via-standard-json-input').on('click', function () { + if ($(this).prop('checked')) { + $('#verify_via_flattened_code_button').hide() + $('#verify_via_sourcify_button').hide() + $('#verify_vyper_contract_button').hide() + $('#verify_via_standard_json_input_button').show() + $('#verify_via_multi_part_files_button').hide() + } + }) + + $('.verify-via-multi-part-files').on('click', function () { + if ($(this).prop('checked')) { + $('#verify_via_flattened_code_button').hide() + $('#verify_via_sourcify_button').hide() + $('#verify_vyper_contract_button').hide() + $('#verify_via_standard_json_input_button').hide() + $('#verify_via_multi_part_files_button').show() + } + }) +} + +function convertFormToJSON (form) { + const array = $(form).serializeArray() + const json = {} + $.each(array, function () { + json[this.name] = this.value || '' + }) + return json +} + +function changeVisibilityOfVerifyButton (filesLength) { + if (filesLength > 0) { + $('#verify-via-metadata-json-submit').prop('disabled', false) + } else { + $('#verify-via-metadata-json-submit').prop('disabled', true) + } +} + +function standardJSONBehavior () { + $('#standard-json-dropzone-form').removeClass('dz-clickable') + this.on('addedfile', function (_file) { + $('#verify-via-standard-json-input-submit').prop('disabled', false) + $('#file-help-block').text('') + $('#dropzone-previews').addClass('dz-started') + }) + + this.on('removedfile', function (_file) { + if (this.files.length === 0) { + $('#verify-via-standard-json-input-submit').prop('disabled', true) + $('#dropzone-previews').removeClass('dz-started') + } + }) +} + +function metadataJSONBehavior () { + $('#metadata-json-dropzone-form').removeClass('dz-clickable') + this.on('addedfile', function (_file) { + changeVisibilityOfVerifyButton(this.files.length) + $('#file-help-block').text('') + $('#dropzone-previews').addClass('dz-started') + }) + + this.on('removedfile', function (_file) { + changeVisibilityOfVerifyButton(this.files.length) + if (this.files.length === 0) { + $('#dropzone-previews').removeClass('dz-started') + } + }) +} + +function multiPartFilesBehavior () { + $('#metadata-json-dropzone-form').removeClass('dz-clickable') + this.on('addedfile', function (_file) { + changeVisibilityOfVerifyButton(this.files.length) + $('#file-help-block').text('') + $('#dropzone-previews').addClass('dz-started') + }) + + this.on('removedfile', function (_file) { + changeVisibilityOfVerifyButton(this.files.length) + if (this.files.length === 0) { + $('#dropzone-previews').removeClass('dz-started') + } + }) +} + +function initializeDropzone () { + const $jsonDropzoneMetadata = $('#metadata-json-dropzone-form') + const $jsonDropzoneStandardInput = $('#standard-json-dropzone-form') + + if ($jsonDropzoneMetadata.length || $jsonDropzoneStandardInput.length) { + const func = $jsonDropzoneMetadata.length ? metadataJSONBehavior : standardJSONBehavior + const maxFiles = $jsonDropzoneMetadata.length ? 100 : 1 + const acceptedFiles = $jsonDropzoneMetadata.length ? 'text/plain,application/json,.sol,.json' : 'text/plain,application/json,.json' + const tag = $jsonDropzoneMetadata.length ? '#metadata-json-dropzone-form' : '#standard-json-dropzone-form' + const jsonVerificationType = $jsonDropzoneMetadata.length ? 'json:metadata' : 'json:standard' + + dropzone = new Dropzone(tag, { + autoProcessQueue: false, + acceptedFiles, + parallelUploads: 100, + uploadMultiple: true, + addRemoveLinks: true, + maxFilesize: 10, + maxFiles, + previewsContainer: '#dropzone-previews', + params: { address_hash: $('#smart_contract_address_hash').val(), verification_type: jsonVerificationType }, + init: func + }) + } + + const $dropzoneMultiPartFiles = $('#multi-part-dropzone-form') + + if ($dropzoneMultiPartFiles.length) { + const func = multiPartFilesBehavior + const maxFiles = 100 + const acceptedFiles = 'text/plain,.sol' + const tag = '#multi-part-dropzone-form' + const jsonVerificationType = 'multi-part-files' + + dropzone = new Dropzone(tag, { + autoProcessQueue: false, + acceptedFiles, + parallelUploads: 100, + uploadMultiple: true, + addRemoveLinks: true, + maxFilesize: 10, + maxFiles, + previewsContainer: '#dropzone-previews', + params: { address_hash: $('#smart_contract_address_hash').val(), verification_type: jsonVerificationType }, + init: func + }) + } +} diff --git a/apps/block_scout_web/assets/js/pages/verified_contracts.js b/apps/block_scout_web/assets/js/pages/verified_contracts.js new file mode 100644 index 0000000..f172741 --- /dev/null +++ b/apps/block_scout_web/assets/js/pages/verified_contracts.js @@ -0,0 +1,94 @@ +import $ from 'jquery' +import omit from 'lodash.omit' +import { loadPage, createAsyncLoadStore } from '../lib/async_listing_load' +import { connectElements } from '../lib/redux_helpers.js' + +export const initialState = { + isSearch: false +} + +const elements = { + '[data-search-field]': { + render ($el, state) { + return $el + } + }, + '[data-search-button]': { + render ($el, state) { + return $el + } + }, + '[data-cancel-search-button]': { + render ($el, state) { + if (!state.isSearch) { + return $el.hide() + } + + return $el.show() + } + }, + '[data-search]': { + render ($el, state) { + if (state.emptyResponse && !state.isSearch) { + return $el.hide() + } + + return $el.show() + } + } +} + +export function reducer (state, action) { + switch (action.type) { + case 'PAGE_LOAD': + case 'ELEMENTS_LOAD': { + return Object.assign({}, state, omit(action, 'type')) + } + case 'START_SEARCH': { + return Object.assign({}, state, { pagesStack: [], isSearch: true }) + } + default: + return state + } +} + +if ($('[data-page="verified-contracts-list"]').length) { + let timer + const waitTime = 500 + + const $element = $('[data-async-listing]') + + $element.on('click', '[data-next-page-button], [data-prev-page-button]', (event) => { + document.getElementById('verified-contracts-list').scrollIntoView() + }) + + const store = createAsyncLoadStore(reducer, initialState, 'dataset.identifierHash') + + connectElements({ store, elements }) + + const searchFunc = (_event) => { + store.dispatch({ type: 'START_SEARCH' }) + const searchInput = $('[data-search-field]').val() + const pathHaveNoParams = window.location.pathname + '?search=' + searchInput + const pathHaveParams = window.location.pathname + window.location.search + '&search=' + searchInput + const path = window.location.href.includes('?') ? pathHaveParams : pathHaveNoParams + loadPage(store, path) + } + + store.dispatch({ + type: 'PAGE_LOAD' + }) + + $element.on('input keyup', '[data-search-field]', (event) => { + if (event.type === 'input') { + clearTimeout(timer) + timer = setTimeout(() => { + searchFunc(event) + }, waitTime) + } + if (event.type === 'keyup' && event.keyCode === 13) { + clearTimeout(timer) + searchFunc(event) + } + }) +} diff --git a/apps/block_scout_web/assets/js/socket.js b/apps/block_scout_web/assets/js/socket.js new file mode 100644 index 0000000..1dc41b5 --- /dev/null +++ b/apps/block_scout_web/assets/js/socket.js @@ -0,0 +1,38 @@ +import { Socket } from 'phoenix' +import { locale } from './locale' + +let websocketRootUrl = process.env.SOCKET_ROOT +if (!websocketRootUrl) { + websocketRootUrl = '' +} +if (websocketRootUrl.endsWith('/')) { + websocketRootUrl = websocketRootUrl.slice(0, -1) +} + +const socket = new Socket(websocketRootUrl + '/socket', { params: { locale } }) +socket.connect() + +export default socket + +/** + * Subscribes the client in the channel given the topic. + * + * This function will check if already exist a channel before creating one. This is useful because + * when the client is attempting to create a duplicated subscription, the server will close the + * existing subscription and create a new one. + * + * See more about it in https://hexdocs.pm/phoenix/js/#phoenix. + * + * Returns a Channel instance. + */ +export function subscribeChannel (topic) { + const channel = socket.channels.find(channel => channel.topic === topic) + + if (channel) { + return channel + } else { + const channel = socket.channel(topic, {}) + channel.join() + return channel + } +} diff --git a/apps/block_scout_web/assets/js/view_specific/address_contract/code_highlighting.js b/apps/block_scout_web/assets/js/view_specific/address_contract/code_highlighting.js new file mode 100644 index 0000000..2a0068a --- /dev/null +++ b/apps/block_scout_web/assets/js/view_specific/address_contract/code_highlighting.js @@ -0,0 +1,30 @@ +import '../../lib/ace/src-min/ace' +import '../../lib/ace/src-min/mode-csharp' +import '../../lib/ace/src-min/theme-chrome' +import $ from 'jquery' + +/* eslint-disable-next-line */ +const Mode = ace.require('ace/mode/csharp').Mode + +const codeMain = $('#code_viewer_main') +const code = codeMain.text() +/* eslint-disable-next-line */ +const editor = (codeMain.length > 0) && ace.edit('code_viewer_main') +if (editor) { + editor.session.setMode(new Mode()) + editor.setTheme('ace/theme/chrome') + editor.setValue(code, -1) + editor.setOptions({ maxLines: 40, readOnly: true, printMargin: false }) + + const len = codeMain.data('additional-sources-length') + for (let i = 0; i < len; i++) { + const tag = 'code_viewer_' + i + const code = $('#' + tag).text() + /* eslint-disable-next-line */ + const editor = ace.edit(tag) + editor.session.setMode(new Mode()) + editor.setTheme('ace/theme/chrome') + editor.setValue(code, -1) + editor.setOptions({ maxLines: 40, readOnly: true }) + } +} diff --git a/apps/block_scout_web/assets/js/view_specific/raw_trace/code_highlighting.js b/apps/block_scout_web/assets/js/view_specific/raw_trace/code_highlighting.js new file mode 100644 index 0000000..5256930 --- /dev/null +++ b/apps/block_scout_web/assets/js/view_specific/raw_trace/code_highlighting.js @@ -0,0 +1,6 @@ +import hljs from 'highlight.js/lib/core' + +// only activate highlighting on pages with this selector +if (document.querySelectorAll('[data-activate-highlight]').length > 0) { + hljs.highlightAll() +} diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json new file mode 100644 index 0000000..d3d1463 --- /dev/null +++ b/apps/block_scout_web/assets/package-lock.json @@ -0,0 +1,32539 @@ +{ + "name": "blockscout", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "blockscout", + "license": "GPL-3.0", + "dependencies": { + "@fortawesome/fontawesome-free": "^6.2.0", + "@tarekraafat/autocomplete.js": "^10.2.7", + "@walletconnect/web3-provider": "^1.8.0", + "assert": "^2.0.0", + "bignumber.js": "^9.1.0", + "bootstrap": "^4.6.0", + "chart.js": "^3.9.1", + "chartjs-adapter-luxon": "^1.2.0", + "clipboard": "^2.0.11", + "core-js": "^3.26.0", + "crypto-browserify": "^3.12.0", + "dropzone": "^5.9.3", + "eth-net-props": "^1.0.41", + "highlight.js": "^11.6.0", + "https-browserify": "^1.0.0", + "humps": "^2.0.1", + "jquery": "^3.6.1", + "js-cookie": "^3.0.1", + "lodash.debounce": "^4.0.8", + "lodash.differenceby": "^4.8.0", + "lodash.find": "^4.6.0", + "lodash.first": "^3.0.0", + "lodash.forin": "^4.4.0", + "lodash.get": "^4.4.2", + "lodash.intersectionby": "^4.7.0", + "lodash.isobject": "^3.0.2", + "lodash.keys": "^4.2.0", + "lodash.last": "^3.0.0", + "lodash.map": "^4.6.0", + "lodash.max": "^4.0.1", + "lodash.merge": "^4.6.2", + "lodash.min": "^4.0.1", + "lodash.noop": "^3.0.1", + "lodash.omit": "^4.5.0", + "lodash.rangeright": "^4.2.0", + "lodash.reduce": "^4.6.0", + "luxon": "^3.1.0", + "moment": "^2.29.4", + "nanomorph": "^5.4.0", + "numeral": "^2.0.6", + "os-browserify": "^0.3.0", + "path-parser": "^6.1.0", + "phoenix": "file:../../../deps/phoenix", + "phoenix_html": "file:../../../deps/phoenix_html", + "pikaday": "^1.8.2", + "popper.js": "^1.14.7", + "reduce-reducers": "^1.0.4", + "redux": "^4.2.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.1.1", + "sweetalert2": "^11.6.7", + "urijs": "^1.19.11", + "url": "^0.11.0", + "util": "^0.12.5", + "web3": "^1.8.0", + "web3modal": "^1.9.9", + "xss": "^1.0.14" + }, + "devDependencies": { + "@babel/core": "^7.20.2", + "@babel/preset-env": "^7.20.2", + "autoprefixer": "^10.4.13", + "babel-loader": "^9.1.0", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^5.2.7", + "css-minimizer-webpack-plugin": "^4.2.2", + "eslint": "^8.27.0", + "eslint-config-standard": "^17.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.1.1", + "file-loader": "^6.2.0", + "jest": "^29.3.1", + "jest-environment-jsdom": "^29.3.0", + "mini-css-extract-plugin": "^2.6.1", + "postcss": "^8.4.18", + "postcss-loader": "^7.0.1", + "sass": "^1.56.0", + "sass-loader": "^13.1.0", + "style-loader": "^3.3.1", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0" + }, + "engines": { + "node": "16.x", + "npm": "8.x" + } + }, + "../../../deps/phoenix": { + "version": "1.5.13", + "license": "MIT" + }, + "../../../deps/phoenix_html": { + "version": "3.0.4" + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ampproject/remapping/node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz", + "integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz", + "integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.2", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.1", + "@babel/parser": "^7.20.2", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.3.tgz", + "integrity": "sha512-Wl5ilw2UD1+ZYprHVprxHZJCFeBWlzZYOovE4SDYLZnqCOD11j+0QzNeEWKLLTWM7nixrZEh7vNIyb76MyJg3A==", + "dependencies": { + "@babel/types": "^7.20.2", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dev": true, + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dependencies": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", + "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz", + "integrity": "sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.18.9", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.11", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz", + "integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==", + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz", + "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz", + "integrity": "sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz", + "integrity": "sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz", + "integrity": "sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz", + "integrity": "sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.19.1", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz", + "integrity": "sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.19.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", + "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz", + "integrity": "sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.7.tgz", + "integrity": "sha512-2FoHiSAWkdq4L06uaDN3rS43i6x28desUVxq+zAFuE6kbWYQeiLPJI5IC7Sg9xKYVcrBKSQkVUfH6aeQYbl9QA==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.4.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.4.0.tgz", + "integrity": "sha512-YxFreYwUfglYKdLUGvIF2nJEsGwj+RhWSX/ije3D2vQPOXuyMLMtg/cCGMDpOA7Nd+MwlNdnGODbd2EwUZPlsw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.0", + "core-js-compat": "^3.18.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.20.1", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz", + "integrity": "sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", + "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.1", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz", + "integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==", + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz", + "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" + }, + "node_modules/@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "node_modules/@eslint/eslintrc": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ethereumjs/common": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.5.tgz", + "integrity": "sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA==", + "dependencies": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.5" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.2.tgz", + "integrity": "sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw==", + "dependencies": { + "@ethereumjs/common": "^2.6.4", + "ethereumjs-util": "^7.1.5" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.2.0.tgz", + "integrity": "sha512-CNR7qRIfCwWHNN7FnKUniva94edPdyQzil/zCwk3v6k4R6rR2Fr8i4s3PM7n/lyfPA6Zfko9z5WDzFxG9SW1uQ==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", + "integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.6.tgz", + "integrity": "sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.3.1.tgz", + "integrity": "sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/console/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/console/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/console/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.3.1.tgz", + "integrity": "sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==", + "dev": true, + "dependencies": { + "@jest/console": "^29.3.1", + "@jest/reporters": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.2.0", + "jest-config": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-resolve-dependencies": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "jest-watcher": "^29.3.1", + "micromatch": "^4.0.4", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/core/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.3.1.tgz", + "integrity": "sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-mock": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==", + "dev": true, + "dependencies": { + "expect": "^29.3.1", + "jest-snapshot": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz", + "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.2.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.3.1.tgz", + "integrity": "sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.3.1.tgz", + "integrity": "sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/types": "^29.3.1", + "jest-mock": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.3.1.tgz", + "integrity": "sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/reporters/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/jest-worker": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.3.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", + "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.2.0.tgz", + "integrity": "sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.3.1.tgz", + "integrity": "sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==", + "dev": true, + "dependencies": { + "@jest/console": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz", + "integrity": "sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.3.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.3.1.tgz", + "integrity": "sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.3.1", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-util": "^29.3.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/transform/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@jest/transform/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", + "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.0.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@metamask/safe-event-emitter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz", + "integrity": "sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.0.0.tgz", + "integrity": "sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.24.27", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.27.tgz", + "integrity": "sha512-K7C7IlQ3zLePEZleUN21ceBA2aLcMnLHTLph8QWk1JK37L90obdpY+QGY8bXMKxf1ht1Z0MNewvXxWv0oGDYFg==", + "dev": true + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@tarekraafat/autocomplete.js": { + "version": "10.2.7", + "resolved": "https://registry.npmjs.org/@tarekraafat/autocomplete.js/-/autocomplete.js-10.2.7.tgz", + "integrity": "sha512-iE+dnXI8/LrTaSORrnNdSyXg/bFCbCpz/R5GUdB3ioW+9PVEhglxNcSDQNeCXtrbRG0kOBFUd4unEiwcmqyn8A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/autocompletejs" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/TarekRaafat" + }, + { + "type": "patreon", + "url": "https://patreon.com/TarekRaafat" + } + ] + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", + "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz", + "integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jsdom": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.0.tgz", + "integrity": "sha512-YfAchFs0yM1QPDrLm2VHe+WHGtqms3NXnXAMolrgrVP6fgBHHXy1ozAbo/dFtPNtZC/m66bPiCTWYmqp1F14gA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@types/node": { + "version": "16.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", + "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==" + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==", + "dev": true + }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", + "integrity": "sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@walletconnect/browser-utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/browser-utils/-/browser-utils-1.8.0.tgz", + "integrity": "sha512-Wcqqx+wjxIo9fv6eBUFHPsW1y/bGWWRboni5dfD8PtOmrihrEpOCmvRJe4rfl7xgJW8Ea9UqKEaq0bIRLHlK4A==", + "dependencies": { + "@walletconnect/safe-json": "1.0.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/window-getters": "1.0.0", + "@walletconnect/window-metadata": "1.0.0", + "detect-browser": "5.2.0" + } + }, + "node_modules/@walletconnect/browser-utils/node_modules/detect-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.0.tgz", + "integrity": "sha512-tr7XntDAu50BVENgQfajMLzacmSe34D+qZc4zjnniz0ZVuw/TZcLcyxHQjYpJTM36sGEkZZlYLnIM1hH7alTMA==" + }, + "node_modules/@walletconnect/client": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/client/-/client-1.8.0.tgz", + "integrity": "sha512-svyBQ14NHx6Cs2j4TpkQaBI/2AF4+LXz64FojTjMtV4VMMhl81jSO1vNeg+yYhQzvjcGH/GpSwixjyCW0xFBOQ==", + "dependencies": { + "@walletconnect/core": "^1.8.0", + "@walletconnect/iso-crypto": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "node_modules/@walletconnect/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-1.8.0.tgz", + "integrity": "sha512-aFTHvEEbXcZ8XdWBw6rpQDte41Rxwnuk3SgTD8/iKGSRTni50gI9S3YEzMj05jozSiOBxQci4pJDMVhIUMtarw==", + "dependencies": { + "@walletconnect/socket-transport": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "node_modules/@walletconnect/crypto": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/crypto/-/crypto-1.0.2.tgz", + "integrity": "sha512-+OlNtwieUqVcOpFTvLBvH+9J9pntEqH5evpINHfVxff1XIgwV55PpbdvkHu6r9Ib4WQDOFiD8OeeXs1vHw7xKQ==", + "dependencies": { + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/environment": "^1.0.0", + "@walletconnect/randombytes": "^1.0.2", + "aes-js": "^3.1.2", + "hash.js": "^1.1.7" + } + }, + "node_modules/@walletconnect/encoding": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/encoding/-/encoding-1.0.1.tgz", + "integrity": "sha512-8opL2rs6N6E3tJfsqwS82aZQDL3gmupWUgmvuZ3CGU7z/InZs3R9jkzH8wmYtpbq0sFK3WkJkQRZFFk4BkrmFA==", + "dependencies": { + "is-typedarray": "1.0.0", + "typedarray-to-buffer": "3.1.5" + } + }, + "node_modules/@walletconnect/environment": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/environment/-/environment-1.0.0.tgz", + "integrity": "sha512-4BwqyWy6KpSvkocSaV7WR3BlZfrxLbJSLkg+j7Gl6pTDE+U55lLhJvQaMuDVazXYxcjBsG09k7UlH7cGiUI5vQ==" + }, + "node_modules/@walletconnect/http-connection": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/http-connection/-/http-connection-1.8.0.tgz", + "integrity": "sha512-IziEr3c53qsMromK7jz0EkbKDHlryRbxXdFR+xaG+S5nfxtUdAfjzlZabvczXdDCgmTij6KbNsZAjBMqCBzACw==", + "dependencies": { + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0", + "eventemitter3": "4.0.7", + "xhr2-cookies": "1.1.0" + } + }, + "node_modules/@walletconnect/http-connection/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/@walletconnect/iso-crypto": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/iso-crypto/-/iso-crypto-1.8.0.tgz", + "integrity": "sha512-pWy19KCyitpfXb70hA73r9FcvklS+FvO9QUIttp3c2mfW8frxgYeRXfxLRCIQTkaYueRKvdqPjbyhPLam508XQ==", + "dependencies": { + "@walletconnect/crypto": "^1.0.2", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "node_modules/@walletconnect/jsonrpc-types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.1.tgz", + "integrity": "sha512-+6coTtOuChCqM+AoYyi4Q83p9l/laI6NvuM2/AHaZFuf0gT0NjW7IX2+86qGyizn7Ptq4AYZmfxurAxTnhefuw==", + "dependencies": { + "keyvaluestorage-interface": "^1.0.0" + } + }, + "node_modules/@walletconnect/jsonrpc-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.3.tgz", + "integrity": "sha512-3yb49bPk16MNLk6uIIHPSHQCpD6UAo1OMOx1rM8cW/MPEAYAzrSW5hkhG7NEUwX9SokRIgnZK3QuQkiyNzBMhQ==", + "dependencies": { + "@walletconnect/environment": "^1.0.0", + "@walletconnect/jsonrpc-types": "^1.0.1" + } + }, + "node_modules/@walletconnect/mobile-registry": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@walletconnect/mobile-registry/-/mobile-registry-1.4.0.tgz", + "integrity": "sha512-ZtKRio4uCZ1JUF7LIdecmZt7FOLnX72RPSY7aUVu7mj7CSfxDwUn6gBuK6WGtH+NZCldBqDl5DenI5fFSvkKYw==", + "deprecated": "Deprecated in favor of dynamic registry available from: https://github.com/walletconnect/walletconnect-registry" + }, + "node_modules/@walletconnect/qrcode-modal": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/qrcode-modal/-/qrcode-modal-1.8.0.tgz", + "integrity": "sha512-BueaFefaAi8mawE45eUtztg3ZFbsAH4DDXh1UNwdUlsvFMjqcYzLUG0xZvDd6z2eOpbgDg2N3bl6gF0KONj1dg==", + "dependencies": { + "@walletconnect/browser-utils": "^1.8.0", + "@walletconnect/mobile-registry": "^1.4.0", + "@walletconnect/types": "^1.8.0", + "copy-to-clipboard": "^3.3.1", + "preact": "10.4.1", + "qrcode": "1.4.4" + } + }, + "node_modules/@walletconnect/randombytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/randombytes/-/randombytes-1.0.2.tgz", + "integrity": "sha512-ivgOtAyqQnN0rLQmOFPemsgYGysd/ooLfaDA/ACQ3cyqlca56t3rZc7pXfqJOIETx/wSyoF5XbwL+BqYodw27A==", + "dependencies": { + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/environment": "^1.0.0", + "randombytes": "^2.1.0" + } + }, + "node_modules/@walletconnect/safe-json": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.0.tgz", + "integrity": "sha512-QJzp/S/86sUAgWY6eh5MKYmSfZaRpIlmCJdi5uG4DJlKkZrHEF7ye7gA+VtbVzvTtpM/gRwO2plQuiooIeXjfg==" + }, + "node_modules/@walletconnect/socket-transport": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/socket-transport/-/socket-transport-1.8.0.tgz", + "integrity": "sha512-5DyIyWrzHXTcVp0Vd93zJ5XMW61iDM6bcWT4p8DTRfFsOtW46JquruMhxOLeCOieM4D73kcr3U7WtyR4JUsGuQ==", + "dependencies": { + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0", + "ws": "7.5.3" + } + }, + "node_modules/@walletconnect/socket-transport/node_modules/ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@walletconnect/types": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-1.8.0.tgz", + "integrity": "sha512-Cn+3I0V0vT9ghMuzh1KzZvCkiAxTq+1TR2eSqw5E5AVWfmCtECFkVZBP6uUJZ8YjwLqXheI+rnjqPy7sVM4Fyg==" + }, + "node_modules/@walletconnect/utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-1.8.0.tgz", + "integrity": "sha512-zExzp8Mj1YiAIBfKNm5u622oNw44WOESzo6hj+Q3apSMIb0Jph9X3GDIdbZmvVZsNPxWDL7uodKgZcCInZv2vA==", + "dependencies": { + "@walletconnect/browser-utils": "^1.8.0", + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/jsonrpc-utils": "^1.0.3", + "@walletconnect/types": "^1.8.0", + "bn.js": "4.11.8", + "js-sha3": "0.8.0", + "query-string": "6.13.5" + } + }, + "node_modules/@walletconnect/utils/node_modules/bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "node_modules/@walletconnect/utils/node_modules/query-string": { + "version": "6.13.5", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.5.tgz", + "integrity": "sha512-svk3xg9qHR39P3JlHuD7g3nRnyay5mHbrPctEBDUxUkHRifPHXJDhBUycdCC0NBjXoDf44Gb+IsOZL1Uwn8M/Q==", + "dependencies": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@walletconnect/utils/node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/@walletconnect/web3-provider": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/web3-provider/-/web3-provider-1.8.0.tgz", + "integrity": "sha512-lqqEO0oRmCehH+c8ZPk3iH7I7YtbzmkWd58/Or2AgWAl869JamzndKCD3sTlNsPRQLxxPpraHQqzur7uclLWvg==", + "dependencies": { + "@walletconnect/client": "^1.8.0", + "@walletconnect/http-connection": "^1.8.0", + "@walletconnect/qrcode-modal": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0", + "web3-provider-engine": "16.0.1" + } + }, + "node_modules/@walletconnect/window-getters": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.0.tgz", + "integrity": "sha512-xB0SQsLaleIYIkSsl43vm8EwETpBzJ2gnzk7e0wMF3ktqiTGS6TFHxcprMl5R44KKh4tCcHCJwolMCaDSwtAaA==" + }, + "node_modules/@walletconnect/window-metadata": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.0.tgz", + "integrity": "sha512-9eFvmJxIKCC3YWOL97SgRkKhlyGXkrHwamfechmqszbypFspaSk+t2jQXAEU7YClHF6Qjw5eYOmy1//zFi9/GA==", + "dependencies": { + "@walletconnect/window-getters": "^1.0.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x", + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "dependencies": { + "envinfo": "^7.7.3" + }, + "peerDependencies": { + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true, + "peerDependencies": { + "webpack-cli": "4.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/abortcontroller-polyfill": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz", + "integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==" + }, + "node_modules/abstract-leveldown": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz", + "integrity": "sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA==", + "dependencies": { + "xtend": "~4.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.0.tgz", + "integrity": "sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "dependencies": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "engines": { + "node": "*" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-eventemitter": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz", + "integrity": "sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==", + "dependencies": { + "async": "^2.4.0" + } + }, + "node_modules/async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "node_modules/async-mutex": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.2.6.tgz", + "integrity": "sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==", + "dependencies": { + "tslib": "^2.0.0" + } + }, + "node_modules/async-mutex/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/autoprefixer": { + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "node_modules/babel-jest": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", + "integrity": "sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.3.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.2.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/babel-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-loader": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.0.tgz", + "integrity": "sha512-Antt61KJPinUMwHwIIz9T5zfMgevnfZkEVWYDWlG888fgdvRRGD0JTuf/fFozQnfT+uq64sk1bmdHDy/mOEWnA==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.2", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz", + "integrity": "sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-styled-components": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.13.3.tgz", + "integrity": "sha512-meGStRGv+VuKA/q0/jXxrPNWEm4LPfYIqxooDTdmh8kFsP/Ph7jJG5rUPwUPX3QHUvggwdbgdGpo88P/rRYsVw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.15.4", + "@babel/helper-module-imports": "^7.15.4", + "babel-plugin-syntax-jsx": "^6.18.0", + "lodash": "^4.17.11" + }, + "peerDependencies": { + "styled-components": ">= 2" + } + }, + "node_modules/babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz", + "integrity": "sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.2.0", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", + "dependencies": { + "precond": "0.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/bignumber.js": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz", + "integrity": "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/blakejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", + "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==" + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/bootstrap": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz", + "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + }, + "peerDependencies": { + "jquery": "1.9.1 - 3", + "popper.js": "^1.16.1" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ==" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "node_modules/bufferutil": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", + "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "peer": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacheable-lookup": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", + "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001426", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001426.tgz", + "integrity": "sha512-n7cosrHLl8AWt0wwZw/PJZgUg3lV0gk9LMI7ikGJwhyhgsd2Nb65vKvmSexCqq/J7rbH3mFG6yZZiPR5dLPW5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "node_modules/chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chart.js": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz", + "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==" + }, + "node_modules/chartjs-adapter-luxon": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/chartjs-adapter-luxon/-/chartjs-adapter-luxon-1.2.0.tgz", + "integrity": "sha512-h1lEns7+8cUN/Dmk24dhrT9hpAimKImQxzHpILqXn2kocdzj9b/fDlBa8v8/OMq5rq0uZEx/NV1WpByH4l2/Rw==", + "peerDependencies": { + "chart.js": "^3.0.0", + "luxon": ">=1.0.0" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "engines": { + "node": "*" + } + }, + "node_modules/checkpoint-store": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/checkpoint-store/-/checkpoint-store-1.1.0.tgz", + "integrity": "sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY=", + "dependencies": { + "functional-red-black-tree": "^1.0.1" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", + "dev": true + }, + "node_modules/cids": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", + "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "buffer": "^5.5.0", + "class-is": "^1.1.0", + "multibase": "~0.6.0", + "multicodec": "^1.0.0", + "multihashes": "~0.4.15" + }, + "engines": { + "node": ">=4.0.0", + "npm": ">=3.0.0" + } + }, + "node_modules/cids/node_modules/multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "node_modules/class-is": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", + "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/clipboard": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", + "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==", + "dependencies": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "optional": true, + "peer": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-hash": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", + "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", + "dependencies": { + "cids": "^0.7.1", + "multicodec": "^0.5.5", + "multihashes": "^0.4.15" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz", + "integrity": "sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/core-js": { + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.0.tgz", + "integrity": "sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.1.tgz", + "integrity": "sha512-pOHS7O0i8Qt4zlPW/eIFjwp+NrTPx+wTL0ctgI2fHn31sZOq89rDsmtc/A2vAX7r6shl+bmVI+678He46jgBlw==", + "dependencies": { + "browserslist": "^4.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-fetch": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.6.tgz", + "integrity": "sha512-9JZz+vXCmfKUZ68zAptS7k4Nu8e2qcibe7WVZYps7sAgk5R8GYTc+T1WR0v1rlP9HxgARmOX1UTIJZFytajpNA==", + "dependencies": { + "node-fetch": "^2.6.7", + "whatwg-fetch": "^2.0.4" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", + "integrity": "sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-loader": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.27.0 || ^5.0.0" + } + }, + "node_modules/css-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-loader/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", + "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "dev": true, + "dependencies": { + "cssnano": "^5.1.8", + "jest-worker": "^29.1.2", + "postcss": "^8.4.17", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/jest-worker": { + "version": "29.1.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.1.2.tgz", + "integrity": "sha512-AdTZJxKjTSPHbXT/AIOjQVmoFx0LHFcVabWu0sxI7PAy7rFf8c0upyvgBKgguVXdM4vY74JdwkyD4hSmpTW8jA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.1.2", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-to-react-native": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", + "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=" + }, + "node_modules/cssnano": { + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.12.tgz", + "integrity": "sha512-TgvArbEZu0lk/dvg2ja+B7kYoD7BBCmn3+k58xD0qjrGHsFzXY/wKTo9M5egcUCabPol05e/PVoIu79s2JN4WQ==", + "dev": true, + "dependencies": { + "cssnano-preset-default": "^5.2.12", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz", + "integrity": "sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew==", + "dev": true, + "dependencies": { + "css-declaration-sorter": "^6.3.0", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.2", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.6", + "postcss-merge-rules": "^5.1.2", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.3", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.0.tgz", + "integrity": "sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg==", + "dev": true + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/deferred-leveldown": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", + "integrity": "sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA==", + "dependencies": { + "abstract-leveldown": "~2.6.0" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.1.tgz", + "integrity": "sha512-eAcRiEPTs7utXWPaAgu/OX1HRJpxW7xSHpw4LTDrGFaeWnJ37HRlqpUkKsDm0AoTbtrvHQhH+5U2Cd87EGhJTg==" + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz", + "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/dijkstrajs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.2.tgz", + "integrity": "sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dropzone": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.9.3.tgz", + "integrity": "sha512-Azk8kD/2/nJIuVPK+zQ9sjKMRIpRvNyqn9XwbBHNq+iNuSccbJS6hwm1Woy0pMST0erSo0u4j+KJaodndDk4vA==" + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.256", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz", + "integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw==" + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "peer": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.27.0.tgz", + "integrity": "sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.15.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-standard": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz", + "integrity": "sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", + "dev": true, + "peer": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-plugin-n": { + "version": "15.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.2.2.tgz", + "integrity": "sha512-MLjZVAv4TiCIoXqjibNqCJjLkGHfrOY3XZ0ZBLoW0OnS3o98PUBnzB/kfp8dCz/4A4Y18jjX50PRnqI4ACFY1Q==", + "dev": true, + "peer": true, + "dependencies": { + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", + "ignore": "^5.1.1", + "is-core-module": "^2.9.0", + "minimatch": "^3.1.2", + "resolve": "^1.10.1", + "semver": "^7.3.7" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "peer": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-plugin-n/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-n/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-n/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eth-block-tracker": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz", + "integrity": "sha512-A8tG4Z4iNg4mw5tP1Vung9N9IjgMNqpiMoJ/FouSFwNCGHv2X0mmOYwtQOJzki6XN7r7Tyo01S29p7b224I4jw==", + "dependencies": { + "@babel/plugin-transform-runtime": "^7.5.5", + "@babel/runtime": "^7.5.5", + "eth-query": "^2.1.0", + "json-rpc-random-id": "^1.0.1", + "pify": "^3.0.0", + "safe-event-emitter": "^1.0.1" + } + }, + "node_modules/eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw==", + "dependencies": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + } + }, + "node_modules/eth-ens-namehash/node_modules/js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==" + }, + "node_modules/eth-json-rpc-filters": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-4.2.2.tgz", + "integrity": "sha512-DGtqpLU7bBg63wPMWg1sCpkKCf57dJ+hj/k3zF26anXMzkmtSBDExL8IhUu7LUd34f0Zsce3PYNO2vV2GaTzaw==", + "dependencies": { + "@metamask/safe-event-emitter": "^2.0.0", + "async-mutex": "^0.2.6", + "eth-json-rpc-middleware": "^6.0.0", + "eth-query": "^2.1.2", + "json-rpc-engine": "^6.1.0", + "pify": "^5.0.0" + } + }, + "node_modules/eth-json-rpc-filters/node_modules/pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eth-json-rpc-infura": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eth-json-rpc-infura/-/eth-json-rpc-infura-5.1.0.tgz", + "integrity": "sha512-THzLye3PHUSGn1EXMhg6WTLW9uim7LQZKeKaeYsS9+wOBcamRiCQVGHa6D2/4P0oS0vSaxsBnU/J6qvn0MPdow==", + "dependencies": { + "eth-json-rpc-middleware": "^6.0.0", + "eth-rpc-errors": "^3.0.0", + "json-rpc-engine": "^5.3.0", + "node-fetch": "^2.6.0" + } + }, + "node_modules/eth-json-rpc-infura/node_modules/json-rpc-engine": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz", + "integrity": "sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g==", + "dependencies": { + "eth-rpc-errors": "^3.0.0", + "safe-event-emitter": "^1.0.1" + } + }, + "node_modules/eth-json-rpc-middleware": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-6.0.0.tgz", + "integrity": "sha512-qqBfLU2Uq1Ou15Wox1s+NX05S9OcAEL4JZ04VZox2NS0U+RtCMjSxzXhLFWekdShUPZ+P8ax3zCO2xcPrp6XJQ==", + "dependencies": { + "btoa": "^1.2.1", + "clone": "^2.1.1", + "eth-query": "^2.1.2", + "eth-rpc-errors": "^3.0.0", + "eth-sig-util": "^1.4.2", + "ethereumjs-util": "^5.1.2", + "json-rpc-engine": "^5.3.0", + "json-stable-stringify": "^1.0.1", + "node-fetch": "^2.6.1", + "pify": "^3.0.0", + "safe-event-emitter": "^1.0.1" + } + }, + "node_modules/eth-json-rpc-middleware/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/eth-json-rpc-middleware/node_modules/ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "dependencies": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/eth-json-rpc-middleware/node_modules/json-rpc-engine": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz", + "integrity": "sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g==", + "dependencies": { + "eth-rpc-errors": "^3.0.0", + "safe-event-emitter": "^1.0.1" + } + }, + "node_modules/eth-lib": { + "version": "0.1.29", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", + "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", + "dependencies": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + } + }, + "node_modules/eth-lib/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/eth-lib/node_modules/ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dependencies": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "node_modules/eth-net-props": { + "version": "1.0.41", + "resolved": "https://registry.npmjs.org/eth-net-props/-/eth-net-props-1.0.41.tgz", + "integrity": "sha512-4qUNJU8xyqV53Lr+5JMnCUoknL/IIQ8Zpk1CKV/8h7tvtdrqbvnvJNb3IC0nUcyf4f0tNRTQnSWslut7XRwpRA==", + "dependencies": { + "chai": "^4.2.0" + } + }, + "node_modules/eth-query": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/eth-query/-/eth-query-2.1.2.tgz", + "integrity": "sha1-1nQdkAAQa1FRDHLbktY2VFam2l4=", + "dependencies": { + "json-rpc-random-id": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "node_modules/eth-rpc-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz", + "integrity": "sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg==", + "dependencies": { + "fast-safe-stringify": "^2.0.6" + } + }, + "node_modules/eth-sig-util": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz", + "integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=", + "deprecated": "Deprecated in favor of '@metamask/eth-sig-util'", + "dependencies": { + "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git", + "ethereumjs-util": "^5.1.1" + } + }, + "node_modules/eth-sig-util/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/eth-sig-util/node_modules/ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "dependencies": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ethereum-bloom-filters": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", + "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "dependencies": { + "js-sha3": "^0.8.0" + } + }, + "node_modules/ethereum-common": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", + "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==" + }, + "node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethereumjs-abi": { + "version": "0.6.8", + "resolved": "git+ssh://git@github.com/ethereumjs/ethereumjs-abi.git#ee3994657fa7a427238e6ba92a84d0b529bbcde0", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + } + }, + "node_modules/ethereumjs-abi/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/ethereumjs-abi/node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/ethereumjs-account": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz", + "integrity": "sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA==", + "dependencies": { + "ethereumjs-util": "^5.0.0", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ethereumjs-account/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/ethereumjs-account/node_modules/ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "dependencies": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ethereumjs-block": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz", + "integrity": "sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg==", + "deprecated": "New package name format for new versions: @ethereumjs/block. Please update.", + "dependencies": { + "async": "^2.0.1", + "ethereum-common": "0.2.0", + "ethereumjs-tx": "^1.2.2", + "ethereumjs-util": "^5.0.0", + "merkle-patricia-tree": "^2.1.2" + } + }, + "node_modules/ethereumjs-block/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/ethereumjs-block/node_modules/ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "dependencies": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ethereumjs-common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz", + "integrity": "sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA==", + "deprecated": "New package name format for new versions: @ethereumjs/common. Please update." + }, + "node_modules/ethereumjs-tx": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz", + "integrity": "sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA==", + "deprecated": "New package name format for new versions: @ethereumjs/tx. Please update.", + "dependencies": { + "ethereum-common": "^0.0.18", + "ethereumjs-util": "^5.0.0" + } + }, + "node_modules/ethereumjs-tx/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/ethereumjs-tx/node_modules/ethereum-common": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", + "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=" + }, + "node_modules/ethereumjs-tx/node_modules/ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "dependencies": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ethereumjs-util/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ethereumjs-vm": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz", + "integrity": "sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw==", + "deprecated": "New package name format for new versions: @ethereumjs/vm. Please update.", + "dependencies": { + "async": "^2.1.2", + "async-eventemitter": "^0.2.2", + "ethereumjs-account": "^2.0.3", + "ethereumjs-block": "~2.2.0", + "ethereumjs-common": "^1.1.0", + "ethereumjs-util": "^6.0.0", + "fake-merkle-patricia-tree": "^1.0.1", + "functional-red-black-tree": "^1.0.1", + "merkle-patricia-tree": "^2.3.2", + "rustbn.js": "~0.2.0", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ethereumjs-vm/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/ethereumjs-vm/node_modules/ethereumjs-block": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz", + "integrity": "sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg==", + "deprecated": "New package name format for new versions: @ethereumjs/block. Please update.", + "dependencies": { + "async": "^2.0.1", + "ethereumjs-common": "^1.5.0", + "ethereumjs-tx": "^2.1.1", + "ethereumjs-util": "^5.0.0", + "merkle-patricia-tree": "^2.1.2" + } + }, + "node_modules/ethereumjs-vm/node_modules/ethereumjs-block/node_modules/ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "dependencies": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ethereumjs-vm/node_modules/ethereumjs-tx": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz", + "integrity": "sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw==", + "deprecated": "New package name format for new versions: @ethereumjs/tx. Please update.", + "dependencies": { + "ethereumjs-common": "^1.5.0", + "ethereumjs-util": "^6.0.0" + } + }, + "node_modules/ethereumjs-vm/node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", + "dependencies": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/ethjs-unit/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" + }, + "node_modules/ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dependencies": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/express/node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fake-merkle-patricia-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz", + "integrity": "sha1-S4w6z7Ugr635hgsfFM2M40As3dM=", + "dependencies": { + "checkpoint-store": "^1.1.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-cache-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-cache-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + }, + "node_modules/foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", + "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "globule": "^1.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globule": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz", + "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/globule/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "dependencies": { + "delegate": "^3.1.2" + } + }, + "node_modules/got": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", + "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "@szmarczak/http-timer": "^5.0.1", + "@types/cacheable-request": "^6.0.2", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^6.0.4", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "form-data-encoder": "1.7.1", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/highlight.js": { + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.6.0.tgz", + "integrity": "sha512-ig1eqDzJaB0pqEvlPVIpSSyMaO92bH1N2rJpLMN/nX396wTpDA4Eq0uK+7I/2XG17pFaaKE0kjV/XPeGt7Evjw==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg==" + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/http2-wrapper": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.11.tgz", + "integrity": "sha512-aNAk5JzLturWEUiuhAN73Jcbq96R7rTitAoXV54FYMatvihnpD2+6PUgU4ce3D/m5VDbw+F5CsyKSF176ptitQ==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/http2-wrapper/node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/humps": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz", + "integrity": "sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao=" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "dependencies": { + "punycode": "2.1.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/idna-uts46-hx/node_modules/punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" + }, + "node_modules/immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", + "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fn/-/is-fn-1.0.0.tgz", + "integrity": "sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", + "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "node_modules/is-weakref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", + "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "dependencies": { + "call-bind": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz", + "integrity": "sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==", + "dev": true, + "dependencies": { + "@jest/core": "^29.3.1", + "@jest/types": "^29.3.1", + "import-local": "^3.0.2", + "jest-cli": "^29.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.2.0.tgz", + "integrity": "sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.3.1.tgz", + "integrity": "sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "p-limit": "^3.1.0", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-circus/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-circus/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.3.1.tgz", + "integrity": "sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==", + "dev": true, + "dependencies": { + "@jest/core": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.3.1.tgz", + "integrity": "sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.3.1", + "@jest/types": "^29.3.1", + "babel-jest": "^29.3.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.3.1", + "jest-environment-node": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-config/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz", + "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.3.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-docblock": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.2.0.tgz", + "integrity": "sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.3.1.tgz", + "integrity": "sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "chalk": "^4.0.0", + "jest-get-type": "^29.2.0", + "jest-util": "^29.3.1", + "pretty-format": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-each/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "29.3.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.3.0.tgz", + "integrity": "sha512-xFLbMR4OF4lntNcO9LthJdPRbI9WgfFlG73aQS6wQ54+v4oSAp8T4FKUw0add+Z+Ghu/dirRxuvc4FzzN5kRxw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.3.0", + "@jest/fake-timers": "^29.3.0", + "@jest/types": "^29.2.1", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.3.0", + "jest-util": "^29.2.1", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.3.1.tgz", + "integrity": "sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", + "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.3.1.tgz", + "integrity": "sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.2.0", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-haste-map/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-haste-map/node_modules/jest-worker": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.3.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz", + "integrity": "sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz", + "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.3.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-matcher-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz", + "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.3.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-message-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.3.1.tgz", + "integrity": "sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-util": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.2.0.tgz", + "integrity": "sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.3.1.tgz", + "integrity": "sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz", + "integrity": "sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.2.0", + "jest-snapshot": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-resolve/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.3.1.tgz", + "integrity": "sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.3.1", + "@jest/environment": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.2.0", + "jest-environment-node": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-leak-detector": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-resolve": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-util": "^29.3.1", + "jest-watcher": "^29.3.1", + "jest-worker": "^29.3.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-runner/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/jest-worker": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.3.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runner/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.3.1.tgz", + "integrity": "sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/globals": "^29.3.1", + "@jest/source-map": "^29.2.0", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-runtime/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.3.1.tgz", + "integrity": "sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.3.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-haste-map": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "natural-compare": "^1.4.0", + "pretty-format": "^29.3.1", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-snapshot/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", + "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.3.1.tgz", + "integrity": "sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==", + "dev": true, + "dependencies": { + "@jest/types": "^29.3.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.2.0", + "leven": "^3.1.0", + "pretty-format": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-validate/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.3.1.tgz", + "integrity": "sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.3.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-watcher/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-watcher/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jquery": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", + "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==" + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/js-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", + "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/js-sdsl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz", + "integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==", + "dev": true + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "node_modules/jsdom": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.0.tgz", + "integrity": "sha512-x4a6CKCgx00uCmP+QakBDFXwjAJ69IkkIWHmtmjd3wvXPcdOS44hfX2vqkOQrVrq8l9DhNNADZRXaCEWvgXtVA==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.7.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.3.1", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "^7.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^3.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.8.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/jsdom/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsdom/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-rpc-engine": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-6.1.0.tgz", + "integrity": "sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ==", + "dependencies": { + "@metamask/safe-event-emitter": "^2.0.0", + "eth-rpc-errors": "^4.0.2" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/json-rpc-engine/node_modules/eth-rpc-errors": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eth-rpc-errors/-/eth-rpc-errors-4.0.3.tgz", + "integrity": "sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg==", + "dependencies": { + "fast-safe-stringify": "^2.0.6" + } + }, + "node_modules/json-rpc-random-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz", + "integrity": "sha1-uknZat7RRE27jaPSA3SKy7zeyMg=" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dependencies": { + "jsonify": "~0.0.0" + } + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "engines": { + "node": "*" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/keyv": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.0.tgz", + "integrity": "sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/keyvaluestorage-interface": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", + "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/level-codec": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-7.0.1.tgz", + "integrity": "sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ==" + }, + "node_modules/level-errors": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-1.0.5.tgz", + "integrity": "sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig==", + "dependencies": { + "errno": "~0.1.1" + } + }, + "node_modules/level-iterator-stream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz", + "integrity": "sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0=", + "dependencies": { + "inherits": "^2.0.1", + "level-errors": "^1.0.3", + "readable-stream": "^1.0.33", + "xtend": "^4.0.0" + } + }, + "node_modules/level-iterator-stream/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/level-iterator-stream/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/level-iterator-stream/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "node_modules/level-ws": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/level-ws/-/level-ws-0.0.0.tgz", + "integrity": "sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos=", + "dependencies": { + "readable-stream": "~1.0.15", + "xtend": "~2.1.1" + } + }, + "node_modules/level-ws/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/level-ws/node_modules/object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, + "node_modules/level-ws/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/level-ws/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "node_modules/level-ws/node_modules/xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "dependencies": { + "object-keys": "~0.4.0" + }, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/levelup": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz", + "integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==", + "dependencies": { + "deferred-leveldown": "~1.2.1", + "level-codec": "~7.0.0", + "level-errors": "~1.0.3", + "level-iterator-stream": "~1.3.0", + "prr": "~1.0.1", + "semver": "~5.4.1", + "xtend": "~4.0.0" + } + }, + "node_modules/levelup/node_modules/semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", + "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, + "node_modules/lodash.differenceby": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/lodash.differenceby/-/lodash.differenceby-4.8.0.tgz", + "integrity": "sha1-z9WelDU69d5R2l0wLKTr/zP6rFc=" + }, + "node_modules/lodash.find": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", + "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=" + }, + "node_modules/lodash.first": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash.first/-/lodash.first-3.0.0.tgz", + "integrity": "sha1-Xa4YDX+BjuZfxbIQsQSnu++YoWo=" + }, + "node_modules/lodash.forin": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.forin/-/lodash.forin-4.4.0.tgz", + "integrity": "sha1-XT8grlZAEfvog4H32YlJyclRlzE=" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "node_modules/lodash.intersectionby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.intersectionby/-/lodash.intersectionby-4.7.0.tgz", + "integrity": "sha1-EvEl5NoAsiKQ/r2htsG2i7klUSU=" + }, + "node_modules/lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=" + }, + "node_modules/lodash.keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", + "integrity": "sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU=" + }, + "node_modules/lodash.last": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash.last/-/lodash.last-3.0.0.tgz", + "integrity": "sha1-JC9mMRLdTG5jcoxgo8kJ0b2tvUw=" + }, + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" + }, + "node_modules/lodash.max": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.max/-/lodash.max-4.0.1.tgz", + "integrity": "sha1-hzVWbGGLNan3YFILSHrnllivE2o=" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.min": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.min/-/lodash.min-4.0.1.tgz", + "integrity": "sha1-SsG5qLr4ttKKaQ1xZRJRDPwUcIw=" + }, + "node_modules/lodash.noop": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-3.0.1.tgz", + "integrity": "sha1-OBiPTWUKOkdCWEObluxFsyYXEzw=" + }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=" + }, + "node_modules/lodash.rangeright": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.rangeright/-/lodash.rangeright-4.2.0.tgz", + "integrity": "sha1-dCrF5C+R9oKiwLaHwpt52TIzkEI=" + }, + "node_modules/lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" + }, + "node_modules/luxon": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.1.0.tgz", + "integrity": "sha512-7w6hmKC0/aoWnEsmPCu5Br54BmbmUp5GfcqBxQngRcXJ+q5fdfjEzn7dxmJh2YdDhgW8PccYtlWKSv4tQkrTQg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memdown": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", + "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", + "dependencies": { + "abstract-leveldown": "~2.7.1", + "functional-red-black-tree": "^1.0.1", + "immediate": "^3.2.3", + "inherits": "~2.0.1", + "ltgt": "~2.2.0", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/memdown/node_modules/abstract-leveldown": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", + "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "dependencies": { + "xtend": "~4.0.0" + } + }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/merkle-patricia-tree": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz", + "integrity": "sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g==", + "dependencies": { + "async": "^1.4.2", + "ethereumjs-util": "^5.0.0", + "level-ws": "0.0.0", + "levelup": "^1.2.1", + "memdown": "^1.0.0", + "readable-stream": "^2.0.0", + "rlp": "^2.0.0", + "semaphore": ">=1.0.1" + } + }, + "node_modules/merkle-patricia-tree/node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "node_modules/merkle-patricia-tree/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/merkle-patricia-tree/node_modules/ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "dependencies": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/merkle-patricia-tree/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/merkle-patricia-tree/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w==", + "deprecated": "This package is broken and no longer maintained. 'mkdirp' itself supports promises now, please switch to that.", + "dependencies": { + "mkdirp": "*" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mock-fs": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", + "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/multibase": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", + "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "node_modules/multicodec": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", + "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "varint": "^5.0.0" + } + }, + "node_modules/multihashes": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", + "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", + "dependencies": { + "buffer": "^5.5.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + } + }, + "node_modules/multihashes/node_modules/multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew==" + }, + "node_modules/nanoassert": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz", + "integrity": "sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40=" + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomorph": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/nanomorph/-/nanomorph-5.4.3.tgz", + "integrity": "sha512-uPP5y0x21KISffZCKHh1A0QW0RHZFQS0BR7LetlHBlay6UWAbjwhjiJTxOO6JeMHko5Cigl617zFoGrYFJ8ZLg==", + "dependencies": { + "nanoassert": "^1.1.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/gauge": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz", + "integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/node-gyp/node_modules/npmlog": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz", + "integrity": "sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "node_modules/node-sass": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-7.0.3.tgz", + "integrity": "sha512-8MIlsY/4dXUkJDYht9pIWBhMil3uHmE8b/AdJPjmFn1nBx9X9BASzfzmsCy0uCCb8eqI3SYYzVPDswWqSx7gjw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "peer": true, + "dependencies": { + "async-foreach": "^0.1.3", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "lodash": "^4.17.15", + "meow": "^9.0.0", + "nan": "^2.13.2", + "node-gyp": "^8.4.1", + "npmlog": "^5.0.0", + "request": "^2.88.0", + "sass-graph": "^4.0.1", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "bin": { + "node-sass": "bin/node-sass" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/node-sass/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/node-sass/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/node-sass/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/node-sass/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/node-sass/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-sass/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true, + "peer": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "dependencies": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/number-to-bn/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" + }, + "node_modules/numeral": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", + "integrity": "sha1-StCAk21EPCVhrtnyGX7//iX05QY=", + "engines": { + "node": "*" + } + }, + "node_modules/nwsapi": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz", + "integrity": "sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==", + "dev": true + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/oboe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", + "integrity": "sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA==", + "dependencies": { + "http-https": "^1.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-headers": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.4.tgz", + "integrity": "sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw==" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz", + "integrity": "sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==", + "dev": true, + "dependencies": { + "entities": "^4.3.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz", + "integrity": "sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/path-parser/-/path-parser-6.1.0.tgz", + "integrity": "sha512-nAB6J73z2rFcQP+870OHhpkHFj5kO4rPLc2Ol4Y3Ale7F6Hk1/cPKp7cQ8RznKF8FOSvu+YR9Xc6Gafk7DlpYA==", + "dependencies": { + "search-params": "3.0.0", + "tslib": "^1.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "engines": { + "node": "*" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "node_modules/phoenix": { + "resolved": "../../../deps/phoenix", + "link": true + }, + "node_modules/phoenix_html": { + "resolved": "../../../deps/phoenix_html", + "link": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "engines": { + "node": ">=4" + } + }, + "node_modules/pikaday": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/pikaday/-/pikaday-1.8.2.tgz", + "integrity": "sha512-TNtsE+34BIax3WtkB/qqu5uepV1McKYEgvL3kWzU7aqPCpMEN6rBF3AOwu4WCwAealWlBGobXny/9kJb49C1ew==" + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/postcss": { + "version": "8.4.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz", + "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz", + "integrity": "sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==", + "dev": true, + "dependencies": { + "browserslist": "^4.20.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-loader": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", + "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.7" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-loader/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz", + "integrity": "sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz", + "integrity": "sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dev": true, + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz", + "integrity": "sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dev": true, + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dev": true, + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/preact": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.4.1.tgz", + "integrity": "sha512-WKrRpCSwL2t3tpOOGhf2WfTpcmbpxaWtDbdJdKdjd0aEiTkvOmS4NBkG6kzlaAHI9AkQ3iVqbFWM3Ei7mZ4o1Q==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz", + "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-to-callback": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/promise-to-callback/-/promise-to-callback-1.0.0.tgz", + "integrity": "sha1-XSp0kBC/tn2WNZj805YHRqaP7vc=", + "dependencies": { + "is-fn": "^1.0.0", + "set-immediate-shim": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz", + "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==", + "dependencies": { + "buffer": "^5.4.3", + "buffer-alloc": "^1.2.0", + "buffer-from": "^1.1.1", + "dijkstrajs": "^1.0.1", + "isarray": "^2.0.1", + "pngjs": "^3.3.0", + "yargs": "^13.2.4" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/qrcode/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/qrcode/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "node_modules/qrcode/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/qrcode/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/qrcode/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "dependencies": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + }, + "peerDependencies": { + "react": "^16.14.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reduce-reducers": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reduce-reducers/-/reduce-reducers-1.0.4.tgz", + "integrity": "sha512-Mb2WZ2bJF597exiqX7owBzrqJ74DHLK3yOQjCyPAaNifRncE8OD0wFIuoMhXxTnHK07+8zZ2SJEKy/qtiyR7vw==" + }, + "node_modules/redux": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", + "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/responselike/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rustbn.js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", + "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safe-event-emitter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz", + "integrity": "sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg==", + "deprecated": "Renamed to @metamask/safe-event-emitter", + "dependencies": { + "events": "^3.0.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sass": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.56.0.tgz", + "integrity": "sha512-WFJ9XrpkcnqZcYuLRJh5qiV6ibQOR4AezleeEjTjMsCocYW59dEG19U3fwTTXxzi2Ed3yjPBp727hbbj53pHFw==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sass-graph": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-4.0.1.tgz", + "integrity": "sha512-5YCfmGBmxoIRYHnKK2AKzrAkCoQ8ozO+iumT8K4tXJXRVCPf+7s1/9KxTSW3Rbvf+7Y7b4FR3mWyLnQr3PHocA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "glob": "^7.0.0", + "lodash": "^4.17.11", + "scss-tokenizer": "^0.4.3", + "yargs": "^17.2.1" + }, + "bin": { + "sassgraph": "bin/sassgraph" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/sass-loader": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.1.0.tgz", + "integrity": "sha512-tZS1RJQ2n2+QNyf3CCAo1H562WjL/5AM6Gi8YcPVVoNxQX8d19mx8E+8fRrMWsyc93ZL6Q8vZDSM0FHVTJaVnQ==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "node_modules/scss-tokenizer": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.4.3.tgz", + "integrity": "sha512-raKLgf1LI5QMQnG+RxHz6oK0sL3x3I4FN2UDLqgLOGO8hodECNnNh5BXn7fAyBxrA8zVzdQizQ6XjNJQ+uBwMw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "js-base64": "^2.4.9", + "source-map": "^0.7.3" + } + }, + "node_modules/scss-tokenizer/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/search-params": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/search-params/-/search-params-3.0.0.tgz", + "integrity": "sha512-8CYNl/bjkEhXWbDTU/K7c2jQtrnqEffIPyOLMqygW/7/b+ym8UtQumcAZjOfMLjZKR6AxK5tOr9fChbQZCzPqg==" + }, + "node_modules/secp256k1": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", + "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.2", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + }, + "node_modules/semaphore": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", + "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "dependencies": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "node_modules/set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", + "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", + "dependencies": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-get/node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", + "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", + "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.1", + "socks": "^2.6.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz", + "integrity": "sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/stdout-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stdout-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/styled-components": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz", + "integrity": "sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw==", + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^0.8.8", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0", + "react-is": ">= 16.8.0" + } + }, + "node_modules/stylehacks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/swarm-js": { + "version": "0.1.42", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.42.tgz", + "integrity": "sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ==", + "dependencies": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^11.8.5", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request": "^1.0.1" + } + }, + "node_modules/swarm-js/node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/swarm-js/node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/swarm-js/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/swarm-js/node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dependencies": { + "minipass": "^2.6.0" + } + }, + "node_modules/swarm-js/node_modules/got": { + "version": "11.8.5", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", + "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/swarm-js/node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/swarm-js/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/swarm-js/node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/swarm-js/node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dependencies": { + "minipass": "^2.9.0" + } + }, + "node_modules/swarm-js/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/swarm-js/node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/swarm-js/node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/swarm-js/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/swarm-js/node_modules/tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "dependencies": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "engines": { + "node": ">=4.5" + } + }, + "node_modules/swarm-js/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/sweetalert2": { + "version": "11.6.7", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.6.7.tgz", + "integrity": "sha512-nbsmUtfTodJvZ873sn61U0brrHtn8xi8wfn+bVVc9IV05fgOdDMhAhdQ2zQJBYsvggbbp46ZFHMr0/fZJnZPtQ==", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/limonte" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz", + "integrity": "sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==", + "dev": true, + "dependencies": { + "jest-worker": "^27.0.6", + "p-limit": "^3.1.0", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "glob": "^7.1.2" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==" + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "node_modules/utf-8-validate": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", + "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", + "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/web3": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.8.0.tgz", + "integrity": "sha512-sldr9stK/SALSJTgI/8qpnDuBJNMGjVR84hJ+AcdQ+MLBGLMGsCDNubCoyO6qgk1/Y9SQ7ignegOI/7BPLoiDA==", + "hasInstallScript": true, + "dependencies": { + "web3-bzz": "1.8.0", + "web3-core": "1.8.0", + "web3-eth": "1.8.0", + "web3-eth-personal": "1.8.0", + "web3-net": "1.8.0", + "web3-shh": "1.8.0", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-bzz": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.8.0.tgz", + "integrity": "sha512-caDtdKeLi7+2Vb+y+cq2yyhkNjnxkFzVW0j1DtemarBg3dycG1iEl75CVQMLNO6Wkg+HH9tZtRnUyFIe5LIUeQ==", + "hasInstallScript": true, + "dependencies": { + "@types/node": "^12.12.6", + "got": "12.1.0", + "swarm-js": "^0.1.40" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-bzz/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + }, + "node_modules/web3-core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.8.0.tgz", + "integrity": "sha512-9sCA+Z02ci6zoY2bAquFiDjujRwmSKHiSGi4B8IstML8okSytnzXk1izHYSynE7ahIkguhjWAuXFvX76F5rAbA==", + "dependencies": { + "@types/bn.js": "^5.1.0", + "@types/node": "^12.12.6", + "bignumber.js": "^9.0.0", + "web3-core-helpers": "1.8.0", + "web3-core-method": "1.8.0", + "web3-core-requestmanager": "1.8.0", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-helpers": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.8.0.tgz", + "integrity": "sha512-nMAVwZB3rEp/khHI2BvFy0e/xCryf501p5NGjswmJtEM+Zrd3Biaw52JrB1qAZZIzCA8cmLKaOgdfamoDOpWdw==", + "dependencies": { + "web3-eth-iban": "1.8.0", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-method": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.8.0.tgz", + "integrity": "sha512-c94RAzo3gpXwf2rf8rL8C77jOzNWF4mXUoUfZYYsiY35cJFd46jQDPI00CB5+ZbICTiA5mlVzMj4e7jAsTqiLA==", + "dependencies": { + "@ethersproject/transactions": "^5.6.2", + "web3-core-helpers": "1.8.0", + "web3-core-promievent": "1.8.0", + "web3-core-subscriptions": "1.8.0", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-promievent": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.8.0.tgz", + "integrity": "sha512-FGLyjAuOaAQ+ZhV6iuw9tg/9WvIkSZXKHQ4mdTyQ8MxVraOtFivOCbuLLsGgapfHYX+RPxsc1j1YzQjKoupagQ==", + "dependencies": { + "eventemitter3": "4.0.4" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-requestmanager": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.8.0.tgz", + "integrity": "sha512-2AoYCs3Owl5foWcf4uKPONyqFygSl9T54L8b581U16nsUirjhoTUGK/PBhMDVcLCmW4QQmcY5A8oPFpkQc1TTg==", + "dependencies": { + "util": "^0.12.0", + "web3-core-helpers": "1.8.0", + "web3-providers-http": "1.8.0", + "web3-providers-ipc": "1.8.0", + "web3-providers-ws": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-subscriptions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.8.0.tgz", + "integrity": "sha512-7lHVRzDdg0+Gcog55lG6Q3D8JV+jN+4Ly6F8cSn9xFUAwOkdbgdWsjknQG7t7CDWy21DQkvdiY2BJF8S68AqOA==", + "dependencies": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/web3-core/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + }, + "node_modules/web3-eth": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.8.0.tgz", + "integrity": "sha512-hist52os3OT4TQFB/GxPSMxTh3995sz6LPvQpPvj7ktSbpg9RNSFaSsPlCT63wUAHA3PZb1FemkAIeQM5t72Lw==", + "dependencies": { + "web3-core": "1.8.0", + "web3-core-helpers": "1.8.0", + "web3-core-method": "1.8.0", + "web3-core-subscriptions": "1.8.0", + "web3-eth-abi": "1.8.0", + "web3-eth-accounts": "1.8.0", + "web3-eth-contract": "1.8.0", + "web3-eth-ens": "1.8.0", + "web3-eth-iban": "1.8.0", + "web3-eth-personal": "1.8.0", + "web3-net": "1.8.0", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-abi": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.8.0.tgz", + "integrity": "sha512-xPeMb2hS9YLQK/Q5YZpkcmzoRGM+/R8bogSrYHhNC3hjZSSU0YRH+1ZKK0f9YF4qDZaPMI8tKWIMSCDIpjG6fg==", + "dependencies": { + "@ethersproject/abi": "^5.6.3", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-accounts": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.8.0.tgz", + "integrity": "sha512-HQ/MDSv4bexwJLvnqsM6xpGE7c2NVOqyhzOZFyMUKXbIwIq85T3TaLnM9pCN7XqMpDcfxqiZ3q43JqQVkzHdmw==", + "dependencies": { + "@ethereumjs/common": "^2.5.0", + "@ethereumjs/tx": "^3.3.2", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.8", + "ethereumjs-util": "^7.0.10", + "scrypt-js": "^3.0.1", + "uuid": "3.3.2", + "web3-core": "1.8.0", + "web3-core-helpers": "1.8.0", + "web3-core-method": "1.8.0", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-accounts/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/web3-eth-accounts/node_modules/eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "dependencies": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "node_modules/web3-eth-accounts/node_modules/uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/web3-eth-contract": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.8.0.tgz", + "integrity": "sha512-6xeXhW2YoCrz2Ayf2Vm4srWiMOB6LawkvxWJDnUWJ8SMATg4Pgu42C/j8rz/enXbYWt2IKuj0kk8+QszxQbK+Q==", + "dependencies": { + "@types/bn.js": "^5.1.0", + "web3-core": "1.8.0", + "web3-core-helpers": "1.8.0", + "web3-core-method": "1.8.0", + "web3-core-promievent": "1.8.0", + "web3-core-subscriptions": "1.8.0", + "web3-eth-abi": "1.8.0", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-contract/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/web3-eth-ens": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.8.0.tgz", + "integrity": "sha512-/eFbQEwvsMOEiOhw9/iuRXCsPkqAmHHWuFOrThQkozRgcnSTRnvxkkRC/b6koiT5/HaKeUs4yQDg+/ixsIxZxA==", + "dependencies": { + "content-hash": "^2.5.2", + "eth-ens-namehash": "2.0.8", + "web3-core": "1.8.0", + "web3-core-helpers": "1.8.0", + "web3-core-promievent": "1.8.0", + "web3-eth-abi": "1.8.0", + "web3-eth-contract": "1.8.0", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-iban": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.8.0.tgz", + "integrity": "sha512-4RbvUxcMpo/e5811sE3a6inJ2H4+FFqUVmlRYs0RaXaxiHweahSRBNcpO0UWgmlePTolj0rXqPT2oEr0DuC8kg==", + "dependencies": { + "bn.js": "^5.2.1", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-personal": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.8.0.tgz", + "integrity": "sha512-L7FT4nR3HmsfZyIAhFpEctKkYGOjRC2h6iFKs9gnFCHZga8yLcYcGaYOBIoYtaKom99MuGBoosayWt/Twh7F5A==", + "dependencies": { + "@types/node": "^12.12.6", + "web3-core": "1.8.0", + "web3-core-helpers": "1.8.0", + "web3-core-method": "1.8.0", + "web3-net": "1.8.0", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-personal/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + }, + "node_modules/web3-net": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.8.0.tgz", + "integrity": "sha512-kX6EAacK7QrOe7DOh0t5yHS5q2kxZmTCxPVwSz9io9xBeE4n4UhmzGJ/VfhP2eM3OPKYeypcR3LEO6zZ8xn2vw==", + "dependencies": { + "web3-core": "1.8.0", + "web3-core-method": "1.8.0", + "web3-utils": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-provider-engine": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/web3-provider-engine/-/web3-provider-engine-16.0.1.tgz", + "integrity": "sha512-/Eglt2aocXMBiDj7Se/lyZnNDaHBaoJlaUfbP5HkLJQC/HlGbR+3/W+dINirlJDhh7b54DzgykqY7ksaU5QgTg==", + "dependencies": { + "async": "^2.5.0", + "backoff": "^2.5.0", + "clone": "^2.0.0", + "cross-fetch": "^2.1.0", + "eth-block-tracker": "^4.4.2", + "eth-json-rpc-filters": "^4.2.1", + "eth-json-rpc-infura": "^5.1.0", + "eth-json-rpc-middleware": "^6.0.0", + "eth-rpc-errors": "^3.0.0", + "eth-sig-util": "^1.4.2", + "ethereumjs-block": "^1.2.2", + "ethereumjs-tx": "^1.2.0", + "ethereumjs-util": "^5.1.5", + "ethereumjs-vm": "^2.3.4", + "json-stable-stringify": "^1.0.1", + "promise-to-callback": "^1.0.0", + "readable-stream": "^2.2.9", + "request": "^2.85.0", + "semaphore": "^1.0.3", + "ws": "^5.1.1", + "xhr": "^2.2.0", + "xtend": "^4.0.1" + } + }, + "node_modules/web3-provider-engine/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/web3-provider-engine/node_modules/ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "dependencies": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/web3-provider-engine/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/web3-provider-engine/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/web3-provider-engine/node_modules/ws": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", + "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/web3-providers-http": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.8.0.tgz", + "integrity": "sha512-/MqxwRzExohBWW97mqlCSW/+NHydGRyoEDUS1bAIF2YjfKFwyRtHgrEzOojzkC9JvB+8LofMvbXk9CcltpZapw==", + "dependencies": { + "abortcontroller-polyfill": "^1.7.3", + "cross-fetch": "^3.1.4", + "es6-promise": "^4.2.8", + "web3-core-helpers": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-providers-http/node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dependencies": { + "node-fetch": "2.6.7" + } + }, + "node_modules/web3-providers-ipc": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.8.0.tgz", + "integrity": "sha512-tAXHtVXNUOgehaBU8pzAlB3qhjn/PRpjdzEjzHNFqtRRTwzSEKOJxFeEhaUA4FzHnTlbnrs8ujHWUitcp1elfg==", + "dependencies": { + "oboe": "2.1.5", + "web3-core-helpers": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-providers-ws": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.8.0.tgz", + "integrity": "sha512-bcZtSifsqyJxwkfQYamfdIRp4nhj9eJd7cxHg1uUkfLJK125WP96wyJL1xbPt7qt0MpfnTFn8/UuIqIB6nFENg==", + "dependencies": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.8.0", + "websocket": "^1.0.32" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-shh": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.8.0.tgz", + "integrity": "sha512-DNRgSa9Jf9xYFUGKSMylrf+zt3MPjhI2qF+UWX07o0y3+uf8zalDGiJOWvIS4upAsdPiKKVJ7co+Neof47OMmg==", + "hasInstallScript": true, + "dependencies": { + "web3-core": "1.8.0", + "web3-core-method": "1.8.0", + "web3-core-subscriptions": "1.8.0", + "web3-net": "1.8.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.8.0.tgz", + "integrity": "sha512-7nUIl7UWpLVka2f09CMbKOSEvorvHnaugIabU4mj7zfMvm0tSByLcEu3eyV9qgS11qxxLuOkzBIwCstTflhmpQ==", + "dependencies": { + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereumjs-util": "^7.1.0", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3modal": { + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/web3modal/-/web3modal-1.9.9.tgz", + "integrity": "sha512-ML1C4xH+JTSHHkKbjxuF+f5B3cDUOCnrdQZ8Mlzippq7zRKHf3NBeuIvDdNjtVclJ2S4zYYVmVqRWrgB11ej8A==", + "dependencies": { + "detect-browser": "^5.1.0", + "prop-types": "^15.7.2", + "react": "^16.8.6", + "react-dom": "^16.8.6", + "styled-components": "^5.3.3", + "tslib": "^1.10.0" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "@webpack-cli/migrate": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack/node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack/node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/websocket/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/websocket/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-fetch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" + }, + "node_modules/which-typed-array": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", + "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "dependencies": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "dependencies": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "node_modules/xhr-request-promise": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", + "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", + "dependencies": { + "xhr-request": "^1.1.0" + } + }, + "node_modules/xhr2-cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", + "integrity": "sha512-hjXUA6q+jl/bd8ADHcVfFsSPIf+tyLIjuO9TwJC9WI6JP2zKcS7C+p56I9kCLLsaCiNT035iYvEUUzdEFj/8+g==", + "dependencies": { + "cookiejar": "^2.1.1" + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/xss": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz", + "integrity": "sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==", + "dependencies": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "bin": { + "xss": "bin/xss" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/xss/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "engines": { + "node": ">=0.10.32" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.1.tgz", + "integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==" + }, + "@babel/core": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz", + "integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==", + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.2", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.1", + "@babel/parser": "^7.20.2", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + } + }, + "@babel/generator": { + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.3.tgz", + "integrity": "sha512-Wl5ilw2UD1+ZYprHVprxHZJCFeBWlzZYOovE4SDYLZnqCOD11j+0QzNeEWKLLTWM7nixrZEh7vNIyb76MyJg3A==", + "requires": { + "@babel/types": "^7.20.2", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "requires": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", + "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "requires": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==" + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-replace-supers": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "requires": { + "@babel/types": "^7.20.2" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "dev": true, + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" + }, + "@babel/helper-wrap-function": { + "version": "7.18.11", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz", + "integrity": "sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.18.9", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.11", + "@babel/types": "^7.18.10" + } + }, + "@babel/helpers": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.1.tgz", + "integrity": "sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==", + "requires": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.0" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz", + "integrity": "sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==" + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz", + "integrity": "sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz", + "integrity": "sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.1" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.2.tgz", + "integrity": "sha512-y5V15+04ry69OV2wULmwhEA6jwSWXO1TwAtIwiPXcvHcoOQUqpyMVd2bDsQJMW8AurjulIyUV8kDqtjSwHy1uQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz", + "integrity": "sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.19.1", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz", + "integrity": "sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.19.1" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", + "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.20.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.3.tgz", + "integrity": "sha512-oZg/Fpx0YDrj13KsLyO8I/CX3Zdw7z0O9qOd95SqcoIzuqy/WTGWvePeHAnZCN54SfdyjHcb1S30gc8zlzlHcA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.7.tgz", + "integrity": "sha512-2FoHiSAWkdq4L06uaDN3rS43i6x28desUVxq+zAFuE6kbWYQeiLPJI5IC7Sg9xKYVcrBKSQkVUfH6aeQYbl9QA==", + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.4.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + }, + "dependencies": { + "babel-plugin-polyfill-corejs3": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.4.0.tgz", + "integrity": "sha512-YxFreYwUfglYKdLUGvIF2nJEsGwj+RhWSX/ije3D2vQPOXuyMLMtg/cCGMDpOA7Nd+MwlNdnGODbd2EwUZPlsw==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.0", + "core-js-compat": "^3.18.0" + } + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/preset-env": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.20.1", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", + "semver": "^6.3.0" + }, + "dependencies": { + "babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + } + } + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz", + "integrity": "sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz", + "integrity": "sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.1", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.1", + "@babel/types": "^7.20.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz", + "integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==", + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@discoveryjs/json-ext": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz", + "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==", + "dev": true + }, + "@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "requires": { + "@emotion/memoize": "0.7.4" + } + }, + "@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" + }, + "@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + }, + "@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "@eslint/eslintrc": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@ethereumjs/common": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.5.tgz", + "integrity": "sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA==", + "requires": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.5" + } + }, + "@ethereumjs/tx": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.2.tgz", + "integrity": "sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw==", + "requires": { + "@ethereumjs/common": "^2.6.4", + "ethereumjs-util": "^7.1.5" + } + }, + "@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "requires": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "requires": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "requires": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "requires": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "requires": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==" + }, + "@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "requires": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "requires": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@fortawesome/fontawesome-free": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.2.0.tgz", + "integrity": "sha512-CNR7qRIfCwWHNN7FnKUniva94edPdyQzil/zCwk3v6k4R6rR2Fr8i4s3PM7n/lyfPA6Zfko9z5WDzFxG9SW1uQ==" + }, + "@gar/promisify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", + "integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", + "dev": true, + "optional": true, + "peer": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.6.tgz", + "integrity": "sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.3.1.tgz", + "integrity": "sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==", + "dev": true, + "requires": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/core": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.3.1.tgz", + "integrity": "sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==", + "dev": true, + "requires": { + "@jest/console": "^29.3.1", + "@jest/reporters": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.2.0", + "jest-config": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-resolve-dependencies": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "jest-watcher": "^29.3.1", + "micromatch": "^4.0.4", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/environment": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.3.1.tgz", + "integrity": "sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-mock": "^29.3.1" + } + }, + "@jest/expect": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==", + "dev": true, + "requires": { + "expect": "^29.3.1", + "jest-snapshot": "^29.3.1" + } + }, + "@jest/expect-utils": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz", + "integrity": "sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==", + "dev": true, + "requires": { + "jest-get-type": "^29.2.0" + } + }, + "@jest/fake-timers": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.3.1.tgz", + "integrity": "sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==", + "dev": true, + "requires": { + "@jest/types": "^29.3.1", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" + } + }, + "@jest/globals": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.3.1.tgz", + "integrity": "sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==", + "dev": true, + "requires": { + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/types": "^29.3.1", + "jest-mock": "^29.3.1" + } + }, + "@jest/reporters": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.3.1.tgz", + "integrity": "sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-worker": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.3.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/schemas": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", + "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.24.1" + } + }, + "@jest/source-map": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.2.0.tgz", + "integrity": "sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.3.1.tgz", + "integrity": "sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==", + "dev": true, + "requires": { + "@jest/console": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz", + "integrity": "sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==", + "dev": true, + "requires": { + "@jest/test-result": "^29.3.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.3.1.tgz", + "integrity": "sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.3.1", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-util": "^29.3.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/types": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", + "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", + "dev": true, + "requires": { + "@jest/schemas": "^29.0.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@metamask/safe-event-emitter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz", + "integrity": "sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==" + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@npmcli/fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.0.0.tgz", + "integrity": "sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@sinclair/typebox": { + "version": "0.24.27", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.27.tgz", + "integrity": "sha512-K7C7IlQ3zLePEZleUN21ceBA2aLcMnLHTLph8QWk1JK37L90obdpY+QGY8bXMKxf1ht1Z0MNewvXxWv0oGDYFg==", + "dev": true + }, + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "requires": { + "defer-to-connect": "^2.0.1" + } + }, + "@tarekraafat/autocomplete.js": { + "version": "10.2.7", + "resolved": "https://registry.npmjs.org/@tarekraafat/autocomplete.js/-/autocomplete.js-10.2.7.tgz", + "integrity": "sha512-iE+dnXI8/LrTaSORrnNdSyXg/bFCbCpz/R5GUdB3ioW+9PVEhglxNcSDQNeCXtrbRG0kOBFUd4unEiwcmqyn8A==" + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "optional": true, + "peer": true + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true + }, + "@types/babel__core": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", + "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz", + "integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + }, + "@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/eslint": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", + "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jsdom": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.0.tgz", + "integrity": "sha512-YfAchFs0yM1QPDrLm2VHe+WHGtqms3NXnXAMolrgrVP6fgBHHXy1ozAbo/dFtPNtZC/m66bPiCTWYmqp1F14gA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "requires": { + "@types/node": "*" + } + }, + "@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true, + "optional": true, + "peer": true + }, + "@types/node": { + "version": "16.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz", + "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==" + }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true, + "optional": true, + "peer": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/pbkdf2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==", + "dev": true + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, + "@types/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "@types/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", + "dev": true + }, + "@types/yargs": { + "version": "17.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", + "integrity": "sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "@walletconnect/browser-utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/browser-utils/-/browser-utils-1.8.0.tgz", + "integrity": "sha512-Wcqqx+wjxIo9fv6eBUFHPsW1y/bGWWRboni5dfD8PtOmrihrEpOCmvRJe4rfl7xgJW8Ea9UqKEaq0bIRLHlK4A==", + "requires": { + "@walletconnect/safe-json": "1.0.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/window-getters": "1.0.0", + "@walletconnect/window-metadata": "1.0.0", + "detect-browser": "5.2.0" + }, + "dependencies": { + "detect-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.0.tgz", + "integrity": "sha512-tr7XntDAu50BVENgQfajMLzacmSe34D+qZc4zjnniz0ZVuw/TZcLcyxHQjYpJTM36sGEkZZlYLnIM1hH7alTMA==" + } + } + }, + "@walletconnect/client": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/client/-/client-1.8.0.tgz", + "integrity": "sha512-svyBQ14NHx6Cs2j4TpkQaBI/2AF4+LXz64FojTjMtV4VMMhl81jSO1vNeg+yYhQzvjcGH/GpSwixjyCW0xFBOQ==", + "requires": { + "@walletconnect/core": "^1.8.0", + "@walletconnect/iso-crypto": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "@walletconnect/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-1.8.0.tgz", + "integrity": "sha512-aFTHvEEbXcZ8XdWBw6rpQDte41Rxwnuk3SgTD8/iKGSRTni50gI9S3YEzMj05jozSiOBxQci4pJDMVhIUMtarw==", + "requires": { + "@walletconnect/socket-transport": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "@walletconnect/crypto": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/crypto/-/crypto-1.0.2.tgz", + "integrity": "sha512-+OlNtwieUqVcOpFTvLBvH+9J9pntEqH5evpINHfVxff1XIgwV55PpbdvkHu6r9Ib4WQDOFiD8OeeXs1vHw7xKQ==", + "requires": { + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/environment": "^1.0.0", + "@walletconnect/randombytes": "^1.0.2", + "aes-js": "^3.1.2", + "hash.js": "^1.1.7" + } + }, + "@walletconnect/encoding": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/encoding/-/encoding-1.0.1.tgz", + "integrity": "sha512-8opL2rs6N6E3tJfsqwS82aZQDL3gmupWUgmvuZ3CGU7z/InZs3R9jkzH8wmYtpbq0sFK3WkJkQRZFFk4BkrmFA==", + "requires": { + "is-typedarray": "1.0.0", + "typedarray-to-buffer": "3.1.5" + } + }, + "@walletconnect/environment": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/environment/-/environment-1.0.0.tgz", + "integrity": "sha512-4BwqyWy6KpSvkocSaV7WR3BlZfrxLbJSLkg+j7Gl6pTDE+U55lLhJvQaMuDVazXYxcjBsG09k7UlH7cGiUI5vQ==" + }, + "@walletconnect/http-connection": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/http-connection/-/http-connection-1.8.0.tgz", + "integrity": "sha512-IziEr3c53qsMromK7jz0EkbKDHlryRbxXdFR+xaG+S5nfxtUdAfjzlZabvczXdDCgmTij6KbNsZAjBMqCBzACw==", + "requires": { + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0", + "eventemitter3": "4.0.7", + "xhr2-cookies": "1.1.0" + }, + "dependencies": { + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + } + } + }, + "@walletconnect/iso-crypto": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/iso-crypto/-/iso-crypto-1.8.0.tgz", + "integrity": "sha512-pWy19KCyitpfXb70hA73r9FcvklS+FvO9QUIttp3c2mfW8frxgYeRXfxLRCIQTkaYueRKvdqPjbyhPLam508XQ==", + "requires": { + "@walletconnect/crypto": "^1.0.2", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "@walletconnect/jsonrpc-types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.1.tgz", + "integrity": "sha512-+6coTtOuChCqM+AoYyi4Q83p9l/laI6NvuM2/AHaZFuf0gT0NjW7IX2+86qGyizn7Ptq4AYZmfxurAxTnhefuw==", + "requires": { + "keyvaluestorage-interface": "^1.0.0" + } + }, + "@walletconnect/jsonrpc-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.3.tgz", + "integrity": "sha512-3yb49bPk16MNLk6uIIHPSHQCpD6UAo1OMOx1rM8cW/MPEAYAzrSW5hkhG7NEUwX9SokRIgnZK3QuQkiyNzBMhQ==", + "requires": { + "@walletconnect/environment": "^1.0.0", + "@walletconnect/jsonrpc-types": "^1.0.1" + } + }, + "@walletconnect/mobile-registry": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@walletconnect/mobile-registry/-/mobile-registry-1.4.0.tgz", + "integrity": "sha512-ZtKRio4uCZ1JUF7LIdecmZt7FOLnX72RPSY7aUVu7mj7CSfxDwUn6gBuK6WGtH+NZCldBqDl5DenI5fFSvkKYw==" + }, + "@walletconnect/qrcode-modal": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/qrcode-modal/-/qrcode-modal-1.8.0.tgz", + "integrity": "sha512-BueaFefaAi8mawE45eUtztg3ZFbsAH4DDXh1UNwdUlsvFMjqcYzLUG0xZvDd6z2eOpbgDg2N3bl6gF0KONj1dg==", + "requires": { + "@walletconnect/browser-utils": "^1.8.0", + "@walletconnect/mobile-registry": "^1.4.0", + "@walletconnect/types": "^1.8.0", + "copy-to-clipboard": "^3.3.1", + "preact": "10.4.1", + "qrcode": "1.4.4" + } + }, + "@walletconnect/randombytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/randombytes/-/randombytes-1.0.2.tgz", + "integrity": "sha512-ivgOtAyqQnN0rLQmOFPemsgYGysd/ooLfaDA/ACQ3cyqlca56t3rZc7pXfqJOIETx/wSyoF5XbwL+BqYodw27A==", + "requires": { + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/environment": "^1.0.0", + "randombytes": "^2.1.0" + } + }, + "@walletconnect/safe-json": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.0.tgz", + "integrity": "sha512-QJzp/S/86sUAgWY6eh5MKYmSfZaRpIlmCJdi5uG4DJlKkZrHEF7ye7gA+VtbVzvTtpM/gRwO2plQuiooIeXjfg==" + }, + "@walletconnect/socket-transport": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/socket-transport/-/socket-transport-1.8.0.tgz", + "integrity": "sha512-5DyIyWrzHXTcVp0Vd93zJ5XMW61iDM6bcWT4p8DTRfFsOtW46JquruMhxOLeCOieM4D73kcr3U7WtyR4JUsGuQ==", + "requires": { + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0", + "ws": "7.5.3" + }, + "dependencies": { + "ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "requires": {} + } + } + }, + "@walletconnect/types": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-1.8.0.tgz", + "integrity": "sha512-Cn+3I0V0vT9ghMuzh1KzZvCkiAxTq+1TR2eSqw5E5AVWfmCtECFkVZBP6uUJZ8YjwLqXheI+rnjqPy7sVM4Fyg==" + }, + "@walletconnect/utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-1.8.0.tgz", + "integrity": "sha512-zExzp8Mj1YiAIBfKNm5u622oNw44WOESzo6hj+Q3apSMIb0Jph9X3GDIdbZmvVZsNPxWDL7uodKgZcCInZv2vA==", + "requires": { + "@walletconnect/browser-utils": "^1.8.0", + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/jsonrpc-utils": "^1.0.3", + "@walletconnect/types": "^1.8.0", + "bn.js": "4.11.8", + "js-sha3": "0.8.0", + "query-string": "6.13.5" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "query-string": { + "version": "6.13.5", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.5.tgz", + "integrity": "sha512-svk3xg9qHR39P3JlHuD7g3nRnyay5mHbrPctEBDUxUkHRifPHXJDhBUycdCC0NBjXoDf44Gb+IsOZL1Uwn8M/Q==", + "requires": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==" + } + } + }, + "@walletconnect/web3-provider": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/web3-provider/-/web3-provider-1.8.0.tgz", + "integrity": "sha512-lqqEO0oRmCehH+c8ZPk3iH7I7YtbzmkWd58/Or2AgWAl869JamzndKCD3sTlNsPRQLxxPpraHQqzur7uclLWvg==", + "requires": { + "@walletconnect/client": "^1.8.0", + "@walletconnect/http-connection": "^1.8.0", + "@walletconnect/qrcode-modal": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0", + "web3-provider-engine": "16.0.1" + } + }, + "@walletconnect/window-getters": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.0.tgz", + "integrity": "sha512-xB0SQsLaleIYIkSsl43vm8EwETpBzJ2gnzk7e0wMF3ktqiTGS6TFHxcprMl5R44KKh4tCcHCJwolMCaDSwtAaA==" + }, + "@walletconnect/window-metadata": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.0.tgz", + "integrity": "sha512-9eFvmJxIKCC3YWOL97SgRkKhlyGXkrHwamfechmqszbypFspaSk+t2jQXAEU7YClHF6Qjw5eYOmy1//zFi9/GA==", + "requires": { + "@walletconnect/window-getters": "^1.0.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true, + "requires": {} + }, + "@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true, + "requires": {} + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "optional": true, + "peer": true + }, + "abortcontroller-polyfill": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz", + "integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==" + }, + "abstract-leveldown": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz", + "integrity": "sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA==", + "requires": { + "xtend": "~4.0.0" + } + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + } + } + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.0.tgz", + "integrity": "sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true, + "optional": true, + "peer": true + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, + "array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true, + "optional": true, + "peer": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "requires": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "requires": { + "lodash": "^4.17.14" + } + }, + "async-eventemitter": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz", + "integrity": "sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==", + "requires": { + "async": "^2.4.0" + } + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true, + "optional": true, + "peer": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "async-mutex": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.2.6.tgz", + "integrity": "sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==", + "requires": { + "tslib": "^2.0.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + } + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "autoprefixer": { + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "dev": true, + "requires": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "babel-jest": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz", + "integrity": "sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==", + "dev": true, + "requires": { + "@jest/transform": "^29.3.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.2.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "babel-loader": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.0.tgz", + "integrity": "sha512-Antt61KJPinUMwHwIIz9T5zfMgevnfZkEVWYDWlG888fgdvRRGD0JTuf/fFozQnfT+uq64sk1bmdHDy/mOEWnA==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.2", + "schema-utils": "^4.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz", + "integrity": "sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + } + }, + "babel-plugin-styled-components": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.13.3.tgz", + "integrity": "sha512-meGStRGv+VuKA/q0/jXxrPNWEm4LPfYIqxooDTdmh8kFsP/Ph7jJG5rUPwUPX3QHUvggwdbgdGpo88P/rRYsVw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.15.4", + "@babel/helper-module-imports": "^7.15.4", + "babel-plugin-syntax-jsx": "^6.18.0", + "lodash": "^4.17.11" + } + }, + "babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz", + "integrity": "sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^29.2.0", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", + "requires": { + "precond": "0.2" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "bignumber.js": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz", + "integrity": "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "blakejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", + "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "requires": { + "side-channel": "^1.0.4" + } + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "bootstrap": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz", + "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==", + "requires": {} + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==" + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "bufferutil": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", + "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "peer": true, + "requires": { + "semver": "^7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "cacheable-lookup": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", + "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==" + }, + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "camelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001426", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001426.tgz", + "integrity": "sha512-n7cosrHLl8AWt0wwZw/PJZgUg3lV0gk9LMI7ikGJwhyhgsd2Nb65vKvmSexCqq/J7rbH3mFG6yZZiPR5dLPW5A==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "chart.js": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz", + "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==" + }, + "chartjs-adapter-luxon": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/chartjs-adapter-luxon/-/chartjs-adapter-luxon-1.2.0.tgz", + "integrity": "sha512-h1lEns7+8cUN/Dmk24dhrT9hpAimKImQxzHpILqXn2kocdzj9b/fDlBa8v8/OMq5rq0uZEx/NV1WpByH4l2/Rw==", + "requires": {} + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" + }, + "checkpoint-store": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/checkpoint-store/-/checkpoint-store-1.1.0.tgz", + "integrity": "sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY=", + "requires": { + "functional-red-black-tree": "^1.0.1" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "optional": true, + "peer": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "ci-info": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", + "dev": true + }, + "cids": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", + "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", + "requires": { + "buffer": "^5.5.0", + "class-is": "^1.1.0", + "multibase": "~0.6.0", + "multicodec": "^1.0.0", + "multihashes": "~0.4.15" + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "class-is": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", + "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "optional": true, + "peer": true + }, + "clipboard": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", + "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==", + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "optional": true, + "peer": true + }, + "colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "dev": true + }, + "colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true, + "optional": true, + "peer": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "content-hash": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", + "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", + "requires": { + "cids": "^0.7.1", + "multicodec": "^0.5.5", + "multihashes": "^0.4.15" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" + }, + "copy-to-clipboard": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz", + "integrity": "sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, + "copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "requires": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + } + }, + "core-js": { + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.0.tgz", + "integrity": "sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw==" + }, + "core-js-compat": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.1.tgz", + "integrity": "sha512-pOHS7O0i8Qt4zlPW/eIFjwp+NrTPx+wTL0ctgI2fHn31sZOq89rDsmtc/A2vAX7r6shl+bmVI+678He46jgBlw==", + "requires": { + "browserslist": "^4.21.3" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==" + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-fetch": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.6.tgz", + "integrity": "sha512-9JZz+vXCmfKUZ68zAptS7k4Nu8e2qcibe7WVZYps7sAgk5R8GYTc+T1WR0v1rlP9HxgARmOX1UTIJZFytajpNA==", + "requires": { + "node-fetch": "^2.6.7", + "whatwg-fetch": "^2.0.4" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" + }, + "css-declaration-sorter": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", + "integrity": "sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==", + "dev": true, + "requires": {} + }, + "css-loader": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", + "integrity": "sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.5" + }, + "dependencies": { + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "css-minimizer-webpack-plugin": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", + "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "dev": true, + "requires": { + "cssnano": "^5.1.8", + "jest-worker": "^29.1.2", + "postcss": "^8.4.17", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-worker": { + "version": "29.1.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.1.2.tgz", + "integrity": "sha512-AdTZJxKjTSPHbXT/AIOjQVmoFx0LHFcVabWu0sxI7PAy7rFf8c0upyvgBKgguVXdM4vY74JdwkyD4hSmpTW8jA==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.1.2", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-to-react-native": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz", + "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==", + "requires": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssfilter": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", + "integrity": "sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=" + }, + "cssnano": { + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.12.tgz", + "integrity": "sha512-TgvArbEZu0lk/dvg2ja+B7kYoD7BBCmn3+k58xD0qjrGHsFzXY/wKTo9M5egcUCabPol05e/PVoIu79s2JN4WQ==", + "dev": true, + "requires": { + "cssnano-preset-default": "^5.2.12", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "cssnano-preset-default": { + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz", + "integrity": "sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew==", + "dev": true, + "requires": { + "css-declaration-sorter": "^6.3.0", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.2", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.6", + "postcss-merge-rules": "^5.1.2", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.3", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + } + }, + "cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "requires": {} + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "requires": { + "css-tree": "^1.1.2" + } + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + } + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "optional": true, + "peer": true + } + } + }, + "decimal.js": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.0.tgz", + "integrity": "sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, + "deferred-leveldown": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", + "integrity": "sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA==", + "requires": { + "abstract-leveldown": "~2.6.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true, + "optional": true, + "peer": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "optional": true, + "peer": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "detect-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.1.tgz", + "integrity": "sha512-eAcRiEPTs7utXWPaAgu/OX1HRJpxW7xSHpw4LTDrGFaeWnJ37HRlqpUkKsDm0AoTbtrvHQhH+5U2Cd87EGhJTg==" + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "diff-sequences": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz", + "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "dijkstrajs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.2.tgz", + "integrity": "sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==" + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dropzone": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.9.3.tgz", + "integrity": "sha512-Azk8kD/2/nJIuVPK+zQ9sjKMRIpRvNyqn9XwbBHNq+iNuSccbJS6hwm1Woy0pMST0erSo0u4j+KJaodndDk4vA==" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "electron-to-chromium": { + "version": "1.4.256", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz", + "integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw==" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "peer": true, + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "peer": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "optional": true, + "peer": true + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "optional": true, + "peer": true + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "eslint": { + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.27.0.tgz", + "integrity": "sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.15.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "globals": { + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-standard": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz", + "integrity": "sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==", + "dev": true, + "requires": {} + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-es": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", + "dev": true, + "peer": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-n": { + "version": "15.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.2.2.tgz", + "integrity": "sha512-MLjZVAv4TiCIoXqjibNqCJjLkGHfrOY3XZ0ZBLoW0OnS3o98PUBnzB/kfp8dCz/4A4Y18jjX50PRnqI4ACFY1Q==", + "dev": true, + "peer": true, + "requires": { + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", + "ignore": "^5.1.1", + "is-core-module": "^2.9.0", + "minimatch": "^3.1.2", + "resolve": "^1.10.1", + "semver": "^7.3.7" + }, + "dependencies": { + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "peer": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "peer": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + } + } + } + }, + "eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "eth-block-tracker": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz", + "integrity": "sha512-A8tG4Z4iNg4mw5tP1Vung9N9IjgMNqpiMoJ/FouSFwNCGHv2X0mmOYwtQOJzki6XN7r7Tyo01S29p7b224I4jw==", + "requires": { + "@babel/plugin-transform-runtime": "^7.5.5", + "@babel/runtime": "^7.5.5", + "eth-query": "^2.1.0", + "json-rpc-random-id": "^1.0.1", + "pify": "^3.0.0", + "safe-event-emitter": "^1.0.1" + } + }, + "eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw==", + "requires": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==" + } + } + }, + "eth-json-rpc-filters": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-4.2.2.tgz", + "integrity": "sha512-DGtqpLU7bBg63wPMWg1sCpkKCf57dJ+hj/k3zF26anXMzkmtSBDExL8IhUu7LUd34f0Zsce3PYNO2vV2GaTzaw==", + "requires": { + "@metamask/safe-event-emitter": "^2.0.0", + "async-mutex": "^0.2.6", + "eth-json-rpc-middleware": "^6.0.0", + "eth-query": "^2.1.2", + "json-rpc-engine": "^6.1.0", + "pify": "^5.0.0" + }, + "dependencies": { + "pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==" + } + } + }, + "eth-json-rpc-infura": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eth-json-rpc-infura/-/eth-json-rpc-infura-5.1.0.tgz", + "integrity": "sha512-THzLye3PHUSGn1EXMhg6WTLW9uim7LQZKeKaeYsS9+wOBcamRiCQVGHa6D2/4P0oS0vSaxsBnU/J6qvn0MPdow==", + "requires": { + "eth-json-rpc-middleware": "^6.0.0", + "eth-rpc-errors": "^3.0.0", + "json-rpc-engine": "^5.3.0", + "node-fetch": "^2.6.0" + }, + "dependencies": { + "json-rpc-engine": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz", + "integrity": "sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g==", + "requires": { + "eth-rpc-errors": "^3.0.0", + "safe-event-emitter": "^1.0.1" + } + } + } + }, + "eth-json-rpc-middleware": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-6.0.0.tgz", + "integrity": "sha512-qqBfLU2Uq1Ou15Wox1s+NX05S9OcAEL4JZ04VZox2NS0U+RtCMjSxzXhLFWekdShUPZ+P8ax3zCO2xcPrp6XJQ==", + "requires": { + "btoa": "^1.2.1", + "clone": "^2.1.1", + "eth-query": "^2.1.2", + "eth-rpc-errors": "^3.0.0", + "eth-sig-util": "^1.4.2", + "ethereumjs-util": "^5.1.2", + "json-rpc-engine": "^5.3.0", + "json-stable-stringify": "^1.0.1", + "node-fetch": "^2.6.1", + "pify": "^3.0.0", + "safe-event-emitter": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "json-rpc-engine": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz", + "integrity": "sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g==", + "requires": { + "eth-rpc-errors": "^3.0.0", + "safe-event-emitter": "^1.0.1" + } + } + } + }, + "eth-lib": { + "version": "0.1.29", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", + "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, + "eth-net-props": { + "version": "1.0.41", + "resolved": "https://registry.npmjs.org/eth-net-props/-/eth-net-props-1.0.41.tgz", + "integrity": "sha512-4qUNJU8xyqV53Lr+5JMnCUoknL/IIQ8Zpk1CKV/8h7tvtdrqbvnvJNb3IC0nUcyf4f0tNRTQnSWslut7XRwpRA==", + "requires": { + "chai": "^4.2.0" + } + }, + "eth-query": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/eth-query/-/eth-query-2.1.2.tgz", + "integrity": "sha1-1nQdkAAQa1FRDHLbktY2VFam2l4=", + "requires": { + "json-rpc-random-id": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "eth-rpc-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz", + "integrity": "sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg==", + "requires": { + "fast-safe-stringify": "^2.0.6" + } + }, + "eth-sig-util": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz", + "integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=", + "requires": { + "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git", + "ethereumjs-util": "^5.1.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereum-bloom-filters": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", + "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "requires": { + "js-sha3": "^0.8.0" + } + }, + "ethereum-common": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", + "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==" + }, + "ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "requires": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "ethereumjs-abi": { + "version": "git+ssh://git@github.com/ethereumjs/ethereumjs-abi.git#ee3994657fa7a427238e6ba92a84d0b529bbcde0", + "from": "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git", + "requires": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + } + } + }, + "ethereumjs-account": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz", + "integrity": "sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA==", + "requires": { + "ethereumjs-util": "^5.0.0", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-block": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz", + "integrity": "sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg==", + "requires": { + "async": "^2.0.1", + "ethereum-common": "0.2.0", + "ethereumjs-tx": "^1.2.2", + "ethereumjs-util": "^5.0.0", + "merkle-patricia-tree": "^2.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz", + "integrity": "sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA==" + }, + "ethereumjs-tx": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz", + "integrity": "sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA==", + "requires": { + "ethereum-common": "^0.0.18", + "ethereumjs-util": "^5.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereum-common": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", + "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "requires": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "dependencies": { + "@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "requires": { + "@types/node": "*" + } + } + } + }, + "ethereumjs-vm": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz", + "integrity": "sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw==", + "requires": { + "async": "^2.1.2", + "async-eventemitter": "^0.2.2", + "ethereumjs-account": "^2.0.3", + "ethereumjs-block": "~2.2.0", + "ethereumjs-common": "^1.1.0", + "ethereumjs-util": "^6.0.0", + "fake-merkle-patricia-tree": "^1.0.1", + "functional-red-black-tree": "^1.0.1", + "merkle-patricia-tree": "^2.3.2", + "rustbn.js": "~0.2.0", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-block": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz", + "integrity": "sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg==", + "requires": { + "async": "^2.0.1", + "ethereumjs-common": "^1.5.0", + "ethereumjs-tx": "^2.1.1", + "ethereumjs-util": "^5.0.0", + "merkle-patricia-tree": "^2.1.2" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-tx": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz", + "integrity": "sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw==", + "requires": { + "ethereumjs-common": "^1.5.0", + "ethereumjs-util": "^6.0.0" + } + }, + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + } + } + }, + "ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", + "requires": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" + } + } + }, + "ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, + "eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true + }, + "expect": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz", + "integrity": "sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1" + } + }, + "express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "requires": { + "type": "^2.7.2" + }, + "dependencies": { + "type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fake-merkle-patricia-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz", + "integrity": "sha1-S4w6z7Ugr635hgsfFM2M40As3dM=", + "requires": { + "checkpoint-store": "^1.1.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "form-data-encoder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", + "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==" + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "globule": "^1.0.0" + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true, + "optional": true, + "peer": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "requires": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "dependencies": { + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + } + } + }, + "globule": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz", + "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + }, + "dependencies": { + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "requires": { + "delegate": "^3.1.2" + } + }, + "got": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", + "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", + "requires": { + "@sindresorhus/is": "^4.6.0", + "@szmarczak/http-timer": "^5.0.1", + "@types/cacheable-request": "^6.0.2", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^6.0.4", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "form-data-encoder": "1.7.1", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "optional": true, + "peer": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true, + "optional": true, + "peer": true + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "highlight.js": { + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.6.0.tgz", + "integrity": "sha512-ig1eqDzJaB0pqEvlPVIpSSyMaO92bH1N2rJpLMN/nX396wTpDA4Eq0uK+7I/2XG17pFaaKE0kjV/XPeGt7Evjw==" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "optional": true, + "peer": true + }, + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + } + } + }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg==" + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "http2-wrapper": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.11.tgz", + "integrity": "sha512-aNAk5JzLturWEUiuhAN73Jcbq96R7rTitAoXV54FYMatvihnpD2+6PUgU4ce3D/m5VDbw+F5CsyKSF176ptitQ==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "dependencies": { + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + } + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "ms": "^2.0.0" + } + }, + "humps": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz", + "integrity": "sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao=" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "requires": {} + }, + "idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "requires": { + "punycode": "2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA==" + } + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" + }, + "immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-local": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", + "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "optional": true, + "peer": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true, + "optional": true, + "peer": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true, + "optional": true, + "peer": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fn/-/is-fn-1.0.0.tgz", + "integrity": "sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw=" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=" + }, + "is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", + "dev": true, + "optional": true, + "peer": true + }, + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true, + "optional": true, + "peer": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", + "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-weakref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", + "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "requires": { + "call-bind": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz", + "integrity": "sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==", + "dev": true, + "requires": { + "@jest/core": "^29.3.1", + "@jest/types": "^29.3.1", + "import-local": "^3.0.2", + "jest-cli": "^29.3.1" + } + }, + "jest-changed-files": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.2.0.tgz", + "integrity": "sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + } + }, + "jest-circus": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.3.1.tgz", + "integrity": "sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==", + "dev": true, + "requires": { + "@jest/environment": "^29.3.1", + "@jest/expect": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "p-limit": "^3.1.0", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-cli": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.3.1.tgz", + "integrity": "sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==", + "dev": true, + "requires": { + "@jest/core": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-config": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.3.1.tgz", + "integrity": "sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.3.1", + "@jest/types": "^29.3.1", + "babel-jest": "^29.3.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.3.1", + "jest-environment-node": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-runner": "^29.3.1", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-diff": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz", + "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.3.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-docblock": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.2.0.tgz", + "integrity": "sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.3.1.tgz", + "integrity": "sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==", + "dev": true, + "requires": { + "@jest/types": "^29.3.1", + "chalk": "^4.0.0", + "jest-get-type": "^29.2.0", + "jest-util": "^29.3.1", + "pretty-format": "^29.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-environment-jsdom": { + "version": "29.3.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.3.0.tgz", + "integrity": "sha512-xFLbMR4OF4lntNcO9LthJdPRbI9WgfFlG73aQS6wQ54+v4oSAp8T4FKUw0add+Z+Ghu/dirRxuvc4FzzN5kRxw==", + "dev": true, + "requires": { + "@jest/environment": "^29.3.0", + "@jest/fake-timers": "^29.3.0", + "@jest/types": "^29.2.1", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.3.0", + "jest-util": "^29.2.1", + "jsdom": "^20.0.0" + } + }, + "jest-environment-node": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.3.1.tgz", + "integrity": "sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==", + "dev": true, + "requires": { + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-mock": "^29.3.1", + "jest-util": "^29.3.1" + } + }, + "jest-get-type": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", + "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==", + "dev": true + }, + "jest-haste-map": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.3.1.tgz", + "integrity": "sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==", + "dev": true, + "requires": { + "@jest/types": "^29.3.1", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.2.0", + "jest-util": "^29.3.1", + "jest-worker": "^29.3.1", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-worker": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.3.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-leak-detector": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz", + "integrity": "sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==", + "dev": true, + "requires": { + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" + } + }, + "jest-matcher-utils": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz", + "integrity": "sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.3.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-message-util": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz", + "integrity": "sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.3.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.3.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-mock": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.3.1.tgz", + "integrity": "sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==", + "dev": true, + "requires": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "jest-util": "^29.3.1" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.2.0.tgz", + "integrity": "sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==", + "dev": true + }, + "jest-resolve": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.3.1.tgz", + "integrity": "sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.3.1", + "jest-validate": "^29.3.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-resolve-dependencies": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz", + "integrity": "sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==", + "dev": true, + "requires": { + "jest-regex-util": "^29.2.0", + "jest-snapshot": "^29.3.1" + } + }, + "jest-runner": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.3.1.tgz", + "integrity": "sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==", + "dev": true, + "requires": { + "@jest/console": "^29.3.1", + "@jest/environment": "^29.3.1", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.2.0", + "jest-environment-node": "^29.3.1", + "jest-haste-map": "^29.3.1", + "jest-leak-detector": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-resolve": "^29.3.1", + "jest-runtime": "^29.3.1", + "jest-util": "^29.3.1", + "jest-watcher": "^29.3.1", + "jest-worker": "^29.3.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-worker": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.3.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-runtime": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.3.1.tgz", + "integrity": "sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==", + "dev": true, + "requires": { + "@jest/environment": "^29.3.1", + "@jest/fake-timers": "^29.3.1", + "@jest/globals": "^29.3.1", + "@jest/source-map": "^29.2.0", + "@jest/test-result": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-mock": "^29.3.1", + "jest-regex-util": "^29.2.0", + "jest-resolve": "^29.3.1", + "jest-snapshot": "^29.3.1", + "jest-util": "^29.3.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-snapshot": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.3.1.tgz", + "integrity": "sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.3.1", + "@jest/transform": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.3.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.3.1", + "jest-get-type": "^29.2.0", + "jest-haste-map": "^29.3.1", + "jest-matcher-utils": "^29.3.1", + "jest-message-util": "^29.3.1", + "jest-util": "^29.3.1", + "natural-compare": "^1.4.0", + "pretty-format": "^29.3.1", + "semver": "^7.3.5" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-util": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", + "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", + "dev": true, + "requires": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-validate": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.3.1.tgz", + "integrity": "sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==", + "dev": true, + "requires": { + "@jest/types": "^29.3.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.2.0", + "leven": "^3.1.0", + "pretty-format": "^29.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-watcher": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.3.1.tgz", + "integrity": "sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==", + "dev": true, + "requires": { + "@jest/test-result": "^29.3.1", + "@jest/types": "^29.3.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.3.1", + "string-length": "^4.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jquery": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", + "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==" + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true, + "optional": true, + "peer": true + }, + "js-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", + "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==" + }, + "js-sdsl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz", + "integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==", + "dev": true + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsdom": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.0.tgz", + "integrity": "sha512-x4a6CKCgx00uCmP+QakBDFXwjAJ69IkkIWHmtmjd3wvXPcdOS44hfX2vqkOQrVrq8l9DhNNADZRXaCEWvgXtVA==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "acorn": "^8.7.1", + "acorn-globals": "^6.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.3.1", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "^7.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^3.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.8.0", + "xml-name-validator": "^4.0.0" + }, + "dependencies": { + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-rpc-engine": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-6.1.0.tgz", + "integrity": "sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ==", + "requires": { + "@metamask/safe-event-emitter": "^2.0.0", + "eth-rpc-errors": "^4.0.2" + }, + "dependencies": { + "eth-rpc-errors": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eth-rpc-errors/-/eth-rpc-errors-4.0.3.tgz", + "integrity": "sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg==", + "requires": { + "fast-safe-stringify": "^2.0.6" + } + } + } + }, + "json-rpc-random-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz", + "integrity": "sha1-uknZat7RRE27jaPSA3SKy7zeyMg=" + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + } + }, + "keyv": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.0.tgz", + "integrity": "sha512-2YvuMsA+jnFGtBareKqgANOEKe1mk3HKiXu2fRmAfyxG0MJAywNhi5ttWA3PMjl4NmpyjZNbFifR2vNjW1znfA==", + "requires": { + "json-buffer": "3.0.1" + } + }, + "keyvaluestorage-interface": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", + "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==" + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true + }, + "level-codec": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-7.0.1.tgz", + "integrity": "sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ==" + }, + "level-errors": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-1.0.5.tgz", + "integrity": "sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig==", + "requires": { + "errno": "~0.1.1" + } + }, + "level-iterator-stream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz", + "integrity": "sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0=", + "requires": { + "inherits": "^2.0.1", + "level-errors": "^1.0.3", + "readable-stream": "^1.0.33", + "xtend": "^4.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "level-ws": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/level-ws/-/level-ws-0.0.0.tgz", + "integrity": "sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos=", + "requires": { + "readable-stream": "~1.0.15", + "xtend": "~2.1.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "requires": { + "object-keys": "~0.4.0" + } + } + } + }, + "levelup": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz", + "integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==", + "requires": { + "deferred-leveldown": "~1.2.1", + "level-codec": "~7.0.0", + "level-errors": "~1.0.3", + "level-iterator-stream": "~1.3.0", + "prr": "~1.0.1", + "semver": "~5.4.1", + "xtend": "~4.0.0" + }, + "dependencies": { + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + } + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "dev": true + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "loader-runner": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", + "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "dev": true + }, + "loader-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz", + "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, + "lodash.differenceby": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/lodash.differenceby/-/lodash.differenceby-4.8.0.tgz", + "integrity": "sha1-z9WelDU69d5R2l0wLKTr/zP6rFc=" + }, + "lodash.find": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", + "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=" + }, + "lodash.first": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash.first/-/lodash.first-3.0.0.tgz", + "integrity": "sha1-Xa4YDX+BjuZfxbIQsQSnu++YoWo=" + }, + "lodash.forin": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.forin/-/lodash.forin-4.4.0.tgz", + "integrity": "sha1-XT8grlZAEfvog4H32YlJyclRlzE=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.intersectionby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.intersectionby/-/lodash.intersectionby-4.7.0.tgz", + "integrity": "sha1-EvEl5NoAsiKQ/r2htsG2i7klUSU=" + }, + "lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=" + }, + "lodash.keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-4.2.0.tgz", + "integrity": "sha1-oIYCrBLk+4P5H8H7ejYKTZujUgU=" + }, + "lodash.last": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash.last/-/lodash.last-3.0.0.tgz", + "integrity": "sha1-JC9mMRLdTG5jcoxgo8kJ0b2tvUw=" + }, + "lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" + }, + "lodash.max": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.max/-/lodash.max-4.0.1.tgz", + "integrity": "sha1-hzVWbGGLNan3YFILSHrnllivE2o=" + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lodash.min": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.min/-/lodash.min-4.0.1.tgz", + "integrity": "sha1-SsG5qLr4ttKKaQ1xZRJRDPwUcIw=" + }, + "lodash.noop": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-3.0.1.tgz", + "integrity": "sha1-OBiPTWUKOkdCWEObluxFsyYXEzw=" + }, + "lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=" + }, + "lodash.rangeright": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.rangeright/-/lodash.rangeright-4.2.0.tgz", + "integrity": "sha1-dCrF5C+R9oKiwLaHwpt52TIzkEI=" + }, + "lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" + }, + "luxon": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.1.0.tgz", + "integrity": "sha512-7w6hmKC0/aoWnEsmPCu5Br54BmbmUp5GfcqBxQngRcXJ+q5fdfjEzn7dxmJh2YdDhgW8PccYtlWKSv4tQkrTQg==" + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + } + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "optional": true, + "peer": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "memdown": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", + "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", + "requires": { + "abstract-leveldown": "~2.7.1", + "functional-red-black-tree": "^1.0.1", + "immediate": "^3.2.3", + "inherits": "~2.0.1", + "ltgt": "~2.2.0", + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "abstract-leveldown": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", + "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "requires": { + "xtend": "~4.0.0" + } + } + } + }, + "meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "hosted-git-info": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", + "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "optional": true, + "peer": true + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "merkle-patricia-tree": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz", + "integrity": "sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g==", + "requires": { + "async": "^1.4.2", + "ethereumjs-util": "^5.0.0", + "level-ws": "0.0.0", + "levelup": "^1.2.1", + "memdown": "^1.0.0", + "readable-stream": "^2.0.0", + "rlp": "^2.0.0", + "semaphore": ">=1.0.1" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "optional": true, + "peer": true + }, + "mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dev": true, + "requires": { + "schema-utils": "^4.0.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "minipass": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.5.tgz", + "integrity": "sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w==", + "requires": { + "mkdirp": "*" + } + }, + "mock-fs": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", + "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" + }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multibase": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", + "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "multicodec": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", + "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", + "requires": { + "varint": "^5.0.0" + } + }, + "multihashes": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", + "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", + "requires": { + "buffer": "^5.5.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + }, + "dependencies": { + "multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + } + } + }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "dev": true, + "optional": true, + "peer": true + }, + "nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew==" + }, + "nanoassert": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz", + "integrity": "sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40=" + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + }, + "nanomorph": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/nanomorph/-/nanomorph-5.4.3.tgz", + "integrity": "sha512-uPP5y0x21KISffZCKHh1A0QW0RHZFQS0BR7LetlHBlay6UWAbjwhjiJTxOO6JeMHko5Cigl617zFoGrYFJ8ZLg==", + "requires": { + "nanoassert": "^1.1.0" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, + "node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "gauge": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz", + "integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "ansi-regex": "^5.0.1", + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, + "npmlog": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz", + "integrity": "sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.0", + "set-blocking": "^2.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "node-sass": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-7.0.3.tgz", + "integrity": "sha512-8MIlsY/4dXUkJDYht9pIWBhMil3uHmE8b/AdJPjmFn1nBx9X9BASzfzmsCy0uCCb8eqI3SYYzVPDswWqSx7gjw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "lodash": "^4.17.15", + "meow": "^9.0.0", + "nan": "^2.13.2", + "node-gyp": "^8.4.1", + "npmlog": "^5.0.0", + "request": "^2.88.0", + "sass-graph": "^4.0.1", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "optional": true, + "peer": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "optional": true, + "peer": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true, + "peer": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "requires": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==" + } + } + }, + "numeral": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", + "integrity": "sha1-StCAk21EPCVhrtnyGX7//iX05QY=" + }, + "nwsapi": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.1.tgz", + "integrity": "sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==" + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "oboe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", + "integrity": "sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA==", + "requires": { + "http-https": "^1.0.0" + } + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==" + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + }, + "dependencies": { + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-headers": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.4.tgz", + "integrity": "sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw==" + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz", + "integrity": "sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==", + "dev": true, + "requires": { + "entities": "^4.3.0" + }, + "dependencies": { + "entities": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz", + "integrity": "sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==", + "dev": true + } + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/path-parser/-/path-parser-6.1.0.tgz", + "integrity": "sha512-nAB6J73z2rFcQP+870OHhpkHFj5kO4rPLc2Ol4Y3Ale7F6Hk1/cPKp7cQ8RznKF8FOSvu+YR9Xc6Gafk7DlpYA==", + "requires": { + "search-params": "3.0.0", + "tslib": "^1.10.0" + } + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==" + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "phoenix": { + "version": "file:../../../deps/phoenix" + }, + "phoenix_html": { + "version": "file:../../../deps/phoenix_html" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "pikaday": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/pikaday/-/pikaday-1.8.2.tgz", + "integrity": "sha512-TNtsE+34BIax3WtkB/qqu5uepV1McKYEgvL3kWzU7aqPCpMEN6rBF3AOwu4WCwAealWlBGobXny/9kJb49C1ew==" + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true + }, + "pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==" + }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" + }, + "postcss": { + "version": "8.4.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz", + "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-convert-values": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz", + "integrity": "sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==", + "dev": true, + "requires": { + "browserslist": "^4.20.3", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "dev": true, + "requires": {} + }, + "postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "dev": true, + "requires": {} + }, + "postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "dev": true, + "requires": {} + }, + "postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "dev": true, + "requires": {} + }, + "postcss-loader": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", + "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.7" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "postcss-merge-longhand": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz", + "integrity": "sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" + } + }, + "postcss-merge-rules": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz", + "integrity": "sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dev": true, + "requires": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-params": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz", + "integrity": "sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "dev": true, + "requires": {} + }, + "postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-unicode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dev": true, + "requires": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dev": true, + "requires": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-reduce-initial": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + } + }, + "postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "preact": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.4.1.tgz", + "integrity": "sha512-WKrRpCSwL2t3tpOOGhf2WfTpcmbpxaWtDbdJdKdjd0aEiTkvOmS4NBkG6kzlaAHI9AkQ3iVqbFWM3Ei7mZ4o1Q==" + }, + "precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "pretty-format": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz", + "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==", + "dev": true, + "requires": { + "@jest/schemas": "^29.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + } + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true, + "optional": true, + "peer": true + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "promise-to-callback": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/promise-to-callback/-/promise-to-callback-1.0.0.tgz", + "integrity": "sha1-XSp0kBC/tn2WNZj805YHRqaP7vc=", + "requires": { + "is-fn": "^1.0.0", + "set-immediate-shim": "^1.0.1" + } + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qrcode": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz", + "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==", + "requires": { + "buffer": "^5.4.3", + "buffer-alloc": "^1.2.0", + "buffer-from": "^1.1.1", + "dijkstrajs": "^1.0.1", + "isarray": "^2.0.1", + "pngjs": "^3.3.0", + "yargs": "^13.2.4" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "optional": true, + "peer": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "optional": true, + "peer": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "optional": true, + "peer": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "optional": true, + "peer": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "reduce-reducers": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reduce-reducers/-/reduce-reducers-1.0.4.tgz", + "integrity": "sha512-Mb2WZ2bJF597exiqX7owBzrqJ74DHLK3yOQjCyPAaNifRncE8OD0wFIuoMhXxTnHK07+8zZ2SJEKy/qtiyR7vw==" + }, + "redux": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "regexpu-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", + "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "dev": true, + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true + } + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true + }, + "responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "requires": { + "lowercase-keys": "^2.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true, + "optional": true, + "peer": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "requires": { + "bn.js": "^5.2.0" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rustbn.js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", + "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-event-emitter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz", + "integrity": "sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg==", + "requires": { + "events": "^3.0.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass": { + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.56.0.tgz", + "integrity": "sha512-WFJ9XrpkcnqZcYuLRJh5qiV6ibQOR4AezleeEjTjMsCocYW59dEG19U3fwTTXxzi2Ed3yjPBp727hbbj53pHFw==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-graph": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-4.0.1.tgz", + "integrity": "sha512-5YCfmGBmxoIRYHnKK2AKzrAkCoQ8ozO+iumT8K4tXJXRVCPf+7s1/9KxTSW3Rbvf+7Y7b4FR3mWyLnQr3PHocA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.17.11", + "scss-tokenizer": "^0.4.3", + "yargs": "^17.2.1" + } + }, + "sass-loader": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.1.0.tgz", + "integrity": "sha512-tZS1RJQ2n2+QNyf3CCAo1H562WjL/5AM6Gi8YcPVVoNxQX8d19mx8E+8fRrMWsyc93ZL6Q8vZDSM0FHVTJaVnQ==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "scss-tokenizer": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.4.3.tgz", + "integrity": "sha512-raKLgf1LI5QMQnG+RxHz6oK0sL3x3I4FN2UDLqgLOGO8hodECNnNh5BXn7fAyBxrA8zVzdQizQ6XjNJQ+uBwMw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "js-base64": "^2.4.9", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "optional": true, + "peer": true + } + } + }, + "search-params": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/search-params/-/search-params-3.0.0.tgz", + "integrity": "sha512-8CYNl/bjkEhXWbDTU/K7c2jQtrnqEffIPyOLMqygW/7/b+ym8UtQumcAZjOfMLjZKR6AxK5tOr9fChbQZCzPqg==" + }, + "secp256k1": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", + "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", + "requires": { + "elliptic": "^6.5.2", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + }, + "semaphore": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", + "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "requires": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" + }, + "simple-get": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", + "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + }, + "dependencies": { + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "requires": { + "mimic-response": "^1.0.0" + } + } + } + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "optional": true, + "peer": true + }, + "socks": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", + "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", + "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.1", + "socks": "^2.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "source-map-support": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true, + "optional": true, + "peer": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz", + "integrity": "sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==", + "dev": true, + "optional": true, + "peer": true + }, + "split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "readable-stream": "^2.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "requires": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "style-loader": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "dev": true, + "requires": {} + }, + "styled-components": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz", + "integrity": "sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^0.8.8", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + } + }, + "stylehacks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + } + }, + "swarm-js": { + "version": "0.1.42", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.42.tgz", + "integrity": "sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ==", + "requires": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^11.8.5", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request": "^1.0.1" + }, + "dependencies": { + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "got": { + "version": "11.8.5", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.5.tgz", + "integrity": "sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==", + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "requires": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "sweetalert2": { + "version": "11.6.7", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.6.7.tgz", + "integrity": "sha512-nbsmUtfTodJvZ873sn61U0brrHtn8xi8wfn+bVVc9IV05fgOdDMhAhdQ2zQJBYsvggbbp46ZFHMr0/fZJnZPtQ==" + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz", + "integrity": "sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==", + "dev": true, + "requires": { + "jest-worker": "^27.0.6", + "p-limit": "^3.1.0", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "dependencies": { + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==" + }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true + } + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "optional": true, + "peer": true + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "glob": "^7.1.2" + } + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + } + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "update-browserslist-db": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==" + }, + "utf-8-validate": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", + "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", + "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "dev": true, + "requires": { + "xml-name-validator": "^4.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "web3": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.8.0.tgz", + "integrity": "sha512-sldr9stK/SALSJTgI/8qpnDuBJNMGjVR84hJ+AcdQ+MLBGLMGsCDNubCoyO6qgk1/Y9SQ7ignegOI/7BPLoiDA==", + "requires": { + "web3-bzz": "1.8.0", + "web3-core": "1.8.0", + "web3-eth": "1.8.0", + "web3-eth-personal": "1.8.0", + "web3-net": "1.8.0", + "web3-shh": "1.8.0", + "web3-utils": "1.8.0" + } + }, + "web3-bzz": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.8.0.tgz", + "integrity": "sha512-caDtdKeLi7+2Vb+y+cq2yyhkNjnxkFzVW0j1DtemarBg3dycG1iEl75CVQMLNO6Wkg+HH9tZtRnUyFIe5LIUeQ==", + "requires": { + "@types/node": "^12.12.6", + "got": "12.1.0", + "swarm-js": "^0.1.40" + }, + "dependencies": { + "@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + } + } + }, + "web3-core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.8.0.tgz", + "integrity": "sha512-9sCA+Z02ci6zoY2bAquFiDjujRwmSKHiSGi4B8IstML8okSytnzXk1izHYSynE7ahIkguhjWAuXFvX76F5rAbA==", + "requires": { + "@types/bn.js": "^5.1.0", + "@types/node": "^12.12.6", + "bignumber.js": "^9.0.0", + "web3-core-helpers": "1.8.0", + "web3-core-method": "1.8.0", + "web3-core-requestmanager": "1.8.0", + "web3-utils": "1.8.0" + }, + "dependencies": { + "@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + } + } + }, + "web3-core-helpers": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.8.0.tgz", + "integrity": "sha512-nMAVwZB3rEp/khHI2BvFy0e/xCryf501p5NGjswmJtEM+Zrd3Biaw52JrB1qAZZIzCA8cmLKaOgdfamoDOpWdw==", + "requires": { + "web3-eth-iban": "1.8.0", + "web3-utils": "1.8.0" + } + }, + "web3-core-method": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.8.0.tgz", + "integrity": "sha512-c94RAzo3gpXwf2rf8rL8C77jOzNWF4mXUoUfZYYsiY35cJFd46jQDPI00CB5+ZbICTiA5mlVzMj4e7jAsTqiLA==", + "requires": { + "@ethersproject/transactions": "^5.6.2", + "web3-core-helpers": "1.8.0", + "web3-core-promievent": "1.8.0", + "web3-core-subscriptions": "1.8.0", + "web3-utils": "1.8.0" + } + }, + "web3-core-promievent": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.8.0.tgz", + "integrity": "sha512-FGLyjAuOaAQ+ZhV6iuw9tg/9WvIkSZXKHQ4mdTyQ8MxVraOtFivOCbuLLsGgapfHYX+RPxsc1j1YzQjKoupagQ==", + "requires": { + "eventemitter3": "4.0.4" + } + }, + "web3-core-requestmanager": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.8.0.tgz", + "integrity": "sha512-2AoYCs3Owl5foWcf4uKPONyqFygSl9T54L8b581U16nsUirjhoTUGK/PBhMDVcLCmW4QQmcY5A8oPFpkQc1TTg==", + "requires": { + "util": "^0.12.0", + "web3-core-helpers": "1.8.0", + "web3-providers-http": "1.8.0", + "web3-providers-ipc": "1.8.0", + "web3-providers-ws": "1.8.0" + } + }, + "web3-core-subscriptions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.8.0.tgz", + "integrity": "sha512-7lHVRzDdg0+Gcog55lG6Q3D8JV+jN+4Ly6F8cSn9xFUAwOkdbgdWsjknQG7t7CDWy21DQkvdiY2BJF8S68AqOA==", + "requires": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.8.0" + } + }, + "web3-eth": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.8.0.tgz", + "integrity": "sha512-hist52os3OT4TQFB/GxPSMxTh3995sz6LPvQpPvj7ktSbpg9RNSFaSsPlCT63wUAHA3PZb1FemkAIeQM5t72Lw==", + "requires": { + "web3-core": "1.8.0", + "web3-core-helpers": "1.8.0", + "web3-core-method": "1.8.0", + "web3-core-subscriptions": "1.8.0", + "web3-eth-abi": "1.8.0", + "web3-eth-accounts": "1.8.0", + "web3-eth-contract": "1.8.0", + "web3-eth-ens": "1.8.0", + "web3-eth-iban": "1.8.0", + "web3-eth-personal": "1.8.0", + "web3-net": "1.8.0", + "web3-utils": "1.8.0" + } + }, + "web3-eth-abi": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.8.0.tgz", + "integrity": "sha512-xPeMb2hS9YLQK/Q5YZpkcmzoRGM+/R8bogSrYHhNC3hjZSSU0YRH+1ZKK0f9YF4qDZaPMI8tKWIMSCDIpjG6fg==", + "requires": { + "@ethersproject/abi": "^5.6.3", + "web3-utils": "1.8.0" + } + }, + "web3-eth-accounts": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.8.0.tgz", + "integrity": "sha512-HQ/MDSv4bexwJLvnqsM6xpGE7c2NVOqyhzOZFyMUKXbIwIq85T3TaLnM9pCN7XqMpDcfxqiZ3q43JqQVkzHdmw==", + "requires": { + "@ethereumjs/common": "^2.5.0", + "@ethereumjs/tx": "^3.3.2", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.8", + "ethereumjs-util": "^7.0.10", + "scrypt-js": "^3.0.1", + "uuid": "3.3.2", + "web3-core": "1.8.0", + "web3-core-helpers": "1.8.0", + "web3-core-method": "1.8.0", + "web3-utils": "1.8.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "web3-eth-contract": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.8.0.tgz", + "integrity": "sha512-6xeXhW2YoCrz2Ayf2Vm4srWiMOB6LawkvxWJDnUWJ8SMATg4Pgu42C/j8rz/enXbYWt2IKuj0kk8+QszxQbK+Q==", + "requires": { + "@types/bn.js": "^5.1.0", + "web3-core": "1.8.0", + "web3-core-helpers": "1.8.0", + "web3-core-method": "1.8.0", + "web3-core-promievent": "1.8.0", + "web3-core-subscriptions": "1.8.0", + "web3-eth-abi": "1.8.0", + "web3-utils": "1.8.0" + }, + "dependencies": { + "@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "requires": { + "@types/node": "*" + } + } + } + }, + "web3-eth-ens": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.8.0.tgz", + "integrity": "sha512-/eFbQEwvsMOEiOhw9/iuRXCsPkqAmHHWuFOrThQkozRgcnSTRnvxkkRC/b6koiT5/HaKeUs4yQDg+/ixsIxZxA==", + "requires": { + "content-hash": "^2.5.2", + "eth-ens-namehash": "2.0.8", + "web3-core": "1.8.0", + "web3-core-helpers": "1.8.0", + "web3-core-promievent": "1.8.0", + "web3-eth-abi": "1.8.0", + "web3-eth-contract": "1.8.0", + "web3-utils": "1.8.0" + } + }, + "web3-eth-iban": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.8.0.tgz", + "integrity": "sha512-4RbvUxcMpo/e5811sE3a6inJ2H4+FFqUVmlRYs0RaXaxiHweahSRBNcpO0UWgmlePTolj0rXqPT2oEr0DuC8kg==", + "requires": { + "bn.js": "^5.2.1", + "web3-utils": "1.8.0" + } + }, + "web3-eth-personal": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.8.0.tgz", + "integrity": "sha512-L7FT4nR3HmsfZyIAhFpEctKkYGOjRC2h6iFKs9gnFCHZga8yLcYcGaYOBIoYtaKom99MuGBoosayWt/Twh7F5A==", + "requires": { + "@types/node": "^12.12.6", + "web3-core": "1.8.0", + "web3-core-helpers": "1.8.0", + "web3-core-method": "1.8.0", + "web3-net": "1.8.0", + "web3-utils": "1.8.0" + }, + "dependencies": { + "@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + } + } + }, + "web3-net": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.8.0.tgz", + "integrity": "sha512-kX6EAacK7QrOe7DOh0t5yHS5q2kxZmTCxPVwSz9io9xBeE4n4UhmzGJ/VfhP2eM3OPKYeypcR3LEO6zZ8xn2vw==", + "requires": { + "web3-core": "1.8.0", + "web3-core-method": "1.8.0", + "web3-utils": "1.8.0" + } + }, + "web3-provider-engine": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/web3-provider-engine/-/web3-provider-engine-16.0.1.tgz", + "integrity": "sha512-/Eglt2aocXMBiDj7Se/lyZnNDaHBaoJlaUfbP5HkLJQC/HlGbR+3/W+dINirlJDhh7b54DzgykqY7ksaU5QgTg==", + "requires": { + "async": "^2.5.0", + "backoff": "^2.5.0", + "clone": "^2.0.0", + "cross-fetch": "^2.1.0", + "eth-block-tracker": "^4.4.2", + "eth-json-rpc-filters": "^4.2.1", + "eth-json-rpc-infura": "^5.1.0", + "eth-json-rpc-middleware": "^6.0.0", + "eth-rpc-errors": "^3.0.0", + "eth-sig-util": "^1.4.2", + "ethereumjs-block": "^1.2.2", + "ethereumjs-tx": "^1.2.0", + "ethereumjs-util": "^5.1.5", + "ethereumjs-vm": "^2.3.4", + "json-stable-stringify": "^1.0.1", + "promise-to-callback": "^1.0.0", + "readable-stream": "^2.2.9", + "request": "^2.85.0", + "semaphore": "^1.0.3", + "ws": "^5.1.1", + "xhr": "^2.2.0", + "xtend": "^4.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "ws": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", + "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "web3-providers-http": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.8.0.tgz", + "integrity": "sha512-/MqxwRzExohBWW97mqlCSW/+NHydGRyoEDUS1bAIF2YjfKFwyRtHgrEzOojzkC9JvB+8LofMvbXk9CcltpZapw==", + "requires": { + "abortcontroller-polyfill": "^1.7.3", + "cross-fetch": "^3.1.4", + "es6-promise": "^4.2.8", + "web3-core-helpers": "1.8.0" + }, + "dependencies": { + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "requires": { + "node-fetch": "2.6.7" + } + } + } + }, + "web3-providers-ipc": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.8.0.tgz", + "integrity": "sha512-tAXHtVXNUOgehaBU8pzAlB3qhjn/PRpjdzEjzHNFqtRRTwzSEKOJxFeEhaUA4FzHnTlbnrs8ujHWUitcp1elfg==", + "requires": { + "oboe": "2.1.5", + "web3-core-helpers": "1.8.0" + } + }, + "web3-providers-ws": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.8.0.tgz", + "integrity": "sha512-bcZtSifsqyJxwkfQYamfdIRp4nhj9eJd7cxHg1uUkfLJK125WP96wyJL1xbPt7qt0MpfnTFn8/UuIqIB6nFENg==", + "requires": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.8.0", + "websocket": "^1.0.32" + } + }, + "web3-shh": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.8.0.tgz", + "integrity": "sha512-DNRgSa9Jf9xYFUGKSMylrf+zt3MPjhI2qF+UWX07o0y3+uf8zalDGiJOWvIS4upAsdPiKKVJ7co+Neof47OMmg==", + "requires": { + "web3-core": "1.8.0", + "web3-core-method": "1.8.0", + "web3-core-subscriptions": "1.8.0", + "web3-net": "1.8.0" + } + }, + "web3-utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.8.0.tgz", + "integrity": "sha512-7nUIl7UWpLVka2f09CMbKOSEvorvHnaugIabU4mj7zfMvm0tSByLcEu3eyV9qgS11qxxLuOkzBIwCstTflhmpQ==", + "requires": { + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereumjs-util": "^7.1.0", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + } + }, + "web3modal": { + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/web3modal/-/web3modal-1.9.9.tgz", + "integrity": "sha512-ML1C4xH+JTSHHkKbjxuF+f5B3cDUOCnrdQZ8Mlzippq7zRKHf3NBeuIvDdNjtVclJ2S4zYYVmVqRWrgB11ej8A==", + "requires": { + "detect-browser": "^5.1.0", + "prop-types": "^15.7.2", + "react": "^16.8.6", + "react-dom": "^16.8.6", + "styled-components": "^5.3.3", + "tslib": "^1.10.0" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true + }, + "webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "requires": {} + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + } + } + }, + "webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "requires": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "requires": { + "iconv-lite": "0.6.3" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "whatwg-fetch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" + }, + "which-typed-array": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", + "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.7" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "optional": true, + "peer": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "ws": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", + "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "dev": true, + "requires": {} + }, + "xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "requires": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "requires": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "xhr-request-promise": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", + "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", + "requires": { + "xhr-request": "^1.1.0" + } + }, + "xhr2-cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", + "integrity": "sha512-hjXUA6q+jl/bd8ADHcVfFsSPIf+tyLIjuO9TwJC9WI6JP2zKcS7C+p56I9kCLLsaCiNT035iYvEUUzdEFj/8+g==", + "requires": { + "cookiejar": "^2.1.1" + } + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "xss": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz", + "integrity": "sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==", + "requires": { + "commander": "^2.20.3", + "cssfilter": "0.0.10" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + } + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "dependencies": { + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "optional": true, + "peer": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json new file mode 100644 index 0000000..ef1f6df --- /dev/null +++ b/apps/block_scout_web/assets/package.json @@ -0,0 +1,110 @@ +{ + "repository": { + "type": "git", + "url": "git+https://github.com/blockscout/blockscout.git" + }, + "private": true, + "name": "blockscout", + "author": "Blockscout", + "license": "GPL-3.0", + "engines": { + "node": "16.x", + "npm": "8.x" + }, + "scripts": { + "deploy": "webpack --mode production", + "watch": "webpack --mode development --watch", + "build": "webpack --mode development", + "test": "jest", + "eslint": "eslint js/**" + }, + "dependencies": { + "@fortawesome/fontawesome-free": "^6.2.0", + "@tarekraafat/autocomplete.js": "^10.2.7", + "@walletconnect/web3-provider": "^1.8.0", + "assert": "^2.0.0", + "bignumber.js": "^9.1.0", + "bootstrap": "^4.6.0", + "chart.js": "^3.9.1", + "chartjs-adapter-luxon": "^1.2.0", + "clipboard": "^2.0.11", + "core-js": "^3.26.0", + "crypto-browserify": "^3.12.0", + "dropzone": "^5.9.3", + "eth-net-props": "^1.0.41", + "highlight.js": "^11.6.0", + "https-browserify": "^1.0.0", + "humps": "^2.0.1", + "jquery": "^3.6.1", + "js-cookie": "^3.0.1", + "lodash.debounce": "^4.0.8", + "lodash.differenceby": "^4.8.0", + "lodash.find": "^4.6.0", + "lodash.first": "^3.0.0", + "lodash.forin": "^4.4.0", + "lodash.get": "^4.4.2", + "lodash.intersectionby": "^4.7.0", + "lodash.isobject": "^3.0.2", + "lodash.keys": "^4.2.0", + "lodash.last": "^3.0.0", + "lodash.map": "^4.6.0", + "lodash.max": "^4.0.1", + "lodash.merge": "^4.6.2", + "lodash.min": "^4.0.1", + "lodash.noop": "^3.0.1", + "lodash.omit": "^4.5.0", + "lodash.rangeright": "^4.2.0", + "lodash.reduce": "^4.6.0", + "luxon": "^3.1.0", + "moment": "^2.29.4", + "nanomorph": "^5.4.0", + "numeral": "^2.0.6", + "os-browserify": "^0.3.0", + "path-parser": "^6.1.0", + "phoenix": "file:../../../deps/phoenix", + "phoenix_html": "file:../../../deps/phoenix_html", + "pikaday": "^1.8.2", + "popper.js": "^1.14.7", + "reduce-reducers": "^1.0.4", + "redux": "^4.2.0", + "stream-browserify": "^3.0.0", + "stream-http": "^3.1.1", + "sweetalert2": "^11.6.7", + "urijs": "^1.19.11", + "url": "^0.11.0", + "util": "^0.12.5", + "web3": "^1.8.0", + "web3modal": "^1.9.9", + "xss": "^1.0.14" + }, + "devDependencies": { + "@babel/core": "^7.20.2", + "@babel/preset-env": "^7.20.2", + "autoprefixer": "^10.4.13", + "babel-loader": "^9.1.0", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^5.2.7", + "css-minimizer-webpack-plugin": "^4.2.2", + "eslint": "^8.27.0", + "eslint-config-standard": "^17.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.1.1", + "file-loader": "^6.2.0", + "jest": "^29.3.1", + "jest-environment-jsdom": "^29.3.0", + "mini-css-extract-plugin": "^2.6.1", + "postcss": "^8.4.18", + "postcss-loader": "^7.0.1", + "sass": "^1.56.0", + "sass-loader": "^13.1.0", + "style-loader": "^3.3.1", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0" + }, + "jest": { + "moduleNameMapper": { + "/css/app.scss": "/__mocks__/css/app.scss.js" + } + } +} diff --git a/apps/block_scout_web/assets/postcss.config.js b/apps/block_scout_web/assets/postcss.config.js new file mode 100644 index 0000000..908944f --- /dev/null +++ b/apps/block_scout_web/assets/postcss.config.js @@ -0,0 +1,9 @@ +module.exports = { + plugins: [ + require('autoprefixer')({ + overrideBrowserslist: [ + 'last 4 versions' + ] + }) + ] +}; diff --git a/apps/block_scout_web/assets/static/android-chrome-192x192.png b/apps/block_scout_web/assets/static/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..22e910c75ad8d0c336e8fe3e5e4286f003360c66 GIT binary patch literal 16924 zcmX_IV|X7yx8K-moHVwRrZNAqZQE>Y+qRuFcG9SgowTuS+c)ofpL;*-&a)r(S)7?U zbAFfz1vzmfcszIj0FWdlM3g?;-v1o1(4S|eRU4Sk2FyuGTnMO{{CWfc#DJs-NX1?6 z%*P{}Ky>b2*~fh3+e@O%7a$ipOwuI}zvSl1h2x082lF3b!=++jIi<%T4Ik2+p{<#!H_R6o#yBL_)- z@aAmI`MJPXABfOUQ$e09*&%=#G3jGJv7yL0<`pBqvvCDI9wl6^$K|pxH!akVljn|M z2vF!f^1Fh@NRg%IQ6k$-1Mq^iIgXNZXa26-j1J#G%E(%PQK+_Q$AIMvHn6Akyt>D1 zx}u)qI;=c66R6+cx(Wzf3mUXl(mTexz;NYNvj7+ta_enHHjK^FbBs0+|*^00nm>!_=OJU)R*AZ{|R0yV*Z1zP}#tAE7NagoVpX zU28{aV#QQAeeeWs7CgAXb|h8jz0A`X-B?d?06o~idhU8*okX1+jBYU8+7uBtQP5J1 z$>}jMTr?PQT*uY2ZAzYe$)OK$zdiozm>|PiBW)x;O2F6jd8!io?CV+K&#(@RK_H)z zZZGu~8ly{5kxQM-9WuXj5!*AEK`K|e!TpB81bt*PE93fhV~{uC8iShSc4Fj`vhzon zjVau2+t$gLf=o-AUxY$Kx?nz^<7~8( zE)J~ZK|u=V;9*o90A515>Vc}qd zj$;?cWyQ!Cj9Xsr?vs#JufVzllO2kNcJ^53#AbpJ0LKHIE!j_d2`;mA4IU>|;9mT# z;;^%$MqilfmRDT^wcodRRo4JkTvy1^E*lfLW-aFYLxgLqzOL*@@4N~j)Mg{ER+z$@ z{wIdIfiuxeZ5Y6|(fXH^01wu+lxt222V?i2rG+6<+ z^5SXda#VSBPBvOZAXMJWqqDK#^-YyZA#flV&%^g7$v(m}(4+&ISREzBweoKP!9N^| zoI`JU^;yT5#}mY5h%>#SmLVmt3g#!KBi8kxlVk-Mf-hZFtlKckzRBHwE?_< zft!C@Ly`o`LSRZ|HwU4CpHF@eBj@3E{c^$Z6NAEVDR-=)9rn4LhN`))?%oWyQXIJR zH=@%)CFi+8mc{t2uP%j`W=I*VD7?gPq+FghagNDG6>^d)0z9V|q1W;?+yTeCu2vfF z@h0*l#kc@+Eo*O>R;1p{^6Jy-qHmzX9DRQ>5(4KBOwyknt=Emg7AO$uTjX3mN{ttT zZ}G`knlHwXA;|Fs{?HHdQUP&-cTVQ|+3zQHGQHI8N5}~NHhWX_ezL-QisVInVEOY| z8xK)2QLqhN&9iqhf4Yb)yPIN4Z~rc54Z5a6!MPv;Uq+bsYHMmU%g}zn`0K7w>LCzG zyF20yg@Y|FM(=|$26=!>9kudkai_)2kbIa7WnfW^HIaSS>T0@n5hZlEhj)dvteo&QAm>$ilqPn9+mZ=&|Y;{$CDyYaO-iB0U5l@rqo=7#=a%UFiNLt zVLn@e+E)lx-m5LQq^~vwleUSRruHVFuMUUOyBJ-ErM#JGYAVvPrBUJW1cA>s#7}5| zr{-7B{vyV}H;cu&Qa(^Xtx|86^$2Yp2^Ne)1o6X23{wV=amv3(2^hxo#(bZj$sIs* zROB&-_rzzAuM{TC*zFYcp*qT29aZ2cHK_57AvKG!%s6zUq9H6o5&n+z~EX)q5?Y9{)9v0$ROi$;;AwS5hGoF284P zma}Ac$`KO(y%FmoPs=uUk~rQd{y^s6I{*)9QP|;Y=U2HS&r?LDkdv5B%l;z^f3$WX z8xp)Fz~>>G&bOPQ5*%5zc5@Jp$bUQ2xB6{?3<1)kCHT)#dl&+#y?}xbI8=KS>Tk{- zb={mE3~+r>(FcwlD%HIYc&bG403pQiw=+%8Lt${wr2uSaUJe~6)~^q$*@Ig+T6PyB z^dFpBK3_xbiAjEs(a_VlgD)=j)$-{P*-O0>D-GP&``CXN8aVI<4T(CSv1S5quMiSL zM8k4Fh%e;8fS~*7%v#_Pjimo`n4k3&yt1UM5sMDC`j37Hy&JI)azn zLI4X^vS&T~o6v~mh?d`5muP^x zt5KXNXutEln?^{~?nN4W05&WJrZK)*CX=kn9S& zgz}uA21f!*N|VFaH_L+ybn`Iyia8d@?ArE7mf%%3HVc#req5Cow6ryyx{Wo%cO5zTcS2#eQvf*92zKv`cA;vqpA_=u3aTm66PLsko{LKRS#UJ zTyEXwCM!MKln#6|K7j-!!i?=970)~}$J{d1ZA7tvAJZoaRPx{cdBfLxEjTF%E!|7K zB@$TH|3qa?rE+oWY{qUER1$zHaM@?-?}s*B?2ZObHXY>a}lgB`T zQ_f*knszty{k;lHP-Rb$nP^DwN&p>mE;lYVyqb-V31lRnmos=%C_U2`<8j6$=;?tW z1`spL_8c6<(mJ#&eoe?W!Rwfi2|Cn<(*Sx#pZh9#&#BNEVlX)V8(4paL zxTwMd+=nX2H-YN7e;#nAj-3rP_wQsST$60`nsfnzb;>1>(a5&sDm@M#PXtp`ja_6r zl{Wk48hO1xkAL!~7B}TVl($I8=kKAYzAFrHc%}LlS|qwBL^hE%@ncQ3+IAp9f;JPN zMqXvRLR<*G7v;^>y1Y6zd4i|V;HrtHt*sXDP5UCI+nZgPdWv)t(V>&KKA*Cw7WNi{ z%aE&z3RES+!Ao8Bg9nO6oEkm_8kaT35I>X7Y77rFtX7y5RwS*ufsr&XRoJ;%HJ?DDo+(u$N(!JogM)Pzz1TeT;Vesl>1T0|t!#q0 zj0}qpSDbCL;;c$Jea4?5Dky(a_ zA-LPRx8aMio`H~Q-K&Y#@7muur5oOck&+HArzg^I^+0V#TcK4XXjQbhAL^3^M&11t zY#`@qJa*}D<&X~53mT_JKr|{?7weW__;B0n4x52wp1|Fpn=6K&SiG1>1>|8 zB3#)WUs#bBkU#o;y3gaid~QRqib=ya!}zj2%wcf%MyB2;P=w3$SxSP2QZ6rSgs2Bh z6y3^rgYpPUc;&Cg!x&e71|cEj=|>K3BA1sMd$X`44CYOT4qioiYpk6eMoYmnMvj$q zc&O#fC==I!+V^?1WGbbrpu>bW&ZpFJe?UnmWVCAvbQXY3qN+v6@b7DLz9;k=zTWSB z>`Cysml(YqDJ7@qEZEzsx{@7B51IH7)nq>5-{;qSx|$JQcMX+H))H&RCSIrN3D>Iw zWvhM5M_RrfS)7qXGBGNI3!H`1R>Ko`WA$S5>|QSmk)w(}y(#7E4>A@5=qh%(N$fID ze@}7;<~e-B3%X_3cfx;l>@h7rvaMh6tyJEOZ9qj@cG|pbhl=*`FUeWU9183A~&a_Ijjmkuf3Q86W@ZWJqXMxmu>V?l{Z1Fs|) zuhzo1v#{y3#goc=VHd}c@hvVU83xVy={PLcew3MY8uDW7; z+@jNmPSZ197nM!ffK>L(*m}K5qEM&!1qB55Qc-MU|BvXNOeWo%Gh9uD`Oo*ZYN(0Y z_3+%dTo=xsH!D2zVGyxFlqn)4M%vV`{IiTeB&{tm#lHp9qIQ|gV*>vaSmV0F(ooYY z5T)CB#n{HLc@{X+JY_pK>Rx0!6R#UH%&bw@&C}qMI?FOcuy>Dw5k#r0Geac9K5%xd z9_Usx%v8kvJoz%;FCOZvpP2j^ZAcLKj6(RLvV==8Y=dW?id){=6Sm>RAc1+_h%a5$ z$KQ!vSZ8#WjgOte@6rY!U88BV_Nn*OmAtb8J7avn$Ad(0hlm9BH2zI=d?}fG*&W=b zz7@J7xn+MxXRv1!^IJKw>uU-(@s$VRV1B!*3z!{uYhIqD5(9_5X4ZvI#LWEZS-xLI zL(mEl?C2Y|gagD=d*Iw%9e1gpO$}CMmd0vr*GHB^q9Z ze7%8hDV*zipAxI%S-k0P_ej@AMaNic%<~_!fU8i108gHc@q||&=dXP`O8+zJantrU z@h=$jSl$t{o^(^P|39we-=+LtORfewK)57|tfFJN5>F8HzR97-t2j7@^Ob+1#R=#nVM~n^6aAKxSeWJPr1g&Nato#O_CkM|!`Z8)R z!t`sDW|f>8%VpV9+vwO5w`f$xxEQG*a*}RH?8Hr4GhW z4t9v-eX@9=UuPPFd%f+1Q>vh%EuUl%&r z4ZOfy@5Cy1Mm4cp1X8lf!WX2YEh82j6|BSV4-M{Ko7K`!Xv@;?Z*gvzYYjGLs+N4K z&d&Z^;L@XpC{dm0f#<;Y3L*BP6%Wdbo{gU(kxq>PzTpB6UB}#_ ziP^B{>6YiA!}mit{|0YPFUS@;S}zu?G@@|HFowRiWeDRqI-0N1fh{q9pp`5RqkOln zCmGTMxbfHEY|?^EtUdMUiGc>$+`(EBwgsYSMP6T>ze$Cp0B&F-k-USRI)e_;B%o55mXJuKp!AZ~K+yMD5B zi^72ImmR|khg~BB#v!fU$Ie3E)mB(1W{lz9;Ph@Ix8D`#H=tm&Wpmgc4ybb%QMKK^ zz2#UuA5a`xKUcX?X`SYt;*f^GAr0lb&bujSHOtHKMvN+ad(%DCcO1r@(W)xx`SnQqj zb-hhcwyED_7=FI#V07?m)gMt4KT$L7%#EfIU*9@doMeSeIt$1}vhdz=MzqzgEi|{o z&&VJdpw9B;KHm3+5#7<;6JNJYV9718t+vo~MDUE3kk~{xM?~hG0|tg&!4j#(l=HX;C~%u-`tnQdWe0>qt_wR5{*gNtHw)(NtD6g0D-#UjgikG15pZ*&d|Sn4 z3TQu}93p}O`g_R6?h$ROxyeLfc}hGY3*x#FW3d6>XJe>z7V7|dvjMV+K7gjO_trSK zIdWuMKz(eKh^*^=@-l`F?aN#wtz!GbQ~C2EmqZNfp8{_`>bd$ZQ;KCZ29W-V0I;Uw zt8Upv9DV>LgN+IUOeWCgF8bfVXKNQ=#Bi4^*v;?|O~JtaKnA2h>m5DURr$-hgVquB zcL=TTfhBSI0yT+V?LEWczvhXkD>5T=<9x8RBwyE*$8GIj|4QNqbnZooHRpl9F>_o|@~}_C$28O)BH$3TaH(Qnjdk!bTS*WFqbd>$3(|)bkIb z8o2ZTbc`{O~*nbx7{KNlPRYzT44OW?VNyG0t(nZ#sw^;aZphd|B_2 zshA67e_LmIc4m{d5%Yy&t6(7lEV|maXi7y&`sz4_<3V%BjT$uk$n3V{E20{ z3t?mRcXJ4DwJLDQ`CPcCr-j=;T6$eDDdl)N3|6pN-z5*B(NWI*U$+cFa?27?LD2q0WmMNHFXd8a#yMu zi=gUueqhn_NYQ4i`qc3#uB;1zQ(g>Ka3;7S-SyU(y4o;Ha@&_W^TUO$V?p3p4~K;7 zrUF)~5?N@Cgu~GLOvaz5mg*dKyJ-s1xxjXg;85=aL1DlC5yI97gMt%lQ)Q)7kW$ro z0FML(=w}0C+#xp!%hq@92sysJUP>#@8-*em#RH(vW@zOhDWK2lpQKUWSQ-UV?NQdw z52xN>G@ppir~CwJ-g}tu%3&0~GrqTTWxU(j_->gX7H_cMG4~&LKFAa_JuCbp@Ky$M zb5KOQXD_ndGrF}gX*OLwXTU@a-(4>|MEgE06wM@tS_R^=H*lwvWFI~Yf=IoiC^-z2 zo63@yoiaOtYXJ8b_Ug4jVY7(C1L)y`F}p)%^O(;sl&D~WMTGZq5AxrqFF0ps@!1wfrQZn#TaXIO7 z6LmDSr0-2|F_<2)#Y*vLAr2ADVmY(@UkS^5XKEHe18Tg|i&nnbV#=hmWr(|JS_z}^ zMTf!nN-w0f|NMawAP}AqOvQ`q2(eea(P`~}#z$9z-;^jcDu(f#%SC35z1OV1pD zonAS>Ure)Dqs3%43u_A=?lm&W38dVr9E$*tYeLC#ZyI}P1xaCo3g;%ah|AV1An6-h z${ifr69j2%2##Mv<*+f}0FQ9l)^HfJ<0zHU3HA#1*!4lq1!GFLa{%I){kYZ{A%TJjv`cZqM*gVAr#Zw|nbS&z*w(~if zIKDwJtY1L|5R+qF(mLnP=CS~r++_U7moTFki-mv3U3N;H1+ZF^x!HmBUq#!9g_f=8 zMy6hn2p8_a%x}6k+sT5T)RU^tOw8T^^)U2iex`rrMgEkXhpJ)tU0K$-p|j)P2@t^o z4(gT7+q5cEzmPG#yrHzLba;a)Y$oUhsPoQ@V(5|4DE!g-~wX3(sXjrB+?*7%WoLUFMJpx zm%!*;9*NqzBl2qudE_}R%7NlBat9{cOI}j>x{dbVDhux?6@O&UU8^!K<(<;c1pT1E zzvC__J&@qztWBp$nR(y0P8pPXhnGR9j7!19gH$RcrAJJUT=#tn2JgX(h_8W_^Zw5w zQUgM8&h1ZC(aia8&VBnT3#^sS5^#W6CHkKJ-@AXZVl6|GP2m76o%_!9n-Om>O;yDX z=yqsj+WbvdrUSEV8B#{TjB9X?z=B44m`oa@#`Z4f@Nw!(#I-ebMRvrorh1XhXhKBb zi3Q}N_O07b(G_h0G5s5Tur(-C9e^``t0Gan;0;tokd#>jul0tcjfuQWW?;wotu7lD zbw3Jj+;B!r=;(p6w1Y@lGJzIsGlqw*Th5;Xj|6E@yFdhR$ULO+K*j!b>dFF!T6w7V zuz4dffC*!;u#+oVSxn#3U9SXQ^j>Bif&%>9coxPs)>)&!@t*wUN8Mtp8!*pNJ81Br zWqnepF+orAUQq8Z7{u#&RwnTcsB33Y!w;5;zexd@HzJ!psguzX*^*cdItQ>`gJR{5 z5G8u<-Rqj6!cqzhVEkI1@F!=8MOzwbWARc6FC&bi-W4Voaq}$+g28V)7R-13`k^I@ z$Np*-jw}VlyXL~XiL9B%krG_W%P2)HCu&N9d-PSap@1Na+Qc zeO_ST3oi&cXe*z12?3EK=J;d%ibp6?<&rv-$^qtlL%?8$f6Q1THE(6Y5-3$(sIMz* zJE>rnM}-ASUz4Cmr^xYq^U+8(cSo^+ue#e6(wnIgOmApNJsMAzYNQyS-8#i97eg8i zrh{K-<#=Fm{*gz4ds9ccNl7N#6Wq#7FJLn=S`a84c*8BTYR!0T~pd* zm#@T-zrQeTMnTY9GeU?>N7i}3xiOs^?O0Q#*5$6ovW@%UgRb7L3(xf-1Bg)p`&GRw z7u1emi{bzc{6$;4^R1(XLJoZVmXOxKh2BU77{J?P?BeT9w&&ip7cx2_ps`j5xGc}a z;o(oH3P2p)S@_cO)!E zc32{&iU(X-;EgzvQYP}BfE4TEdIPw&eh+exu6@Q?^-CQ`ee#7PvX!|Wa&T=OQdtGHeY;_e0p_ z*5)05c|*$x<5xY0z{*q!=0c%WUX()f!$JWCY%y5^6?;C$Th7V!`y|2VqQA%i%TL1I zP>;BZlUQP=5mHbIWm9J;N7-rsv1aJ1R#d?_j-{rS{>V4EOqdY(<)J;0kvu(MLzVVt znv7Ra33vn}(FdFm9n)c$@iO9!IE?Q;nj3qfj^Db`GD{3gkk^Rbv25+vOfWm!mxUtf z6gdvClBXVp4Cw_*4y`>PY(KlapJz(eQG$jC5ylT#;DXYPAE)`8HvW;54K#a}Y$>>|Fk1sRtacj| zgFpR>-&%8r=EgX!!GAS0p2(04`C$7$loMaPDO0eezgEB~u%G=6F#F4ke}LQ6<3e}a z$6iAKn(46Y0fx(FWO)z;O6OgvzR6uJ7Ow1`L%ss`QoMtH#-8~E}CzFT=^=c^^|jUWK- zaztVQoaTE`d^$B|5DjfMOfoO0JJd8BdbAfWIO6`bZ#3KseBeI4yf~xp7-<*g?r{;y z0!XB~YixnBf(8R`0buoBZ;hX6FsmHvmnAD!eIfN zgoF?>K%4r~M=Wd@zcnqVRC%=o?b`@n&9p22JC6Td!BRj#c{s=vRm|;`wtg;ZM^h%n zG0<8&h#b_iqCnlZxj+rbWZzTBJl%E${404W5-56_L-3c$Q%{qGzYALp`NSzqT>d$_ za4EjnPJ9TBS*H;ZyH$zj*z?3WDK*GM0<}-IDosfNc?iy}164)$(o~;IWBy(ICnh zLj7&ydn^odC!)q3n{VVLQ2#go{YQ8m($zkyPW-2y00YpAvVT@tfj~8GcBh+jq|tQa zkos%Whj0sHkda3a0B$*u?mKSJ((UV-FKSj*Wm|Q@eDc~pz~~BcT*^&~t-F$kT=Pu|eIi{Tx=6oJg)igN z1!oq%XI3^Vw{%4vVwES3K(B~tD@uu=rYEp2>Anp+dOJts;ABF&!kqL#A1{hHaaF=9 zY+`uwSVN^bU@>p0kOsbo2GC-JO9!Q%xn_F2n@`gqsv(YH^=M#~+wMJh(^YFZvf=Sx z0hBUtwGuv)suH}@cVLmkJc@%4YIlI_Xk>%$HW)TU2^I#xqa<*|uDs`` z5OS1lp0K&-pI@jIy(ryyTuxIX3Z%(<*FU*jmiQ`?rY#;LK_s*QBcK0Pot2LQSAt?V zFMt4#fd|nR%+n@93d17k4A*7Wmj?~wB8 z8La$Zd)fcV^S}DaSUj4HCm!!g7|fN+)@Dcatv~#+_CM7Qo*{xG%pQY8u=lQ2KpdK9 zcp9LWe@-~*|LAHHvW_7~2HIH0DnWdNy)tg>0g0lc0}d??h}_>bJ|nRu2L5fXng#Xx zj&?3TR8Kqy+O`@}8>j9l?Z#Y%COD5gmeLx@*+mtIUl0MY~4THc}#Ye`NU%89}@{2P_B z^L^z91~oV^sZY{Vng9SkxB(bweBi$8ADDqnxFzHLUqZcTctl(SZBn+t zfuBMu9TbVIe{*Fof8&DIk*_Mdu9zX^^bMSPN zDtPnpU}!Bbc0~bgtlUP5$Gmp_Y!x|9-h1jvx=WSFMZPP1wN{aTLk-4TB6+Gn{e4^u z3*b}7B0GU-h&j+7R8@OHM9-_Cq>A@~=+o|nZ_(54{^Gs7Na9bMK zH5cMhavqn8|KcB`-WW*~7b|ri2nE(V1M0bRgH0|YSD_caY(uRPWH|K_1w;lR&iQyr zqR(8YmkMXb-SCyy;wrKc3wFU48E^@XX?p9aDcfH6Pzk3c1Hhu~A9-D$PX9zq?&F>= zMKkz)gn9ur{qj}=Ddhr>YROZaeNONqr10c(W*^Q9KGW{3rVB49(5d3uTnVuL4gv@9 zAD#YJo`60u4l_9^`juKFlI&?k)!nj7fDG)33o9h;rz11gjNQWb<=%CV1L{&fHoTzko&lPu{UgPRacc#EgISTK%9F{0QL}a(;pbDXuKAqwZ^Rn3vEY+Z*S{A6~3I< zAKT%)ZOJr!pGMkwS`BwMFoE!&j*e%Ur&M@b-i|#VLT&>)r-6pg%$Esx>_$Y54-VUr zVjtyECi())^9U;uRyueQxz_V))Ntcx0Ug7n@83Om@CSSH=T$xSnk?5fOLSf~q7D4b zw)O9O#HN}@vuH?`FVDqJQ7ANAL?+Z(@ieFL%i5J{s?P@ZqZ0+-66#CPu>G?9Y~6f*}R3ntH6GmqC? zIEOlx3?h<+tHa|cQ@X2$B6dM4;DQcF@waS}Ex)6@HwX)>_XwFx)`BoYk1YblnCKp_ z4qFzZdl%iAaBv6`=~FWlhL61!?l19B5MlI?%QeP(kt-|LFM7~v=35wQjczu&jZ_S| z=SV=MefLTg5rgDL*jJAj z*c#nHdh6-0FsbCLdO&al3*Pe#%lTc2Vgo-zoXC$3pkX3pzyL?c(1Rh5}BkxR?M|i>Gy;!XT}Eo zL~+4$X8q_1(=ht$-+zhiGg)8Kc2m@*1{t0FLbt!&kf(7H`8eR?>GN;!+RP_P6k|J( ziz{@mETuai-a&a?Fm*P!$88ziy4C%R-#~lJ6(B!5YS$Tlb@$tOPrUvxc;2D*48P;> zg{Ss#*m1eRaXx%DCEKU)_(f&lEb-TY_2)9lMn*wP;FA=d34&3iBjA6%Cf}DV&-lqu zvBaL^+rrEe8Qnyg8(3ty?3bFpi;9z-r6cjEhCU113qM6OQA7$Dp)CHx_@o^Pv_E{{ zK8FO-+qXTpJ4W&wa`;J=tcIwxrFMI^}jEH z9Rxv_@H04Eu{4lCoFM2c&=;7@268IL)2}pA6dnB+Am_gC3|Qg~?HA*^1Md6rmq@CQ zohQ7lf2s12YyC*Mwou$@C$O5A7ez1otm#Wsf8D7fh{ZhTsn>$0#O1S;woCfTZ259Q zHS412W(KocZic^i|G<}g46YF4Vp@5e!6RN}dGJPj4Y+scARK%40w}0e$B*S%9`B(3 zq*65h^OFvpwU(lEdH;6)(i2D-I3%J6^xBPMjg^+r$`gxtrRT6?pFzE9ll$cMDR~|c zfk#)pkh{M9PMTJtsK<;;!TH{O%kYT+eXqGQ^)m_({PQ^C#ImRSAWRoQ6xC1o%LU9! zJ$-9nUGtPCiTO_TpVo*v3%LGdxznE*+*j@lQo8`@@rTYDa@h)BJ4In)?}Kpwh}5hlWoqNUi)EQM!g0shf;F zz&p=xQOn%-nJA(fEXB#cC=4wROMSf27|HZbVi>7dc)x)n1Tzjnz^8CU z@DuJFFqraUjvGk3-9=F0(}{S%MfPj$xuxrgqCB!Mpukp~C(5Fkt7Wh{^5)e~-AI+o z1v6-1fDV+go`}HEC`C(ufwv9$;@*mncoVh{PosF^nMgadUe*($BKg|rg@}H0J#e`6 z7)^n2?x2h8O~}@u9>B*$jfxS2QQ+PBSJChCp#XEpk4aMrs0-@mo`1(Uv&ITSc6`Y^ zL*@hf>5oj)_ZTq-=jQ(>pT7Hh4PtPZ&@3qAU9Y&afHopaVV-Bx;-PcrK9HrR<+# zmMr_B1M2Ek(8tewZ%19p(sxED;@^ucc;z466;1kz(^nzS^4Hke{x3;UDQ*_%>g<#3 zZp3cH+%3wdZO0D-g)LXfEtI>W`ycT$8E9)9=@~x#){uPSr)D(g>HT_hdfYHIwG=1K z@^8hW+jfRKbItl_!nGjEuwl`^Gdzc;f0X?9hFooGwNf5fj3gQzHB13P)Sk^C_So_>4J7T<;q_ie+sxZlFgyz+l@Jd7VaM0G?LB+=`|se7o#(fh-cX){A}*+WOuwlb_@Z&=-XV5pPO zFkj)A#C%l8!{T_X7~xt$krOWChu!l2!tLDn7}|hdNNOQplPpkv%sTwsNVe*1Q;4CC zk$8XkgP{$k&fadiAojD{*Q|-aW}IsnWniNsc}?dUmgV%1zHFY~QT~wMEYS_@OFN-L zp-=oBOf!UQ4v+a`1Awq1lD>MsF+#la-Xtn(V>i>ZEhGOTH01!DJtofI}BNo9!esINtBkBp|-IA|3I`qo$Qvb9TGA$1^X2I zq4f*w<4PH2q22x6ry^;**htEmU`1@{=^Wj{?QL1WI%DJV+-v zLPe0W`C`lPJf~4tFVn%yDqREyx*cReo*V^5>~V|{Gz}+<9#Poz;Cm#^2yi5D{>}f;9|bXbjbA6po_!#|44CaCBNdx3g#HmZi-NjLTJT1@P;f4aLz4}iSY z)aokbP&2=H|0+?yHv+`#oJZ8^bp1RY!&7p@Zx)`khBGnKbToxF4=Ojpnp)olC z0(!1qlkB;W-JzdN;>vP6mjkA48XkGgE&wnIft$E{f-q?MG|w(c)$rZkoxSC zjezCY&c;YP3$b(}VmK7@WDhOqma}=iu{^ZraO=y+Tcs7C=4K^#x+gw+^=Y~unx5zV zK7k8d$84#hs7*-E{PQ7BH^ZVHM%ek9gQOu)XNq%(1Sz%4tDbHaCcz@pJFxeXbwM~5 zI4|K6=-1gfZ9x3S|J>4TL{M2${)i>UYo2f|*DG6%*94=|bJ>f)of}nD`!t`IU%!h8 zI1;i;YKw(g2;Yq)TS$lFqW6~x2a2i}h}hUwC*e*QN>h|>!zc1KZ%BA~p(R}>Y7zDg z54Aj_jWW@@+Jetvcm4f$Ri$j4JDXkNoISP!Zut-J=Ocj#U~hg-NBmS1+n8rKRA$Tf zQ8-ILgD=>+AAsUx52gf1_D~VOydm@Mkol+HAX2G3E!Bl2sh(N~(f8E*E#%8XKV3+m zz5VpD93qk=KTubFXgmJ7T)SeY0@nLY%;YR+Zpgg61w)L+1)LiLGLV=!xEbXZe%quU zv$<*}z={|UNu5scj2>nKPAcPV;*6hU5pCo^`gIqdqUT0POK-NX-E=}{ixNIxf{rp{K-gMzuWj3OPGm6^E+z-zppW)L|ppE%em8)6&u8Ldpu*6aSH`& zwK8GRWysV0#FIq(Xc#3p;tywGF}J;n7Q<>+qVbE#!`@s2lt|BkZwGF?c|n$6TNo3^ z$Lf&OT#fhP1{02xXy&lctCF=WLF3SYHjZpxU_JhME(wc~f~NoF`*W5P5$)AO_q9Ic z-ZX-V^jL9P%Gb!8m%{*Av=ER@&zu$&dEo!t%@yfUsS}0U$s~w>*GVTQ^Yw~DYQ7S8 z7<<=V`WnhT!AT&@v2HBOalbQbS)vH6!I{VW zx?npQ)s>Uu>M4%@Qs-xBxqK}VJ)JvFf_Ol0JG@*S0uxF%AuYywc1rFSrdA;5fwwle z^3J;+tGU|ykYpthK6r=;T+VHiLT4jze8FcqTj15;K*4ZRnqGTU7mz|1G)(0)35vR@ zV=2M;$E(yQdQ_>?o3}>f!sq8&7J(rpI3Kjx05N5+Tl1q6tq6#Oh~RRG?27d_8#G$l zJ+4)VSrrm=eMl7zQ;+t{5fu(%I(tgaV~o zi+o1oNFl*R8Lx1A= zVkK9Mzl4H7^A^mP3^;gq>&|w{d6E3Qo+A1m73ulK%0;se;Wjt-5OY)t@jP(#hw&(o zr%|wR2JCQekRRhK)F~fy^Vs6l-3!|{;p~J#$H_Gdf=p`(SnVnp4rQNq8Br~#FR;Fl z@qC4|g}KTfs>LO5wg1d(t7$6bXBsZU2`NkBrF6LhDEdC6>SPz89+IB9y{XOvl zoHeHD038Qf+|2t|j$-6pzQ*@>tB(nbrq^~-avnAyU5XhM17*%z?eFjunAQb~9i~Mb znW(04BiEc*$dn&QC!ohK(wF|4Fg_D&uHKc+g*k=>1I3m zeXEaQ*P_feGH}`kKBj8HKdyYajYM7f=|mKVeS!m%57Tk1utC4%71NO%+LeCYXL$L= ze)5j*EX8zTaiqg<_C%&}#(c6C-oXa}`hUBlS}MU8Ox~JUDh~q~Y<_=52WUa3odI!LL74JXExf(Tb()8{gxo=caOe z9G`2+#L~$ehj?0h0E9F!qAg6AX>jiKWi)wW02aoxW3E9h)v zj-}+4wFK`Em=9{cdJye-stKO%al`}OaBR^~^)@tMZwfG7oB2_H{nodr$iz|7YuiU> zF2&S1Z0dhnlpjM@8b^rLnQzy9f+bK<_UlaXAHU?;=rg{`8 z>^~!zN^SoHw@!wkj+Elc-`c3|LaF-ue@(xUCy1zsD&r;GhNkG9;U{295}~HS$vc2C z#0zEN(E)Oba~E^z>&TpG7?dE%8(5L>v?B`a65Vs~T5-~zCWJ2vd9%Z$kcXrbNgf|3 z8Y;wuq-bK$$)~SwSCD;Fmswu0B_$`{5^u%ggJmg15|Tlkq(g4uuIeZ_mu0z#S2!^= zHAUhZ)6%LdO#8gOF%s&n>No{43fjqlPrI*%RDD8VuJ};gOs>v|>puvC$F(juTD5gu z55x%tu`mq-n^BI6WL<1QxPlYG;oq7ccv7Ex1a$vrUjVMO;*aF25uy{Sr+?8SjDp~y zREv7=6tYyUfr3(DMhJ16x&*gY+%+$)#$fg~M z`uLT0ZO=#}eCiLAiCDKR-r$3N!IL1_1JJz;iMiCJ4%o)Cl<{tW&AEj+=B!&*a@5!mie5p$S literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/android-chrome-512x512.png b/apps/block_scout_web/assets/static/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..35fad517bad0f8550fc6e39099650865b0fc3a09 GIT binary patch literal 64101 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Ƕ^A;S={*M>eT6NFypdZA> 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

    N$jM4PnRuvZK``R`c}}k_}UF= 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 + + + + + #da532c + + + diff --git a/apps/block_scout_web/assets/static/images/average_time.svg b/apps/block_scout_web/assets/static/images/average_time.svg new file mode 100644 index 0000000..74318c4 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/average_time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/block.svg b/apps/block_scout_web/assets/static/images/block.svg new file mode 100644 index 0000000..88aa9eb --- /dev/null +++ b/apps/block_scout_web/assets/static/images/block.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/blocks.svg b/apps/block_scout_web/assets/static/images/blocks.svg new file mode 100644 index 0000000..759aa92 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/blocks.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/assets/static/images/blockscout_logo.svg b/apps/block_scout_web/assets/static/images/blockscout_logo.svg new file mode 100644 index 0000000..2ac7cff --- /dev/null +++ b/apps/block_scout_web/assets/static/images/blockscout_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/callisto_logo.svg b/apps/block_scout_web/assets/static/images/callisto_logo.svg new file mode 100644 index 0000000..88d5c12 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/callisto_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/classic_ethereum_logo.svg b/apps/block_scout_web/assets/static/images/classic_ethereum_logo.svg new file mode 100644 index 0000000..0d2a8d9 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/classic_ethereum_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/classic_logo.svg b/apps/block_scout_web/assets/static/images/classic_logo.svg new file mode 100644 index 0000000..1187479 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/classic_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/controller.svg b/apps/block_scout_web/assets/static/images/controller.svg new file mode 100644 index 0000000..081ed3b --- /dev/null +++ b/apps/block_scout_web/assets/static/images/controller.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/cube.svg b/apps/block_scout_web/assets/static/images/cube.svg new file mode 100644 index 0000000..b4bd863 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/cube.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/custom-themes/circles/balance.svg b/apps/block_scout_web/assets/static/images/custom-themes/circles/balance.svg new file mode 100644 index 0000000..3078ec9 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/custom-themes/circles/balance.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/block_scout_web/assets/static/images/custom-themes/circles/copy-circles.svg b/apps/block_scout_web/assets/static/images/custom-themes/circles/copy-circles.svg new file mode 100644 index 0000000..2a4c168 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/custom-themes/circles/copy-circles.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/custom-themes/circles/footer_logo.svg b/apps/block_scout_web/assets/static/images/custom-themes/circles/footer_logo.svg new file mode 100644 index 0000000..8b78714 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/custom-themes/circles/footer_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/custom-themes/circles/logo.svg b/apps/block_scout_web/assets/static/images/custom-themes/circles/logo.svg new file mode 100644 index 0000000..ca71ddb --- /dev/null +++ b/apps/block_scout_web/assets/static/images/custom-themes/circles/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/custom-themes/circles/qr-circles.svg b/apps/block_scout_web/assets/static/images/custom-themes/circles/qr-circles.svg new file mode 100644 index 0000000..eb21a05 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/custom-themes/circles/qr-circles.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/copy-df.svg b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/copy-df.svg new file mode 100644 index 0000000..56f7551 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/copy-df.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/dark_forest_logo.svg b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/dark_forest_logo.svg new file mode 100644 index 0000000..21ffb9d --- /dev/null +++ b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/dark_forest_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/pic_balance.svg b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/pic_balance.svg new file mode 100644 index 0000000..cfe8f39 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/pic_balance.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/planet.svg b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/planet.svg new file mode 100644 index 0000000..0fc63f7 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/planet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/qr-df.svg b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/qr-df.svg new file mode 100644 index 0000000..fb13829 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/qr-df.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/union.svg b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/union.svg new file mode 100644 index 0000000..56a4854 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/custom-themes/dark-forest/union.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/dai_logo.svg b/apps/block_scout_web/assets/static/images/dai_logo.svg new file mode 100644 index 0000000..9c6539f --- /dev/null +++ b/apps/block_scout_web/assets/static/images/dai_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/ellaism_logo.svg b/apps/block_scout_web/assets/static/images/ellaism_logo.svg new file mode 100644 index 0000000..bb022aa --- /dev/null +++ b/apps/block_scout_web/assets/static/images/ellaism_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/errors-img/etc-block-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/etc-block-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..ee65aed7c05c21ebc371f866cfe57a4e460a2480 GIT binary patch literal 11447 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/etc-page-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/etc-page-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..f89a5a1be073363327affe06343b7c3ea8f66053 GIT binary patch literal 13383 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 F{2v_ZJ_!H- literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/etc-tx-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/etc-tx-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..f4874aefa926b14ee4444474ef2a17a88dcf779e GIT binary patch literal 10565 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/etc-tx-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/etc-tx-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b8305e5eac4a831e107c83b9408db4c44fdd19ed GIT binary patch literal 21965 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 M(=mirYCFXLH@4P{r2qf` literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/eth-block-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/eth-block-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bdb96bb90a6deb69655fa0fbfbed5085a8f118f5 GIT binary patch literal 23984 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 t0cj=A^3v>+B$8Yy^BU=$QoTi`by3j-MGPkHki z1w~LsMPBB$KkDuRy7qI2#oi-(*oG`+W3N3$aIKsZA$Eov3F{0E8(A? zyj||H|8)AeV+~3iz$;|+(ilsqvn(mwbbhbl~xr>t3BzM_zQ?zwsH7 z3vfAC@{G=iesZYs16o2UrRJJEOj9cE_jja{84=ViPaqAIN$OyLSz7K|##QJ=Tu6#x9`4jsN)l5a8EY*Nuz0oE`S9@W)1%I~4Ok$W(+!+e^E{ zf)P%P)`euVmF`rF_k>Dq9Pb3S-0B*4b4wZL2khU`cqU3149e!8HjC zI<&8Og!{(3E^!~Xo!eJp)~)P|0!BHOXcp@Jk%6KX-S%6yf1DmLj*dE`M9O3yWAp7l zqP*gi%X9UFd&iO;(IOcd`^l_K0n|~oi;(6&^mtY@l8Upxc{sT}VHi>T(ohQIt-XGh~BMndS=`rkZ36Z&0ZFAnWcgyVQJgQ1{9eU_PGJr5ZG%B^KTz_%& zIs`vWpTw5XmR_;w7Jk>QA^%MNSyVFnj=TliBgIzC2xDDZdIUW}I!@k5IB}+o8W0X+ zhf@gPK7KD{n$qsE!-@X8N)60p#A~42xo_9+Z2aVhp_@DWBkj&4JgFo-2dNg3X!gV>z^$v_x1XKa0)HnhJjX8; zI`xY?zDtZn)xls@gBrx1(I3w$3GCP-C+6gQ>`ihHyT2(|NkiZD6C)L@7B2f%*RLVW z&`A5|)pg-%n@#fk*}tm(WfTmB=cJDY-{r05brKT16eu082Du#IML7S^xu#j!G({)iqizPTyMYd4YDKxJIu!j0jByl)7DH`urVjx;`=DeS*n5`YunDEH9 zb=)zVL5aZ<)O4lGXD}`AEp4AO&NPW_A`se8xKW1T!!(-hou>Mopzc&!qu=^RXH1=T zEJEAmr-`;M`R%Vd)+8;gC2j*x5@PlP_fB3s+MQdv^Sfegw_^BIHd9Oc6)rH=^MPnX z4cRotE<{zo_@{U$+bIe#4|kkMhkTl{;jVu+s6uTn3yop^(auCnhGQ8tUz+AQTC_0d zoq2%yig9mLtToN$I;OJK>L0KkM{wV${Hj^^_hPRQHQm>R+CTDaI7V5l*X{D@T~ZaD zHPk-p2zUhiNa@+l32goQ+TN1aCg^cnR!TK0$fAWK+&pQfskOEu4j{t%{nru?d&P!3 zMES3_Z~@WtyuXr~02V$}!?DX>`$g|AMl`lOt<)cfWDLi?aqY$3O(5b}*?!9HRcuSD zKEh^s;c)9YAFXfy<$jcsKumjqgc-3G$By>G;jQn=qQhL0=E zNJHU?p3noLHKxZ%Vw*VKh~q~H#~e{S`YQntl*sX}`>gt2^2w`Gi*5wICfc9Kh%U9z(o*t>k*!^ZarP5)T zsELT|4tpf(l=~B0EoO#qJ3Si-=1axYk$gR$tpVuIHdTiV1d;F5Jqd`<_`-x}iaq0K z-ggEZLMM+B4sC_$?20(1xJ5;C6iGaWwifOv}pyCP5R1@TG%|829FPtC01n~7pCoygaC@qI;JYmq0A=<7|B69kBPic+SiiWd*1cRDrA&fiUJK#;`w;})P?7r1aMj; zG`;B)a)a;UOJvNC3Q(@e_}Na?=R@crd(pcozqpdYbmu!uZ!*FJdSZzx>)dWzsxh7_-GVUV>^b}X8hYIuCqF;$KE-(VRb zo%1kuujuj9s^0hEY_qq}h392vdVi(_1$wa$`tE&~ZH7MJZ+Gm125}l^?d-EtJ--s^ z;1k>WMe$?Kiq#|luIl((HVtk?Z}-RfEsI*Q$Pjj3qZNO^jPrzFPX>%Jqu{Nv}FDw6R~I!x*i zUECfv1f5CB*oC{RnZBkI^C!R=ENOi~mDnc|#8nFp4<1fpu2DG00 zh7>%w%k9@lSG@Z5rg&@YBDB;{xY{Ub=I&-eVe^6R0?y^KME?8;hptE+D%!W*g}-^{ zMUZD^Hj}fzrt;%H9+^u<5%c({YfY%#Dv+9JEUo1X?^LWILlO+CZCy{E-yYu0HvGDm z8bOq~{V|zr)tld;NBBEJMSpOALakxC+G||v^_06GQqpvK&K}BcxFme~=5F~Xvppnl zA@6k_)#(BGA?+1Oc>H%IBQ-TMsX6cZle|hQgH6JXC)Bay)R^Z^twaDg2C*dbjX*PZ z4^L&kIpmdpo97zh{nS{YaKc-Q;^S^5fq{lU;H|aNjlE-jK1xx97{TwGp@wE3KfBii z*l_WI6BB;4DvBJDOL0DWgf0@LRkqT^pf4^i<^|nB{nLYreT_+^=UupPLR|OF2JNny z-qy~`>6gbwe1yL*0upDut~(A5_4{2M>=`vpd;5&$RIwUoTNoZNrO45WB=qpVYQ>&y zJ89YI*$GDu+=*kqOufmB6Lc(uO0ZGZ;^7J%=eKv15ob0Eo^8eVlbLC1-<4E& z%VIiR6W7Bf0ToMEQO{r16c>u`T~CuX*xa0z2@2cH!GFK%TWvH8>sOUR*DH{W&U^7uZfnr8K zcP{tJ_b!b$8*;Yjp&zcwuOh{oa<`Sw_y&>3$spPdwRNS{X-~xl^>j5L4zwnDEx7#< zVKX(?J=?$Z#IOxl3~n3qw?#f@%MV|ZZ35YrioxMLrSrN|PCm@46AKfnEW9=ocqahtJI*=A;WPfz1B3dCc5`~*p{s>7?b9&_ zVEiNo%g{n+!ktKJ-wg_Mxpusf0|>3d|z)90-@0Rqu9-Er=%4G3aS9cD@K3sgG67!fYd5Xw6?`bJxD9%geHW z8i{P)X)|tN<>Hx>^#Ow}l*JxsRM5IryrfOwWbA|9zra1_TRQA5b&rw8xdBj;9egF%VJQY{!ZYx zY?r_1ZnNrD9r?2}q4I;cWA&n=wh?!sXkpE6Dc>m5yt(?@OQVAwQt`!qsv4J%R!29x zCCI|~Tv1}?KCKQ!3)z1>mYcOdCaVt3uXf;mm?N2Gu}xcLhVfaL?;4s&q#^@>|iEF!$HGt#j~GNek+gtH}8u%OL)hBDSW z8kMk$on&KR9se(dxPHEI-V}`CjLllKXSl!B2e$Wjc&*ZOFo2yLjebstb(k(4Cx1hC zMpRT!Av6zqBEwuK8f-`qGUR2o(<#G;!Wsevg7S{diyE-$VvzKmR6x_Ap>A0(B|)G% zZdh~ze|WrDcfuz-4ke+q2C^$^V>->nWDSR;|&qaBPB5)Agy19C-0e=8Ma+PGPN9oktgz;(r7%5*d; z?=v6(^0@{8icb=4PPdttbulJ4>*q+ERObckXsfBCz;S9wtz%Pw8*6CviC>9AC4m-E z&54&2LYErD(_p>nc|16ZM&%ffHsnc{E}$y(g>R+cuB0)8Bxl9CBOgi>z}d&6BNosa z&6-^%1DqTUc))zj)m$*@DTyOWJjeb*F{b9dAaCQU3l#+v5 ze&RD*8&JgW!B^Z#OF%{j3?Djt{k@R+@+G(2^JKJRmS%a}u-^;_T%kEGU&`=lf_b7- zglYhs5(0?g3SIAvb}AYSNePbaO`Lg_ewU4V0Ltst+fWeU&gFW{>9(a+uxTjQ2q~b9 z^(`hf7a4G_y;~00=?+4kR8nXW0+sm8R{Ff=?i4&7D0o?EQHlnQ>ri<`<=yO2fRY|) zEuct4QGps8AP86*1*nK0hk=R>82-yb&Q!JU&z&*XMu~exjEgDy@Ce^lq*uvBjm5X7z&5B zoz0LF=%Olcj=b0X);Ia@I@n&H9}_)y_AeK>Q9gfDHy1$POm;s6hv=OqY*m!fIh`?!c~TWe{)HyKVEE zp)j#yVB3=pRe;*(iA{!)-7Ow!tNrn`KW8uA1z_j8HocN+VjW*2?xF7Pq6W}zNYuER zHoai^r6vMtzIvNH@ABZRYhKpN{_tAe6eu$>cyztW*kqy0~%U#Q|x@C{Tw*zBcd=(-MQ z;$Lv=Qmj*WL*;KrgJi81zDeEH@32FVN0pj@l_LMM(!$!y?rQkY(ZRUGoBN`Yhj7KAJSuGfQeDA#Tx&b_Etm*R?*xkWR%x!y#kP# z{HYy^w!&rqPn0KFak`GfmuxpWnO$ie_CIeuA+K=_cDJ;unU5?}?416e>mWO#eKbx@-0}D1dq-&!CRUbM;%Ryo^Lq(=TY%i zJnO}=sdA5PAENdzAC$6zgU?6bR`3I7!`{~!k5kBhf=b=^n=MF(!d8B#oaMmtH?H9! zMc=CaYA?*DyyrJ0;K-4i1I5^VKqtu~Z z^W)emw)Z$|VKdQ(lX{UAIa=FUZM(?oPB5PbHgM=Yw_p%fPKoGN{53uOWLQMT_XXCJ zO`z#de^`b;CZX*=cN|x-B`Wf}{+H)1Z;)@2XgX%ROJ=O09ZYr{fCs$`t-3X&sLK^v|b)nQZ?ECB%G%4I($lhaezGAeZIEubaq|ht~ zPw1nCy{uVv@vg`Q1L9$1(MU-+fn`rzE>`L?BlG|MIK0Ws^lP=x%3Pyd72*5PIkGm84IeY)$k-(k$#Z4E4;tDe(oye`a$XS3}=Y9R&XbfcKWtE4EQS6ph1u-hzu z*CKL7Kk4^=U1uXmg01{<^fG!?qwVJMcFl{6RZ{6Zh=8>TqT@bcMjp)Vqq6+zv|~Ge zX%o@lTn>fNYM+%|EAKn#(cp+QHL5nk;!ID*o`4sflb7?uc}mYl&PoyA2|Lt`wvDbG zR(15|1?{~9(XQ7auWU^QW4qg3K=28NefG3@(ipw1^D)|&RA$GZ`RmQQozsyH|1dKI z_@Iz2wM|nmcO&N@q}yj692(RjFG;7_CEF@c@U}>H#Xpyk6EAQ+6%^Q8CdiVauzi8K zZoT~t|McvTb`>MJc3LGmU)RT6FF%3^;^o@i2z}VF@+@liY*2bi zE&SzP?AIPEoP zEG0i4Fo>DJ-V$VF7WjFTQr>a{-bftv7>ywC7YdGrE-6ed^_7V~98t*1a9WoJ|$iuCNkX;=$bd(jpjg@IX_Dn~TsQ+*;4z7XYs4?=A$ zdY>WfUJ#l5rBw8a_eiSs<<;-7wGVGsN?YC!JbFSg$R6N?uON<>~o5u^b zRP*Ls%t0=$(_3X;Mt(M&L}Fu-0l~F*`B75QxGJ*UT;rJ3Zk>fXRN|`=i)vZvyY@bPTm_tzfXL#byBo6 zz_OteQ9X-ithS~rkBBVgxL4OpeA0O`OR6_0(nzM%8XS_XiX``_ZP113ZkjneC`VM6 z-k|{za%RRM|9+WoJ}6LlkrY#yoatu5`Rww=81<-U(Xy+W9HTeG<=Z^iZB)5bicZ%1 zX()$Fg^w7}E(WlEAz%`#r{FAvS4x>RP!& z9HN0YVoQJVLCIO2ySUM#WXl5Mu-m%OKH8_U&2xn?Y=hg@J243FTH$ecXVMsTJQMzp z!)b>QlwSA1!>njhwc7*K?=BiU&RN@|OJ%cqh1oOUyDh_J_(&_bC z`Rd_|n2e+X;q@IuRmuL+ScYw|Gst%t@wM0rrj{%}bhY%3L6(9~O4g6K{iGFI^WzZX zSs48U^(&SQob1sYTye+xYf_V{8i~DD+QNBWF3qGDdX*$@0-S_EY7wq9lIv;ww6nUT>`zb`omcul znW_KoQto*HmhAp^*5$ZEsfcjk{PkCl+yE-Myw*FbSoeZl$8r@NtbN888?){}UTM0A0Q}pQU z+Fbn!|7Wv{2S?&3!d}1nV_tUe_4%l`-uSGR7?0P*qH)+#o9NLiD9j2U7n(YBc999+fxWAUrjXkZ_1=|x+v$oluZ|AlD+7yOFM3=ph}?^ zDbaSeyy<-{D?`mdHL_L~AK>djG`<}7iT|D^og{&ER%WC3;3*V~Cise`F6Ef5%rS{x zq+oKN(`aVy-f~B8D(goN`UdOLw>qaBD12eDlLIszxIa+Qy9-P*L%a7jL5zUO7-Zbt zMXX=y<-TpNQ7P>_$KUooANvS_?aH{(rgVe`DKUCSlDtz|fF5_G(5o+m=Q`d*I#5d` z5z2s{Xt}nh^53ghlohdo$d&2d+r*CkRSA|{U;2}BJgrJ+@)PW)4#v1Vp12+Vj@ zp1HA!zB0_ez-Z`Yv~xOn>Pvo&b$PmwqkJW$OvpM*m0JZ?zKG9FyO9g^zt<b+DPBj}+A z+NW&O8+_QRqyCsmxO>pQU%-pVE+vHVs)`lyHDL}BJhkOe-LVv zPalgUH~Zd#Y=O%C#@XAtI40(iva_D>>N z75H?oun#4Rt(nMa=bV>3KRxjX1Bmw%^oWm4E0@(;V|85kOZ3j_q=tG8lbp)WCf|}< zYi6v-gbC-CMd1x-tbV3f&mda!8bL=JW01RFqguN2e4y$>GzAmPO%v?s$=d6H&=IiI1M- zCZUONW0ujj5mpMZuFh&1p^P9<^0(?5Er1z*Kzzaj%>oW8@vMtrzV_0?ksZzMlyUkCtT!*9)qu-!XIS~P- z-Cc{qi+@~-U-8cBEcjd`Zi|_{;K1TOT=7%8N>|RHjzJ35R#)#)Sf3`yYI*lyqyS1#|~H6U{GNT(w>{ zYY?`hg73MA|50Mpv)-dAKAhO9kdeYwYUGnLB4k}|qDO9~5-M+>(S9(v%uB11k=ilQ z8!lCRe*0#j`a5M!h>Zzh9swwyjbU_`M$jReA#pc&l;GhSBHWZ$2|H*dlOyDoL0A%R zSc!!2oAh=GPl4P57hY}p+DbIf5rPKpMPv*=K#(Y58WebTiyoc*@O6KcA0uyj|7C-buP1 zm~J{+M~FZj!r1)Q<7N|_?S&Vc=adQQk*L^S8gSu~PbD`*Ee>EDt{$jt@o$BZ>?+th zZ2xB2)QDy~EKs@%A!&mcqj&XLcDx3Np5=W#R(g(y^$$GR3^hd3G4~PEmi5nQ?@!eW z!@n`C8~uc*Kf}IRi&DQbq^)}L0<18cwbOTw^Q%|eH^MAmel=pR)$X-vyN7f!i6xAD zUg_)W#)LGCoz?M*?;!MzPp_}7w>0A;ni+6G`OHEQsxA`fg)oUlCzp#EM(>(Qj!0-P z=79BQbZe*5fv+hsE7t77KUI{kAj)rySu7gzmQ*&o;UI=Dk=P#;S>Ud@Ojr7g;m!3Vb=9Vv(w88!iy*+63TP#W;P zDzOBE!PlmC3$?46mQZ^Nle`d`!t92e>ZL`?OW`+$D&6j0R&tlB%2%(HGp@dWh`Mud zNvho=u^fn&nD-yNJ|b#q79}&NIH@jaDRdi^rS~ULen!S-$x75T={-dC&w2~ghoP~X zs&Ia!0pG=8aHLEGfcj0eTmKZ#nD^7=LaZ(wOQ&ArMb>!rK_nE9y1bgndq*pAlrJ~N z+SCTok%A+s z5S%=2)nq^I_(5lqwR3Xv!tbYoglHfK@(61oj8oQNn|COA*}|r!F40%hW%nU@Q?7uo zx8Z}3gtN+Q`E=^CShl!N3k|n;68{=S;E7jTUUfHLt0VZ@n!rK#Jp`QIaET8j#?ONfosRbS#^e5A^irQQwmV4P!{ zT5K|LB=jI=^vESc(V6RePM!Y4iKeBKZ$BP?HeNkP&aD)JmBf*iCRXO=kDysh=Zm2Tpd;v=N(z5h%^(tu~kvGhbAf0~zQ>svZ2h6T-l?;G)t&i#N z*@i5FoTp^yb3c2iA;yHs7f}utwGjD*F)QbsCs2xFGIG$(O8?VW=KEN=?J^A35}|}x zZi22#)G)UfxGN^&V8ym%!zAlj&VC%TmSHxweOlZ0QJch3BjGQe=xeH+&XHl2VnKWb zA=apQW?wmxT5lFThy19xbmZZwAz?%px@&kFSZ2PzFo{~6f|4qL3T(-sFA=39^p+W{ zcsCu4hhM(>R*<{oh?Z%mGu8_i&GnYFFt&@R@M#VdfN(1;AzlVulGAgg#lS2~51RN; z5)@}g@D0dPBSRgvOJ0OnGZ1uWGTBZfYDLpG%&UG|OcIHa zz<|Y9K>m}_P#wB2QT$2a-^S*T zZsBNF?#H--yNYp)@EFJ;eQI}urqNF9l0xzx1uIEa!{TiYIbbzXR! zLnqIv*JhSw@h{i`f^aE&7TbyRvhAdi*^$59usoH_2cJ->=|cDKpKbV4`28Xh&QOMj z^Abk+ltoFav`9X7tF_I63NvqWO` zdVipv{xDgfStR)WF#nJ#9ZJW`$B{n!MPOIYD^FKJ+%5Fx&na z9pCXGj^H!@67uZ_m#x}hVy0-um+-WYfc{c9DDdu@$>caR7TbzEv0m!JibGFx@3sy#~L?hXav}1qHHH$v&sU~W_5#96egbOV}-$I+jOXLGjNe|vZfojrR_?`|w z6;MZY(m3(S3gLaXqYe0)ruFs(xIHu|@=$DP2XFMC+c~j0e$-`8 za4TxboUmoe)JC@??%GpNfj=Y1eD#?GXr8VXUxo?P`K)kW{7WkRWmM7m{CfwmVn}^C z+4#i;tPUuZP+)QTLyp_`zdY%ko%J>VCt;Xh6ts^^Bqp=v1nIPX zDwABRXHn>wkBiCwBl;`bh#hW9ZC+=Mxv$HHSNo4vp2M+SChzws79lX?S9)Mm$#P9? zlpm)}eV+KGC&ApdA%S1V^M~4hoZ&>!)nnz-=S;L4W3@o5!dW}2%N0-yOkJ2PcGW-@ zEgM7QHl^c{mmc9(v7;;t(WBV1PeW_*BixlU>m(H?U*ceGdF6?vpM8L4URQrZUB<9= zjL;;k?o`V$2Wrx7OTCV&a*d5}8i+ngtHtg4mRA6DFVF}k&Dy6N8qhb8iClSXLvQ zw4RqpT;UW|wRE^?H~9s>#L*+Hw-4RF{F1FaTMqQS81>|EQ10%-i2E5gl)rJ<)^nsy zbM#cwkLk|X@pq{ddHj5W+hDOc;F+L28yZqr&Wj$QSga;Y;b)e746c;@WE_c$R{D=e zxF_=Ua@onjZ3YO7gz%lgXTKEXXJ=nSyJuiwU-O;lS%C86clpemOUhrt?D2e@E^4LV z25En^nrQmnpG^C~eIy{0Gsx?^59MO$VV!SX-FnghdF7Jw34)o%b<_5Dy6;HvSbtJS zYCI_{Y&(Jgf1!ddz%M2TurWzdPPcEYVb2pVgG_4kx+2%RYOdDMSlqL*eo6oV>7e&Xl=uGmHs@jENDACO5>DA;9hMM^vz~5 zd7Mq5R@pkL$_u8`tGruVA@(3KO{!5gT7QA{zR$2kqJ3{Yy5RY|B&iJHTsf`oS;yW4 zstuVxO79V5L|57RV#^bWe~2hRfPvRWaU!1h z{mJuUHpY47#*ubYfR<7*cfJ_Z!9#}LWGxq7M&c_0i>2!k%h6mJijku!Y^u19Hb zPS|Oh$#W|vq=_v;XN_0j``QT%y6BPqYl2;kMK?j?ITHVA3A6-#-yNy}?SrPz2c8QI zJq2$!nICfA7->bTH|xHh=_l1vDKiK{kcyXSGwSYRpgmuqI`MV_4SB^g;XaP|l6}l9 z==>+wSoNu3#+wWr7Jj68W0sk^JMe<)Yf#&YWXIRoNnrUKqfbPk8>}NL4rPJE8ryN& zF!|5V^P4{C44+WDfUn2qZifRDXJw<+Mcd6jS@tyd$r;}osB+PICVPA=12gI-AayLG z!gG+cBU^W40{b3m;kY`5z!JHoJ@^S83D2@#jJ<*>WDU9}SOcwDV40RBu|}^jvGfci z&tlyz>|&5_fMxsn8}S|XWRMc1oWIXJ!7HXh@G@NJ?|{<@8(y_%%-XJP5(f*g7hK1h zI*4+OIaS>&TWRojQMwafFO#dl5HlO_9?AR{9Y%iS))S}MK|-coYpBPzy63wA(@l&z zLH|LLEV{P!mnbfhdfC$`-%GB3lhBU8NuxI1_O_{rgmoROBY^J!pgVzOicjpp9G zxnN;)IKzLA5U&$5q7PIzGyDB`>Y*hlS_zE(ZUws|k;#8~HC*_L4eFt4Rv0IE3zSE| z7zd}>!DUYga}PKF%HJet36i7EHO5;>3Hi|ZIZo&v)I+&5r(=}~`!)=8^oWIl$~qLv z!3_0K4{esiVT59RI+EET#R=B>jGvd;$lk<(zW=p8{>o(Nm^{-*X9C15%vEWMAG2&v zyrS9>+c_KOE_oYP-@hn;k*eju;=-U11}&BWuk8>q5KzC?z{%CWhKgQ9j?!!L1iL$H ziwHGwVkFGpS{drx?ff3+-b@9nPg`t@!@{)!J)rCN4PldmX+Hz6orXy~z zuazz}Q)xBJaO6dWB^>Ga0gz;Luu+|h?Q!mc^sst4Z-YMA4Z!tB$ehlfaT}aak6j`? zJ>X7I7QBKDiUo+!XlcoopEfuby9p=@M)u0TAOgeyjsYkFE9hT`w}0%<0(OG(nhg4c zaAm;QfJi`woX<(j8fG72B&38Cv%`=Z9VsXe35*WyZ=RoQR+u~d?>3jPlFxsZ7mk+E z3ekI&51Y+qM_&;kM3xR@X2J*n9}b1`{4|7(i*-VmJ3hy`(@?|e&A;;b@nUGTfs5cT`&O{no5%XmkWyZ> zPAGF552I#YL+S~*{N!|tJY5dv`5e5K93n5tg#9@djTqa6518Rw_vUxDSxQaOT2Y9G zPN0qQ?sfbPe!+-iL|U z4iV)`_f<$3jU3;{ySQj#>v~(EFUi#1hV5UJv-_JVMTfYTv7XP8@?+v@5{19)D@@!< zgf9;pcLlFy6Zp%&puk*W<5U&yK9QoF|VJ3kSFgL#TqP{&P{-dPw$03+l+Lq|*mtj$@OtKgaA z91nb2)J;V7!70Rm6LLQA0mYc&z2(Ihf->Mh9&{9Bz%F0fJtk{~fCX_&1UiG{>H+Fv zES0FRQf>Te>E4YHmcQ%vW*Hh~0hnAmGZcFN1dMMb1BP5@gyxjlf`Jsl2!<$tA$aJ7 zLXHHx*R{NFRFk7dTY;JZ2u` z)G1(B@HM&rq6TTU{iMNt1A`uY3IXXKWoQEcQNkT{&Q5>zzxUq%aDcx*CmGOEKKC&1 zuX=|gpn~8jKR?4`w)wdIYLA@?-t46L7s0i=tWb{+lr=RMrB@#8^HV@EL0iK;?lQb_ zoHKuzjOXo!2!OaZ4lU;k|w0vla1=TDIFkq2$^e?)|-XG-SoCHm&5 zK!yyH(vu6{w~?d4w*95C<6(Ve4yR!&U{)M{!WNTp!m}n-#e3wU$Kf7=X8c_NsI>oh zYHd*1@E~@5kaCV;f&!-_M}$!x0ii4y8y8{3L;vEp#TC2H?efVd%WL_YIR@E1TH7-4Qn&+>Jf#)SyPVk0{Z!ZUbB}@1< zZf#BRNY6GYfLxl42_QbDrp}h< zbYBM?M}5~GH>J_UnY2re8pwbo0z_<>R!kN*?a$NAn>m}=RNR5Bv&P!-5741!gw~1K zm@X#OvP2&-Mq@gJVMYfpEN%gf53LZJRXvXo}fudC-?M?<}!V6q@1Rx)IL_ z1zaEH$U9Ff?yaShaC|}kNgMIeb*Oq^Y>Kt}MP4SlU}?P3n7>F^EDfX=;fnqK*$Wno zTCcSmDGVRb%s_JoBuN6B$rkqB=Gh6nA3K`vzWVXTdb?=x45k45s?~_03?*yv@3mN; z;^Xr1o+!a*FoA{+^OOK(;~HS#y$8Z5`73rEpf#DF#WSz10|u$dq!EZE%_RY~?mD^l$B~uj8S&u~{C*iB+nsuZR7tA$*aMk$)K3i#AbclHMx{B zb@$*2Mmosh{7?kSS1K?E-YTM?lWbr;YIZZ zGVeu+i`Xu#P|H#ODg$abXX_V=RVu}p%tR^3_E(7ranHV7_UFO;qr5=r674}##mRps z*SxFZKM2AIbFz+G-Cx?>#<$AllSkvb+!?zQ!0(UFsXJ}|xkvl(rS;E)?{`Xl&Tm=e z=Ka~%GpveEx?^uQB?I~vIclIB;Iz>$mj@LT0ts{KN8j{s4WQt98%@Rwg_YWnLvuPl z%Z+70I&=vE_7h(ekS)c#C?E8Z!(`+E7Xb&N?bUKxUJY*H&X_9!LVlw+G8@x=CyDhl z$P9DJ3Pujd;Q%BC`^LEaemBjt<+Z=ZhRiLk)))q|9b`4a7vMTc%6yjj&1L!ur}RH( zeIK&mEI;@|cfOE2V~se$uZ6p97pAHX#XcXQ7*0<Cgg6wKKx8CC_$!pZN`49Z@tC9IL>}kmT^N@f^`~eJ93gxY z{15UQ3X>r^kl#|bVFSVLo2RklMv%3(05F zmSLhL0cK3C7!4__!;-LIWGGMQ+6syB}7G`B}A=+0Eino=s0P?<`=Vpvj$-!TNAsbMj<)~dDm)j z;Y6fzpP9MLTH|Et6p;h+pP~5atH+xmG+qLL3Hi-p=YoT`iqvYto_A0%1&I+LCbb?y z*@j9FpYvFwanoSm#j^<|XA*qpwiv|3aYIfI9aJw|;Sf0`Lfgl`1Nwl@3wYC6usC3* zt8_jt&42w$Jy+Nrc8bSn7bVFdOPP&hkOn1~U&Si1PT0?aZ|e4B_cBZR>I zAr#Jq=Emm9zUyOMQW7g_+IedJ+ptZ@;>UX82|2VzA-uD&Fkooqws`i727S$I-Qr-E z1lAT<4VirAmW5*Ib@|w^vy*m%~UHL+2<({l7xdwJ&x*IV&UM!f4JjKR5`yl-a) z56)ClF0yEc3s~FQ1Uk=RCmRfR*gfD`^ScK%qR=0z87;O{6U12zRA`Z`g2&uB>AO#UL)7GT2et952K~z^ zY#OmA=e|6s=vd78ItTlCr9erefQj9gTYsU zxaw&QTn{_7DMcs0Dn^C4BRH0HuLUDf<_ZG?;u#ZB$X&yD5nyzV;wkJiU%x}@;_kO7 z8F_O3Am>+L^ODA8yb;5x3GuS{i?3dY)6O%l3zR-W&5d{dHVi+anFAML3*2k^){qU) zeCb((u-378W%+%Q_MrVn=*GOfWSn|N(kOD{vi4zQ;5-6p?DqN>FdcT;fw7CS=XW=E zA(7$od0nB|NYTgh$4ck)F^aWGVes|#^O<1eL&)eC{nKh4nCKGd8&Lh$GGVC#`RD)E*ii!gj3^MB2b%$b4XS z(r%gTGE!C>u zhU+Y-C_q3!iy>M)LIa1mDmENdZjiE?j5zJ!gAf>&!C#hqw%vYpe}puA_Kw>CvZ*sb z1Kykm9--rnPM$BcuGgkz+`x>Xv5@lp@@R+>3AKz2b|4;S;#n*+c%%JtqnM#PdTE=`;5Mi24}(vPE|)rhbnxq z`e_7>77L9!1EqZys^a5akx_Kpn;iO@j?Iv&$5Qu(aj|oF;N$}O^shW(#T!MNSloZ& z=Sh$NNhZ;E$z;vzY_j@b=z^mU^2*ZC*q#$~r} zR)%Uwd8j3z){FC3bFNSq+W`S7yX26zkC+qC;*#v~`_y(ZPiwigpApH9kt6Cvr6sn6 z(tNxDA1WLz!Q25%H9|vU4x7cCW}_G^t86N$cwNKwPs7Crq1qSV~d_7W$*?U8yK)h{X01l{}3>yCza994dJ9*0-P zDYx8<0s7OX=luhtERV~&!3M~=(L}fi>#2lp977gideIVy5xPuCTvcM`C;l_@`_yxD z2hPUxg5?l%6Ojf0^Bv#fxsZk)edS$~;#u}#-q#~3Iq#_1*;HnnolVkQE;Efirdr3w z@Y6}E{TFpSjm+eHDlj0z!cg0`-l4@pHf?)RsvSRnQ2O12C=DpQ--((IzZ7;5jyEJ2X$o|4@3@)>>M|g+j{#at zOe_rSg-VPaNG|4a(S*0O7Hlbh^Q?`m9F-E)p^XxfL#2mIuL}2{DIWe(9O8ZGG!L)T zoty#+B_otLBy5V$Tmfb$K-wUrRr|G+?hrrq__5WSRg5SH3dy0{OuwNA<<%ZEe~^rpUVaVUiCo*-4mwsFD8x3 zuyU9<0lN|lTy_nOSJm(C(dVlsAgY%h0cW9u)xww3Up!s3uzhl;AJ0P#14WWFfI)~h zDr}0{{Qcs0$}zvYhaA=37oO4dG4mL!eCx>8zGC+{tY0`J8+2pMFAA*cnBy!TPKVP; z2bR8k2#7LvLd|aNNUO!5W$oQ=e4+9slh-LD4N>M{;76c8nGO>=8BLCU1Vhmjt1s0G_o9dTouoxb{1C9XrIVw%wT+vE!;x{;9FPb@opKr{h99z zKwfg%hVuhuhYjE7(u%;D889#9@dtkb{gEF3(hv%l!(yMJt{f@T>34!(q?$w~UA9m6 zxP5f*zYD692dn!yJEX*zlw2t*II$>|7J5!mbGNcHLVFo*!xos1OH#ib7H|7{aXg)I zl$s?NhH!iRN_zZCOT)Rg&V1k`fN0jHv62I5NL?x>KyTzKaBX$RKsm7e^r zR^7R+k!*2kWr_6kR1UiAd(ym@;8Khb2v|%=;iva@R7A8WS@tAS-%!=oH%Ff@gWdg@ zp$A=2`Mc%)<yOH9^u|OTVqiU)j{;IPxDb9k&z<_!!mW4lOvspI)aRI|TZmI+UrA&K= z!p4v}IW``y9F|H&nQVphaRYVTbiRfY0X*-aGB~&HotNCnU9aH#jl5Q{{6A;40N|j z#XMJuI4tyfokMb9C2HS2y&@_#k}{QfZ?*g7eeLdgcY&l!Vdvh6Se7`vBib!Wq?x~u%R##q%TcCUXK%vSn*_7-p z>H=AE7*YACd@z~#AQSN6_P}lutGT1capuSQA?;GDS{!&NEcup^#T1~Q!RN!n=g)TQ z4lhwFLT-%L56FCs2C00F3ec4rhlpsEK}jP@wV)3e4moBo!p>*z&rl)*9IUe86S^(G zLw@{xs9s*P*@?ITbWhxNzr-za0T>ctkDis<^2|}n30yrwlbn5y+I!jtcYXcUZM1c0 zr9Cr}DoZvuGE7F}sOr`DD1LGkzQblpj)j^i7pH=Y6C17Gi|k$pjt1r^TvT0f*VGCN z;i;s_l&OYH*k~XV({~4K2Gt0f^v2@t{!1~ed#5AZ!(;;2VJ|+4&nd-SSf>^~ zZ)e#(%|pS@)b88_DLjD6a;nFjeH`nNZIx!zdGR>Lp_=da7m550Sc-tYQ?ym{iBbGG zZPk%he1!zFVmS7r!`27;!7n4JKQ_BZihyLm%=+zqOsdAt|St znXS(2TSjK&CfRTchuJ$DCoESACXZyK%%;vxCED!g!f*MsinPgAO6bv(ww5IFM51r* z;ZK%k73U;u8Fd)nKI4pT&JHZtp0V0I-DH1W7gDe~!ScB5c~r>f6$!W3SnYma(5`kS z>t=&nlj!k-M$QGTOlrh!UpW!92ot>|%MoBJ8bGHCFwhJig%xu~BaBhiDkL{SuNN2Y zGkzv}Ve_;eu(Sf)DA`<8-VR_SDXp7mVzulq?({+~GP#jOUwnlv;Xw}hO%RYyLcK^| zt9YuJ%=fabDG;IQ(+3K+F^y8Y!FFCHtu4P$KYl)D9eM&3)%8}X85F|H4FpUWX1=_E zCU4Ch55`o*o4H%Fus$(0Xg>ZNSh5oxN}ycR#_EK-dw*sLodzHv=cZ@}OcEUVYU@n{ zp8h)8IJV~)F(gV|08S&6u}dAv6%$U4p|0OUO1%cymq2LmS9PW2E?JrQghY*l0s_@b zKYQL(&53R=yum1{A2>%-!^%8X3%Qc2yeBwA`P0JbkjO|UhUfe_U>g;I^R=F+jxEii zI~Exk$ty3dA0GKQ611qee3IiRa8N|9N&3H#b8RRVV&^fvyTl#$SXu{>4y%P08b$-H z7-Y)(6_4t9#24az9KPCbwG!32QwC1!r<*a-_g}2&^5)vq(3*U8b<^WL3(pMIxU#Ng zUFsqLf!4s-5w*Vg&!e0IT6|LA2w^9o(1qf&8TUgc*Tx&zuY}geDg&txAcDIQUHc&Q zcccy=fEn))%T|R{Gruw>D=6Ol_(>J2f9bXmLta+(iTKxaIagjDn~#F*b5E8C;zcYs z^E&{GaVq`33?^4lWjq=dxYT*29h4swAgRxj^}aM{(M>Ry4TzK9k&MlffDgS1ODMd3 zXsL@I1eA`qC| z?hsc>JPY8G1gOzg%!-u)|Dq^)`&-7uj}$2v8;>U}-eI4VF_h>s;LTx20Cl(@-)^x~ zFzcwxQR~ajr`;#%*}FyIT|2x`8L1fBbF1()Kt8}~$(9WW%2Wg2a1S;$*aMr0?xuL?wB+-P`oM$nOi^jg) zFZ{>5${{PBd_|lKiAVbjHxJ+cx{uM%H*b!TuWFx=Z7r<++ zHXz*dhIZ;RR&&u#LFlhAPPBEw{H6w+fwEOV_<#nfI#7xib)1_cZfY;I73d&SE_jMH zfZ+iFP>vE2IV3K!-%eazi&(Yn(Ciq@m$9QBpLR$h5^ z5;55IT02P=KwSaoqi{bpkb4_|WczI$&c!u(0!#SB&$6zyS=DGa+CK4>S7~4(v=_X{ zN{Po2fEO#&;)8=t4GX5&qCBRbCv|Grq!IRxkz*n|$v51qM82UD6w4gwYV2kJFdjx` zCbc|20zSzMA7vORpVV4P#E*?q7QUHf2~3Io19wyaFPxE{uU0hBI6}{eUgEcQRNRD_ zO{?w}N7V8p$J3QU_b3e7ZBso+Hp`5ppn&IfX>e93#QC%RjAU^|5x?592R}!veB_F}anI+Pnt_nI!Kv zXN15G_#bEfzK-$Ur|Mk(v@5DMr&<3esr(y5p1_3Ici5n|-#}bUTa-R`M-}vvJ}|Ig zjKfE$i9Q81^_YOMyO3d)S*hQUg@<X(YVAAs{hyafMlLhy8(wK_ zCZw1y+193;eJ{lPJ>rKRp3n{VtaCLfP7DCA>mm^!tQvy5qrTv0t5-9{%^|TxH8Bo> z!bk@v!xaaW8m!HZb-Ue^gkY>o1b{f;T}IA8!`cXH_PX-HU0Q^UbK*3T{Er1uT7Ljd zU2L%N2)BV=t5*o%VjyGFM`s__Thz;GW@{1p=@-xF5HeohQN zQ;Vw=dJ<_t^M}>hoL6yDe7h@A(@#1HR~%8bDinb6z!T){cE5P7NFVW(Cg?YzV3D1~ zarZJH>Hx*fxss*94&Ah{p#kJEodV-ctIvM202shgy^?-VI?0OJ1XOZz*{ZsnH zlL%6EjP>Ry8coJkQ(`ABNV6)VO&=ZqlT7a$=)MJ1x&D{H3FNoaQ_suSB0EV*=#vP@ zR3e05hS!9Ro-c5yWM+(^56eeiiVg6JsvLa}LrUpYUm4G+bfE71K~5ks+GbMZdp6a6S&d> zfDHcdfjOa->3O#~H*NaGQ_d&@(>D@4VSQ`$Dd^z+QhK-IGC)wd-4&znc*h~+=(|D8 znwbomr;LY6mo1hN+olfAA%w9vTxpyT|6TZ@bXmC)u??FNOya3yhK7V;VF9V~X73(q zt?H2@#F@5rFX{L?(wH9S-1z+*DTee)m!&0SGl>_kSh{13p^>5@0`WU|n2cq)dNL7S zBeP}J(r-@GzzUPZ)bw{NtpgO5$?3I4$ApC%*(tau*U_|MOdw9US2^L$YuC-8mPZ@U zuruI$4a4XDD`tak{mq5>BJ2#fz8TT*oX+1{Vw0a%3h9^T2q+KAmixQ?NsW6b%c46D zv1vDEUWtbwI|5Js>aEU~EosiqY2MK;)QAxae7&NB98+ePZX~#>A zccNeW*6Gi;VxB1D2=;nsH{EB0O{U&KoAYv5spUm_*OIN7hhutB9o>J| zG+)7{`p;(8w;gtTg?BoBwuYer@oyJ%8Z=r%E1_Q z8+H~hS~SF7_f4(6zV^oCvC|WBhiLA(M9#Wd7@Y+Q`fr~}90?zBr%v*TE5R^)d$A#a zbK2Y~zNwyj#0@?o;ERqdE^~5kgwI#At+&Q8?LOPiWova+h9#x1ES+F4XCV(f&=bKM zUOAK$AMx3dQhJHtgDzTv+3uS2Fh>%H*a5|rGIs*RU6kh1$baa|cV|M^UQ9hKT$ugZ z>3JRtji{M?)2w;#G;O9U^IYfOKAxxfsbS%ppW!bFhNT5fI@vY3D0r$P@1}_FoEiVa zm5h)0MAcx&M-8P=>X!EKUH9{xkZ!%0Gy48NfJFiC+YWZNH$U&OjmD|;9&_<$vyFb& z+v1Y(W$Hi}h8mJo`6|+kNa?qXdo5vJLJ|Z&AT?{$VxNB_Sc;5j`Vnb+Khw?jNhGCpE$ch1>iM zu{87Q;vU3?X#DP-N^hFR0{1tQEey&=HC=pAN&(0pz@f2}9#v`%gOrhVB~`XQl!g8| z--SO+@IgNyvHv(8&H#hQ;Q+SCKMpMar8U~HRj+))GQeH^v{zRuaT*(_UoupcncS;+ zo3qi*4D>#xr|xw(?-V+I(%YbdmGk~pWI}P^s+n^rYuqmCJw0Jw?%$eTIe(CebX+CE z@HQA}XRc=6Ua!D8&#%nvDRzIcYBw?zp(PUPp5>SHN6%6i5|aO9Q1;;m7XCpFG4X(kDh9JKl(!`4_wv@aYd53wg26ys* zY*ji3-oty;P5aO)>2a^mqTDU9HOXvOJMoDmvMws2U}^+1 zT{r(ZewPZykt+VjU0CaF56zV4&i%&Ue2#O~4UhfZ;V*YRVA5vCf3lskoaBJl+7l6G z+>Wx{c#tC5+j5>_WUU>O=g0jwtLkG@!+Z<4J;VzoT%Q#AZ2O-7{45xgH=Fbik($&n z`B#Pa?a<`TFt}TJ;mgvvb%vVW6cP`0zyDVbir8H3fd_dgV9WdhXATcOQGW~&!yvz) ze~`!yc0vgSv76o>?0*^O_LZv8i9jS9e6ZkT82XC}J?H9T5w*iC{V$sO&mA#-F3AHe ziNcdU^sU}e{|u^54eNwwi9T_7)ZL__t7FA8tmi;UJW`4Ms%LH1ePnAWgZ4xs?eF$; z2p?OwzC~VY<7(LXDHnIGZy{IpvQ*~SF%N!>QN@s3jurC8KhEc*$ng=|p$O&Y-h1#A zIG5B+@(WENn2}q##_U}42WGp2hx0Q5W`n?kDGi)=h)JIaRGCRi6lrYscxI z8ru~f4oz*gtdR*Oi|ni=^IBu0i5`qJ0(!zmqLhMz?)xXxnl5wILen)Ct20b41v_7M zTOuPQN7#u8EQBz98O#7)wG5ijWlR24H82=j20mNkSR1D06+UL)oJzT|Njqss)8mL~ z?i!bGq2WHGd^ln72WM&8Uxz|DXEvILc{)>p2@yRvl6FfpWo5u^3N^YOn+^U~B5!tY zR*y2{E@vJ7Q+Wk2fqSliXYy{h`R|mie|D#Q)E^9z!r>1Qxs1AxqNu(J!nODSs?20r zl{Fcpk)hpPxeny=?H&azITRjHFiiGcJG|M{fl~9DR|xowrDCMdRJo#N_Qa<j_092f6ackm1m1az(e?eV*~o# z5);bElmSJacaPm7n?zf?LQBi4s6(PX>&Yp$S@QgR{@KY{dA#x6>DA#Wp0YXC?0uIN zGQCzSgpkkkx!NuM&AwKr0P(14%;cVT?jQcHW?Z^~?XgQ%l>|kT+kQk-b;S>b($bhg zcIVF2sBvwV8#Tb4UnsewDK0FWS=N4n>{wS>*oi!#)1={f)0K40H0WDsNs>u7YvK zj|dfp2ryv4N=~WWi}XEKIbGFFz28(v0iEg-98WrxGc_|dZw@F^XrcW}-2f9ub+um^ zW+PUPZtwI1jXWY8+mJ{ce?9UE7wc@hu) z;@b4W(KjU~oguPcZMmjI?#%Ux-q8=$BaUiV$rK+Hza#BGrd`xsu^iPbQ;2ujQe}?} z){UKxl)7BXyc0z||7T#Ry7UwOffcJPp#%$?%Glk+XAF&$9xrJ-`Jm-^rrc=0fwCS; zd9~8_$2S&~Z+wYAPq>F#2}rw&T1gFvj9e29Mc%|3yn(JPxfr?jq6v?8GMg-uEWkW?E@~M1J`6M(PR!Hfn)sF_3NCi;4Wo_)O6HnPl*s$ zANHdI;gx&TtTpCzL!$ET>C8Sh9(4*xU_={?;bX&uE4`I91*Tg>i9d&u_khs!^D*ma zuK>0b)BV$;QwGKR&Pb6SsW?1i!O|;{UHj^d1$bnA$^-LA={@SqJxc|c&ck<*-|&y` z_VX1U4}c*A2r<-Mf$P90hSxy1hoqr2ta|BPBtzpCl;b`3zOptbsnz@RAQ!U$HGs=9 z53h8clOBe@+jf|YHldAN`J}6?I;Xpdl0t-uon)eG*=yU&`dOyx6YYoahi-OR9m4k} z?v~p)_aSc~ShbUFtcClHkQEY4w_=n zJ#Nl4KH)P_9LX}YbCgJHx(H}`tLQ3T)*&i#*Vhccq^zCm-d)(eex(mST!yjiyp-H$ z_3o}?g#m;y0W8=CQd#CGwNU;A8h*hl96nO3zK%L?(%6MpnbDn<_(E+YdygJHgg$1u zqi`VhmD;C5>8ntFPi?^cZmOnbeowX0X^m_eUPuwv59XWj1@hUT0XI&s&Y!xsjX6p0 z5L(@dXRXv*bcFllyzufnGBox*(^W(g+SSw!RTnMO_P0%H(+!8ZcfRgcu)mMomo)C2 z`w9v(_248NA;0rYgW`@Jp7=7^Y!v0bWW=}VKtUTu+bf6lYMn^ySZ@}1s~;vaPpC3*ql z9m>gwq|;r7Z7Z_w2_?KSG8uM#2z_HZMZzrj+T1T}&6O~wJz1A58!bV}+ zGX9OVithFW)`0Cs4JE$y1z!R~pHsDwv5IhyQy0RMNR$Tn9)og>mH_{rm~+qmlz#cE zE(SCVdN_?seiAV`8ezQVpKDuQ=L;U|J`RI>yCp(pCsp!rcjGHPrdg`OZ%N&;1WRYlEI5L74yRq&$3wN6UMpAfya;7ON0>yE zV*z9sHW?Q}#G=Ks`a^^orr46Q>fK<_k*mZ` z?}@E~CUlN>vRwn`RwjW}uCVRX$M3EEV$a+pCORd6@XPD5Omn_-a^uc^e7_<7%h732 zE$Vq18RdX%OLC z=qQ@}I{WNi2G}ZXx}U0vWx4o__Knrz1tms1cHz=vGBaG#%!aK^2{w7uF_hm%zW(DR z2~&OtxFb@8(4md=i5lQk@iIZvo4Z{y8{_>oYN5zso{t7Co#KeLI9Btg;|qkDZ^jo{ z(GJBYx!pCECpYI!nweVMQWI_U=$qEqAxPEi*C03UwA;UU;*8$a?M9U?cV~%loB>z# zY_IQQs579p-{vW+#bfVVVP z7cO3jzZ%5$K97?`vUu6iFRESxZxq|k3fO1nVkO@Ae7IL*3aS|>M9Hu`8(!~10brwd z`)uhMnH;MrJgG*k@j3SfkvJ7hMtu~j*{SnRv66=(SU3P3!+`}dzbNic*UKxb;nNz! z_S-=GX3h%|KGxGo4e;z#t=|xEYNLE8xY4l3r`FEDr=W&5m(DPR?0Q~8i6oGI{7^W| z#Z3rWe5T6kEfv@o0c$L+D2Z*oIPNSLJob26s;vdlwJoU=e(+AqFvN4=A`9SfpTyLi z6L~iBg0j=Hwtfw%-AHP{U)fkab2V0SqOj7>Ws|p_`bNJYcny5TfvdV*-}*THE+oVo zByLXc-LC``Hj17_?;E8W8?p15`fHY8m;FN)jvYQ53m-?>d@tEGo zH)@Stx3+s!Fq;0F-X! z%-MDXPZFj^9K6k|NYXw!4+xVe01Pl0+bn4*nh}|+cvs-}8>i5M-FsgF@L~D4iXXl- z^sZ;2-^1yCjTXct3BZo8y;`)Bqq8n3R>@Z4MK@vu+{#*bIx`0Axf*iWAhXCM-69P@ z@SX@ojs@sxba9vfEQ9UEqmzcv zD!c#%e4tcT_+GN6bW)utz>F;dp!(5aJ$Fk8+}j;#uBV5+QgfEu>=75CI_S&*1FWmM zk^M*<>aJ-JI|6hKEjYpvEPSfc?QJGxf7F`e>mdz2$~639-(x%F<+%;TPH0tj zVr0;@-?TT)jlKKjA407j&%UzjjOAkuz&-P z0}T_Un==j+FRdI6xUC!#ICq}jdtG5@r!4*Kr1q=H2}Me-Uxk`;v{=2Y!`21icx(R1 zJ4J3&T-cz2&%yG1l=5{fN^9Y3#Qi3}rG*hEE>?n!3}S(jP3$hZ4+i?w&boBYg=pSN z>B1#+Pbhxo(w2*femuUcZ6Oy8${l># zu*zD^T3R-K}Q9=iRbpQ+|V7&U~GK}hwM|DbICJYbeM2*mT0Ge?9aa0_k{mlNRr zBFIU7jc3=T&w)RNV9j8u+;%17trWYMXT5j>YdF+_8@xN&@1(&-$(`XF_yJziI*s;? zVPhyz&#oM#Ni!TShh)1LvqB*Ts0pk5b0}5FE!&-LojUl8Cy6u9m=VCdHdi#@XBiiH z%|kc@vS}zS7h^l8E2>fH)0{nG%2@IQ>1B8s%*EJ%UP)=9zG(?&nmfIrWN)t++8beZ z3-+Twulhvz{CU5?b*uYRl!q;Y83Wg-j+=R!AuDm`H?I7j2kcsVnKGe{Up?maW-Vko zNi_Ib?#KydC`ois`n6CcY!c8wc?BS_mf!jV;>JQx2%MRt;J@P<|3u!%8&OBU_@nQ`@-BeucoR$g(fLmBL%B%s!# zm996Zb;`)d6bc-Qge%s1cJX+^yS8=W{GtYN@%x&(K>*cKzn8QTRoCRIM0N{%3)~0A zAo49Zmcmv>&g+;^(ZxZ}06^Dogu(|Gn) zvompD7pX$ZFSkR_cR-u)?8IT^((yZ8%3jU)PDsVghyV*^1-dAExESt0(_&9nOEPc0 z9^NTCUvWr8b`&`|rBNv<1@s@rJVd37P4qh5w~6EzGrr{kWE7E&pg~+YF6>zj@!skU z!|(X?xn`cP3b|c$!Ts>qW44E_UM*m!&owIGlnj2cN@j~h1n*6&jg_y5Cvrn~SGeA~ z|5)8_LUFjwZv`^rUl0lNwQU^WeVM z3eQ`2vWB?8jtnj=Uf%9k20Mrc^ASBU=cwkfu$9f}s1~0?r(07JCEtenN-}H@d18U&Z*@0N;t<1!n!Oy7 z6t2f!5`;RJ1WW$VWDBE7w|YCQc-?B1s|h9GqJIsf@Po!FE%m`u{%xt+Z8MRy#Hab} z$#yb^x}31Y6uIUY*(}gR+xdhHJ`%s<8a-63Y*K*-8)Tq zS9XUP%wW?3&!EEsF-NG$EA$c@=87ZMt>>oaX3J7ddiyw2R%Y{~P-68*ggSEg;_3c< zf!7heLucL|FF>iC^zM4{uDP<)IgQMlOUX3mdLOm;*wX9Do zDuUYj5{K&_E0SF5t$rXTH!suxPpq7lYUimr8>A1#1e4GXJ<{rzq=NcH_&o!)y{j?Q zR_Hl7i95Rkz4%6c-6?dG5~uY0_;xtx1z&}*!i8HtLp*{j^G z5ya*|f)S*f-@idBdmfoxkxn?UxVxnAteQl2dcG-AxcoMV_fDT!LOMIjw=WMDb~*bs zal4FJ_XX$p(c0}*Uga!Wudd6sY^i3g~nMel$(^tx0t!ILo$@$b%%t_PA6{CqWY{)<3Mt_mjdI;r}R zK>}SCWLO<~*F_5wQIk(tf6rjgOb)%M=lwF}-(ZiTD^GZyl5wKji|dp{s~~7_2fEBB z{Y&++IRQw&bcenwO7+yYPm;zMx$$9KP*GYWn6C@qRD?C>Tlbf1TunY@Aeljk!CDF| zL8(3J)uDst)WXx zbS~|@Ppxnu56D!edM0}7ms(X35Q))f@@arX+uhklC9vtp%Lk#`V)fG@apV1qyMl># zpq+xdD?_YUAd86nP!>&Ak3Te;OS+E#bK6MIxHxS8L?&xi_Up&#s9f3@lrF%1P$-ub zM}m@Q?OWuV7r_eEbTgDf!gU#DSCSfRklPP4{3T?4_Rjjq?|@{An>i9an)Q2e17*Y^ z?9BL)kJr0?IsASiXb?BBeGW$ppzAyLOpVEzXlRXnw?mm z5rBMWd-i${NSsOknFsjDd?#U`4i>P@+ zN04Sv61vha<>`*SxfuF^8YmN|r*)y%i9i~qbxm#B{B)xVy=PzQHW&!{CpsHA)UphV znSV9*^lvWX!lXA;0kM)bN##$>-&+dhuFr2A&c=M1$;y^&A6ajYTbDQf!~%ZK7G^Z$ zcO9`jG;&hMObx22&;qyz*w8WhzQl4)CP?#}-4pv&T&y&7e>X#|;lUI$Oj6!}Ko9TI zrKNI9R;k70l6AG;e#`G5D$?b08_FOV$ccRuG{ru_8vh`T$hNaTQ|rM{HXQ*ITABO> zpDbz*<&OmlhJCkxyty)n^M$P`7ngbSxp+Uhvv^a!;M%uxOt zw6!Or_7UiRRrTHRRDbdRZ=0-=J(G1SA(Cv8m31Y1&uqfAS6BFihJnsXjbJn6N?9jqR*Q+F9)3iJ7j}j(r?;tOv zLw=1+(4@-6e6=MyI(rleqC&b~$t)%K%R|R`_kr7P12&4Ug}U-tmY&yDHN>yl#Yt&4 z`nrdrn8#55?`)MTH%ld=rL5J}TsF1dZeZYq1JN0GPf(X5^V9^u3)PHh>x4c*u_b0v z)M;8{!ur@zzngMK&0VCQbU;y0idegkOYQ7%{?K?Y0lEA@+&gmg?$!Vtkoj-9rorzA z&n*b!!#ph1T<>N_upjgLO21q#2W=5)2;86yauC&(TaKt(_c)2@x$vu9S0n&y0Bz zDZi>y1lM*`2L#)fJhnv0yP)w%Ar05PGkaOumIk5UcX`IISP$RtOd z55*lt8DjtqG<3grZ*U;O}&CI`%ma?d}MG7p~$^j{e-?L+`!&QR52{H zVS}oKPMSyK@TSITMLE%O%zoZOJY3*N!H_%e_~f(SlV*P=6Af8IQ$$h{KmKsN3Uo4_ z*4XLe22N_6`_^V47^oWqJz>I-)?5(;c*Ps#GBU*Xydj+VEYh}9c|L3^=<2T54iyp> zFRyFhy+39F9v*}dVNW2}Mc{Sy;$@1|v}kYWr08Zwr}J)vwc*_091Z+{{qJWT8e{X1 z%aIvc?&698`v`D>I{btmBpx>MGXOW>Mk^GGk2D#`EmAR`;U_m=<&)J9dS)O*dy>8b z!mbmADe|da>jH$hZ|gx~7#E~9P2>?S5HM=%$tkQAXVt)r9c$Ng=3NU~EtS$1->YJW z#6(T|jTTH{57+A_+WgOc-8ui6Ieoa@c^jF*0SqUe7Z@iQdT>5jdWpUN>!Y~q5wmCZ z2yLI|t-#gNZIu7fO2m4i^wIj6QTH*db*3{0#5C@NEXY<>07dC$NObaf`{wv$jQ-J) zwe9}%7d|T9ca`rgc%}+4hw=UKkbWM4(s2o0O9{KW4wAL0=k(6uXL|z;Bz0NI4F}p_ zDRHXO^q)!4S4Qx8G5s{o=#r3nyma`6D*OcvIL@&vpD2;VO#z zkn6%jVxZc9B`I}6(n?njS9V&7=BF}ES_24^J~=^I)o85!^0~b)eG>x5Zm(_zp6}LQ zT|`@+jRk^%aFF)^7!D~L7wWTSkUU@&l%1C5i%suMXl2_Y&6|_|w^{1_>|bG0 zCr`U);&8+xQJ7QIM-Q1v%66?P93#E&xq$@BWe5NV+&@Ppt**B`)_5LHHF-t|SM)zV zImUO}O~0R*1N}-0Z{G1Thxl&NpHS61dwH1c9W_&+U49Qlfmh$Ep9a~Na(+a%f@IrL zvhMfdWh2R1s&iDa18v`Q1j=g)%l-DCxoM{-vSCSNb0Fnd5OSRb)}z(+psdfqVYlxT zF8ePmXFXWs9#Or`6VXkjBK}o$CV$I33&|?1m8URpE8KKT`({;p;sfgJrUjc!mw`$k zq&Q*rG@ELA{+?MCTqzv2v($&C&Q--z*caL-c~d-o6ynKtes_>T8o=U*uit8-)F@mj zlTceRJKM?9;msAW^SeLtjjQzfCh$XAck&l%$POAZCYx!_u~ImJ(rc(o3NWF!k?e)r z+O(65fKJg@WMb#$u5A=H;CZ1WwsaR$smlM{{~N1dLyxpz&B#fEh*JRrG4kmQAp`PS z4_N%7Q%MsdSCZk%Cyz-=Fqkbd>Z7! zAd_x1F%Xff@baG{$>#K8OSU7qe3c6^&K6xxn9)m)EbRa#x~bwDbE>t*CoFT zAx5`ygXrR49?7Z;SG|F+c8#^#a-$uiu`fpNO!68fner;0o{~;syGPrNPR0UG2TCG7 zPz3&|meuz|OyL8pcllOmVU*MRVBm@#B(`~~1_>LvRDTOy{4VQb(ZKml!7z)m>$S0I zo%lQ9KN2t(^O`*Ulc%z0L<9h<`D5MqSZHb*H{@@n=3e!u#q;e64kBx8c>&FT|7FB) z?U!C~aToYS{>LV!24$rKo`3lBlruKkMFzwTrpsd!A9J-82TcR*mBi|7G8H{;yOiiC zntmUf{1T2m$zm%8YbNX?2$zSDcmQf-OUXVjM_8BfHZeMk+wPJ?ziN8ql_a^c1_^qd zK{2@`x{acC_Om!q>4kCKD3*{rS$zGp7=gdMN(@Nsy>T)x+}dSNNh>`2+OlW!jCXTz z*(lGZKwzlkDcAZgJ5&0Ek(@!vT&I_= z?y;BdOFqkk9Ap(Jp;@bdWHe-*;{m@Qy2PwH(JxS7=WY zeo#gVxYrzfYpxpGV$V31{04%opj*?JwVj$BS4A@5vRf1G_5%2#SyE%==yttTJ)v*n zpj7pZY3*A_|G|hld(RoO=eNnY|A64{f8Ylf#K7HOX~s#(mXtouh1~H5`QgoDPOW}Z zHfgh=1WD%|U1bC%5A+yN0SRXYB;@&vvqH)=3JBqW5PjX^tmmib^8HtGvCw|V#aL{{{$zg|~U0EjeLIyk{{JrCOAQeEu02J7BOYuU2VuAl?$gPC_ zMssS_oPg(jhu5XmT#g$@HG~S}(cE}dEo%jA^e;Rqn1Ghka?~Xe?!9M~^<1Ko;-ScXZ=^pbSQiRYb}Xay4ofhE~x~3 zP5#72hR(Pji!VcI*v}^xC(sfukyaRCXs2SdmKi;=Pu{p%UdDUFn3S4R5(jJe2tcmO zUs41g4KVussG{kmORgY)6KbIb!nS0o@Mf(765wz;MuHxBt$ZIZ7+hXSHO=A0RV!c! zqBL|t^5rH~ zEt0POZVt8Eo3#W4eRBWVjK63s3<+v@c`I05(aO2P(Uag$d->DU>gL9;8k31S%%VX) zBU_?dAA6nbp3YDS0H|8xt!DgkWUYuD27vrMenzC}Lz!YIyNK{=w@=WS-dy96 z@66$Zg0bRruihQet-|R`WnDL5(V7v-e=GRsyV-;&HPO6~%E9SKhQD)-=UFK&d&k{> zpPeMaxD+1|;4FUOvxBnU1uDL^XhzP7bt^Y|O}Ulott2qH`FRh#d`@A;MFeyUgLhVf z>vaF?DpZf&nbQjLe%J@HF*bPa78ZXYk3@m;9NBsZpjsgEtDUFRlzupds?=^<01Oz9L8$`b?PeOpix- zHdTtEpM`#ZK+SiQ!q(=-@!tJPUE7$cHHz9DH$%yA zma;HvASg%nEF53p>%=^`u_&H6Yp@Is8V}NM&mC=I9%6r)Qn>4l>JV>&Ga>$;iD;DX z-f{;=Fxx2V@(h>0y4oQ2<;GeO-je8RnKYv}CdZ>!=ADP@Uyq@R!h55iaTouBXOA)< zY>Rp`?hE8dA~mTL@o3!IarDM7BYmDXYo)IuX*q6ZKN$>@R9MzDSPAy8cJ?;;u9=2rW=$t;K1-~YXXD%2D@>w_ z5x4Jkzjqoe^J7v4t2)mMGa)Ij{K^yDy9DRvnkAcTJcE2ZB<_%0?Rc-Swf`^BxU&wac!~6Z|ofKFRG9dl08T5)kwjX-(?3w*iH=-vbih41u!jz5``Sl5&woa)Qa+K`BjYf!aOy!d3q`$ zybGsh&NF4DB|a>lbbw8_EViRAYv!v7osjewoj;@pmv-r8mNn^de7sA`Xyj)o*{yMTT)mT8B=d@2lpU-%5Sa|ey+hm&%q6` z>Lo#epW!Sx^;3Is8bMEEpgeEa^+H>FoZ|!gvf}M^HZeOqZgYr5;Cv&6k`!bk)D&Nk z=`GyUIJv12KN~*PyYDf=EJ;-QwtXPIGd;CerExAc9dCF<)n5-fS^ro@smzyaOrBgh zgs~-z?en)!=j|}EbUwzKo72m1R_{XVmHVc&^k$zVfi11nUvS@@p?Yoh|D?)F>G57| zEB{eX=y3*L-rIvY8k$IZKeY!5*DS*~8LOEGxIE5=AMhysc+b=K2jNqn!TW_2eFf!N z-B5|i3yB*%dvrA>FV~}-aMD;?a(xBMv+?3|1*DchsKGFA#f1sZ(n@U1HUtUY75G75 z+HBagm?(CYm0{){uaEoor2Urk^W~aLhv?#k!)g{H8J()VYWK^}*I+RT z$Bzt2(I6wqXrT#WThH5NTpzI3PKv{uo|^u4UP;+PMAG;tt6n5#CO2rWa*$2=B~Fop zb-j492m`36-Ics9W~0AP`=9r%q}8Z1xfYh6G(f;UPxAX1Mx^v`it$GXYGTs!!;88>~4RX+;k$zHpu2!*Z2B-Em;{(W;T^VDWwiF2K+LhHz{~(U@V<_ zpp>S*t`c2EN+9a@?mc!nk^!s#VWmq0oAIRe&G8g3F?x%glFC)KYWH2pfe6X%yG5JU z^@%^9&?B*+X5o=@3vtaWfi(r z6s`rdcoOr~ciJK|VCy`!qjCGnBC18uY9#Q(ZbecVI(a6EW6@pmw7EPr(W=4mfP@z2 zc3P8%4Hf0Thmg&((=t;z+=1%J7Gn;oyfeo|5m?1~P@;<9oI` zO~a_AM%CQ5WFf6JU5qqFa(+xZ8Lih^l0RFz|1N8VW%a*SYli0oWY$9 zj9|}Y{k~dAU`uk})>QKyq+Cll*+fXwk13Wa`eOoxQ??(563c`;sbJ7Rj z&fjfdK)N6x+q1k#qF**i(h5knla}}aZiviJ@C)96tKn>u{`h7+7_~d5&8k-@q|MIX zoH_@#(eJT4AhtPUgItJZ&dHhRyK@}^61r7R!?OBA2DSJCwT%WNFtSeek7D)9yfC+` zSq)6XMIWLn`kGzv)xW+T&#CRmgA=D)5hjdJuC#IutPPp-X+3Ri{0ei6cBob7lT6E3 zcXqjKW2`{+vkC$Om2Z8)Q}A?396k5}%C+*wyl#ZPZbqqo-VFqFxZvX7z*N(~QOwOZ z#e?H)rcFQv#xy}sE>rKMV{xgmzM-t}6Y=Ewkc`)}Z9*=vw2Pi!I0?E2E=WlptTaaE zmp3JQrq-O4`1Ad7hPo=v(e_c#l`NzBO3rJ$2y4dMN6FKOMAcGDFt3zhUS@)9`b|eZ z1Las^w3z9miVoI;E%;<+eDo2~`bSdIxb$8xFOkPZQ!_UlBy&O`0{jt_VmTmH;LG1t zn@NEL)@iz=7H($Q-VxGD{sg;ybk(o=55x#zZVgqwSWndr3^Dq=F{%gbWy;ob+Y1`# z;$kGrNs|^96-@`f48fcfC;$IqVk20!(|73=@lDk&c0AXA?9=9D)4eReAIs@j*--_+ ziLktQ#8ySnIuMlH>XVvEA$O2ObjWErLX`edKMHUgTXAa$t!8WlxHbU>u6d%K$9g(>|Toz@6b zgNnfEPsYceN?s9ff<_tm6y~EXiDU*+Vup*w*1%{Zr4n+)=70#GY3osdD0-s~y(vHQ5U@;^&AyFRgrQMe%gM_kCx1Wm@rSKu&w2Vhk6#V*XVa?x+0lOtsr zM-I0CM}cfVy@{@0a#xs6^{l%f^1*N0F2lo=WCC0jAJm*UG)rh}l4p z?Tca|!q1mT{8b$fsSJv;(!veNb(E)i(4zyD8jcvYt+Bl5SXuG>8{0}0 zCMFsXCnmf()jLg$=J+Gu>r@d=hDv!LEhA)O1ZK;8U#-|S)D7URpD(lgAk3N8h+J=3 z%i)*kk{_{Cu|Ix((?)&XtO~}01$mU&*6|ulg#@U|#0Nv)A44|%E%(&u!T99V`X{CK zO(k+%3;UPM$F7H(-RP1ZL8vgd9HRx=4XjVUt`)ok91CJZ?DC=bfBNp)F(u}f4F2w3hO7)AK#xVdeyye|}<90gPA>^aS{Je?( zZU>q4f2dJ#Y#c^@T%W)G*O_|rTbNtBw5+GKb1XQ!;iLVhWDc@t`twaMhPCQTw8|`B zwkYs6?u21FRSZYnvDcCUw(&s5(z7KvYt}UVbF7S>-H(!9ff0FS zU7Uzqr8nAaWJ$XZh z>h_UOg|4jBo}J#>{}k;^de_}exw@soVHI;#3(5c6IG3#ilv6s*Y~w~8EFS11lFd%H zl4e5>6PU%))$KK3Icn|%pyW)SOL*CdW(Khc@Fezz&UHvs(JME<(*?{j>}$pdsF&MU zL%8MJe~fQDP8-RB#N5jk%VyGHJPCLsFH#VbjuzknRq>*%Z8seGY2$e_L3a8F*euVL z7wK8dW8mj29yN{rdyw;ZA2(0qRxu&4u=f{|nI+ta$_t4hOt~3Gx--w++_cR1j3J_O z@Xx^Eev_B5{o2&q70eD1;M(t58=NK4B+;cFVfLNzQ_XBL@3d}nw=nhc_$T3u#aGul zq!Z3|R_cSez-5PL4Oq58J}S%%*?k&EC$CSZ<}qyrJ*0=vEm*5l0C7c2J(UFV`hU$4 za5KzdoUU_jmX-mv^-yFb-xuDTTG+^U?3MG2RZQjCE2>otfHG^;m(e`tDSKAWy9W;h zRoVT+f6X#^v|HJV&*l%UQc3dE#-@sj0!q{Bv`{3rZ%i$aeQyKS8o9&oqluK{cRHW& zeNe@+%i_F(0T$_MA_=IiTmQ-8igIan{k z@#I!F2etJ;qgo!p{BUo@dOf-5^J*0nYHQ>B^+Lu83RRD#z`HH$Q`?xq-ZXG@GLEV< zgEUZN5C71!Gep~?5p}InBhze%I3Jqs^&={s4HMJLe!iIfE{z*QhH4x@*X|GMsy@Um zqka{eDj~iGHok0`FXTIJ-NYBD9y9WWr2CY5 zJRuioR(a9EGo-S@Vw#(myNHHniw5iA*K}6%kAJ#`l0wy_`2q#O<``zu;8q{9iha_1 zcL^x!=4W#uo7d08gz~NpyQT-#wO)%%Qc3V6+Ne8^1DErjdBIt!{r;0VkiAdNI~%&+ z(hp=bTLQ)3nKB;1q_Mu~;~m9`CP@gUXMj9nh^vX?SC*=Y{ej5)#AmfzE6yE}2T7zp z`U|sahy3KE>sGBhUh9nRZaX;TX=~Qa0UxterCeU$_O7ol;_qV-O;R_~Y!TIeLZCTy zTMReWpn2j#Mb5$v^U`3n9?Fap4Bvjy9`ZtyoB!!qh&Rc3$81_*<#hU|Ak7v@mZa<1 zXIk|3oIF~`oHnEdd%3x?e#|46_Ss*`EAZW}OujQ#jdDpUqKkyL`5nN3dx7oy-EnH`11W?Gu1khYdh(p>6@KNWB`v}|^J$7#ZV>S!Sp8=M zhD~_!%!trmbyGe3V3nE~{E%DnNtdm?wW>AWRSFc;Gh{qZX+)7F<$UyOp#CcFx%J9z z!zQzlWt$jqO!A-cOb&)3jn#Uw-8md49rl?Ic?PYT`O&`p_iHOTTu&Mn4rjadns&T4 zj7l46z&aj|UOwyP=T!1Z{%xFG^I4n&6vrf(_pDW4(#w>J=I)%_-hR4_hDn>Ai2L6! zDT6lngQIy}Q-z;RET8m;-c;xT>17b^bV!!JgT3KbO)UZc(APO-7e2x#d1V@6xPbLG zgt-a@x%j*~lafuoPZhD;6Nr0O5u;yBYY!i3-4SW679O3#dh9qkG;&7+GM%1rt7AhD zbj$Jp_x0!JS)4~_1fp@%xe;%rODtzx*+qq5@QMn=-7r{{Y?f2C?h9G_(*`jhxd`{?+*;@++^+ z{dEs3)%}Kr?ZWn^o&41$m1UN9_yD@bP$E8EUNc%H{q2D<;;T0isAcLBpYx&bR%MPe zI<=q9P;B}xm6pMaR~P7#{M?CTtQx=nUDKvsu3MqumVR0o>4kE@U*&rY?pT{oB0rTh ze5X8kbm2F*yMpiTG6)pR?Cjn0rIr_c;Pa`MUp4tR&DzMvEjRh-^vi{~aliiih#1cK zVf(p>Nbi;B-FR%JBhq?LPF8LO=WX9A%M{T>B9cYET+ZiknaEc8=yK;xedEMxXr?C@ zDUDGVU}S{t!IRe%Hacx%u=ySL90Blno3MrhoM6?L^9tt2T6%?jq2m#=pl+!1^E0Ny z;7Od;s@r+|^fFrl0R6n6z@G&{5CxfE5tLnWfiDe_BTNqnlCmy`j(2dnDR`r@PgyL% z?r!g!@9%ef64?2k z*bUFohW2`(Ys#d;72+^6eaGegEf9S~wq|*ciT|Lmj!)y2crWoKDM^1T zF`Vaf=)Letw8=wfg}2{Lz?zc`A{;cb6xP~NV^C80f;zDIV&ZS_7~#z52$qXKkNV-@ zvqJHvReRc7*I7K_m2Ug=gs3e+5Z%1@X09jRm#45c4)%p~OuhvhlP?G-z5P3b8U2fr z&64VwG-yW3hnNaBu(4MX>vbef(1@d(a=Er4DY4O@A#t7Tr_7qgV5j|Ar?}zweqlkP|GWIgTIRyrWU!62Utc%gf+G0U;rkoV?#e zsZsYtk;`LbLPAAf{0-Q{EB%wrZNR@BcS{tRi`WxA0RKU&vi!$A^#e2(15Hrxsih|c z2N7Y*+tRg8T%kZ={eWO~kw^8VK4{;wJJL(cIro(I1(3OKjDk(?bk%@8^G2AyV}E@o z$D0-Xnvt*F^a};pz<{0DRR@=noYG4-h>Beuuwe0YGCnD3+SWgkG!Dkxm&wcWNo1HN zS3sx=Q%r5V&mFCxrvMiHi5G(+q3!7Jr)mtfg)5d5b#yC(HV{oMvAqOs{isQOz37X% z=P25n$(#F|*B`zEd7qTjrO34wt>K!G_z)Zeda|^R=Gu(R;*pRLQ%KOeN1pNp^l@_f zVz-7e306A$`_Z*$w(nP6sb6fxGNWH}^L5azg}nZfG+k3d9k55hr@##dTlw9`T1R?) zpVG#r#(H_lbapxPX3D--OYbOhQMdK?Pa5MqTngNZ0jDkSWxx%*VHK6evG-@o=yxl_PS|?=}uFm0^~yy_}uAB%ce-;V};M) z(BisE>&W;s1<%c&^z-@{L`7KfE`-?pWMl*qgwtjRe7kw1r zDcQ*qS(*BGDbUCViF-`H?z43XRDIHo@~`f4@g}*$9QO2I{f}IDii?aAF&QJw zautA0?>H`*y!&O;>O1mi8N!q6hq5SnWo8P`p3s81|eC+ePamiHvEq(7wh+nQyTIS zf@}e{O|+wwLMAEWZ{4OCqIX=;oqWU}Ux5J==k|_xusX1svCrC)jXCnKE6Yv~>FI6hOvYps$GOGREInYRlJ;K2 z$XaByEqvpYgzY!MY|N%BYjl^?4~5S+6}xKQY{BJRdDS~}|6BBwfamXXOLgm>k_2T_ zZDiw!@s5!OBr?F^+Nxc}Fg>`M6Nybp=Nsx`3vjm>V`53L3qCLS(7J!OrK89DuEw7` qv(dzSU3SlD!_DZ1P+T0!*La8Nz11F*qY^jIzA4MAzbt)W2LE5M3E1!e literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/eth-tx-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/eth-tx-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..89b2390fced1d8131a30b00ca2c1c3fa19460163 GIT binary patch literal 10643 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/goerli-block-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/goerli-block-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..b817b32f3daa96fd6b44241d43d08b711f27dd6a GIT binary patch literal 11360 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 AYybcN literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/goerli-block-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/goerli-block-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1b3b7c446dfe92747d7dac07eb7c9c3de51db412 GIT binary patch literal 24034 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 TnD8)|%5urr!l=~XPUQaq7_i-7 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/goerli-page-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/goerli-page-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..cbab3b1720891261a2fa2bb0752bc80f3d736a88 GIT binary patch literal 13387 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 HzY6<5>+i3? literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/goerli-page-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/goerli-page-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a28940e7ed629025e0a37352146e223d935411ec GIT binary patch literal 29168 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/goerli-tx-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/goerli-tx-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..5840f379b85ead8566879565ef78e65483706bab GIT binary patch literal 10309 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/koan-block-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/koan-block-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..33852e377192b1876ebc09429c8150ba56c2a248 GIT binary patch literal 26425 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 SuEeBflG{3l+Lf9%sQ(6cTjA#b literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/kovan-block-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/kovan-block-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fc774c44478641208bc09e7a310920d1b51f38b8 GIT binary patch literal 24324 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%0Ds1Ov}ch0}JBsAv*(~3Fnchf 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

    ~TJx7XPM2~%}wvQ<<~ zD#*TT2`dnRpHNTTqxgDu9;Z{qywUi#i67kRQ|9cVL3as7>RwZB9>?iqc}iw?lsNq} z&nPZbth#)|mtJ{7($^CoIWosLc&2(3l?$x5$RUUzwa<+IA$5{^a$Q#+Wfy3;OK$!l z7!TDmAS_-y$kTT{5+?6HlDOA{dVKiU>BH3$7w97Bi$Ws5rqR8IF{}vtWH-he?uRAc zvW(pPc~w#bE^`;}R%&tP1a%D|c%(FopkAusE*4eZW#diQTQYyENnsJd`J8bLFqgqx z`#+XqKj|(v;-ZN!Ig$R7MLH#NUh@U?IfySw$~ zvJGR1`GL*W`V5ep$zR>ndsM~qWnH6vm*dL8u18ZT#W^JiZ~7k)K@T(Hq5M;UXsU8` z{qKS-e+QWYZM`+&f#bWVBPv-NoMQGn1fC*XeE75+5iL&*6D=iF1`2Tg|2-DwnqAB2 zhL|=>H5}}i_v)kqgf9PEPr1buVmc|a4FWV^_x~bAw+i|_d5Q#IX^d*+qTdE!E`NXV zp=ur>xLX&Vetl`ghJfCul0*v;o~|O-+`p5drZ5=0il5cyVT*oYa{ek+7%n0 z`D)N59O;gJzZ~#0xT%&ay6k7rw-@@2?%d+pw4b844nKXLYz)ciX^xOBFY^Bb0q#n2 zTL2Kk^AHR>P`LEG+~?sd;tbo}nBVv>U^feZb9-BN8I#Ok?KChH?4!%mud~aqmJfU~ zyEo4u_q$q1n9HH{4L^}!YIrj?s|pN>QU~Eu`WMf361;e4UDJ$;K8ikoz&%`I_Dj{J zN408gUwmm7;TL)QU{^uS4ve9HEGTpCVxev+&2E1~qguJ<^r2)BFCX9tbZhOPY42tc zpOOA7ZGO7Zy=r{kRgN2k-E=sKjCyd-H#Mz&of7BId@8Au9`hV%e}2@K75(=XzJ;jl z*AM!d(cUie^MTga;9ILoZi+_A?Gi>3pHjn~9X|p3VIMMyOqia-8D;AMQW!3o+n80D HxIg(H6tx^Y literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/kovan-page-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/kovan-page-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..9611a9e967b227578acb5ea2133a975030390605 GIT binary patch literal 13510 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 E1NMel?*IS* literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/kovan-page-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/kovan-page-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fde639eb8618efd049261d84e011b4cc521742c4 GIT binary patch literal 29445 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/kovan-tx-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/kovan-tx-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6c6c73a03897da6b4b39f847c55453b2cfe081e3 GIT binary patch literal 21863 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 YO(-xClxeI5Y@jM|{*0Rw!ND)(e==QZ{{R30 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/lukso-block-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/lukso-block-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..76288ee739b27bd8b535b30b96e4a65f77e460e8 GIT binary patch literal 11875 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 eraAa literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/lukso-page-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/lukso-page-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..4ac9c923497bb050153bc3d6c2a97fcf0390703c GIT binary patch literal 13113 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 WG8L=j;M=SKKwU-qajBAZ`2PTJOFnG? literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/lukso-tx-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/lukso-tx-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..b769c0a44424d309b591d392f151dd7a891bf3a8 GIT binary patch literal 13640 zcmX9_cU%+C(@y9Rno@#P5fzaxA|h200jY`tDj}cgprZf9qnoq2Y5W-r#t!ia}UlnVd=@R%6izR&o_002N% z4mQRWgcp(n0I2hs+`joB7`UFr`bwr#DB8%u#U)w=>~&KluBE%X`-O|L!OgVKA)i0@ zoWH`67~s*pG4K7dHqWjZK38=|r!1`e@S}ip;tx-c;My>YAoW%y0yZ36KkAWb@V#-^jBHX1GeZdf)P6($S7VSY;piA^!#>Jmn?6+ML0*&Q=7Pjb33wXIGoFv#DS>+h`E9n+H=eKY6VDHro6 zUEcPpTxpjoeC;ifI`;_H_FyIz$xPI9P9#ZmZ7JwJ^G`fs3Qqg81vz!4y@ncpY5>dj zL+j6$&lwLpSeyM!4(IVrPQrk?qY;gBhS3}_14f2B`k$N0xWTs0|BfM1z;UJ!nS{(w z5huu|%07Z2omQZ%>EG=JCC~e6$9Z!o&;!X~SK6P2T>1L_OeOUqUK1n9ZLO(0X28)q zD+T~TS*H1BhpaJbX7j;tF(_6))TOh0e40 zz%EPHP5811o<*%MgF~+>8p|WUWT1Wv-OzT2#bdaS% ze8NyxDcS_e5ds3UG>j z5qK_ClVIw&6#|Rt3~=&Mn=NP}IWQ&hf2bsb7O1=_Yxt)jr>nZQ9GK#I=d=#ZDnJH@ z-F60~3zbxD46+oo*1L7SLAA4)MK~wv=!j_y^#J_WzSeE=RMb^%OrFXrz6`>weM<$T zyHDZma}9;q8Ba~>p0)98$iLGvr{zD4qMerE-@6xoU~0b7iyS+5EUBjV(GGS&f&N$% zV_*wuD3gCKr6W{zvQJW?PMJ1Ro(kgT&%xGCl%Om5 zw`{sv_OXeqHLt-Mzj_v9>_<@-%8n;ZsfF4)Qy=!EH%+sVnbY*N9 zr?S)5o;b~Pr5F?w%C=DhrhUZA^u~do9HuaJh72V6`m&C`sm@ z=5dY@azCMf{rBktN`O!_>1wqm8I!+%CH9FjWueEDF7Y-RS#S;VI+h6;Vj>09i*Hh7zZ?4>@w#eeg zOakg|$BLcSCxM2p&Sj3Geeet2(4_CEyQu`qH-RcA&& zF)++1%@|%{^dc1u=13!&IVF(#tiZzZs^E&Y8i&Wd(8R`LzgdCKd~rR%pI@+)N!6n< z!pp1xr>aFIsGXW7O#AiqEAtNYYlmX(;ds(77l9+yi2ufx{4T|5Ayhl?naRpRx^DLT zp9>EZ`mM%zUkzzK)T{)A|9`xcn$Q7h4 z`tgMgx*XIcgV1#q8AATXU1p&x9mV0)Wz~^yqR}c|Y(CazMM|Eydck96ZDu>v>wd!c z)VvRbn${lRlvB~JfgAX8oy9H{c^3I-rm$QoJ%v;0rA(auI zB~A2xmVJdQwh|`qJ*vmlRC`HQZYMM>U)&jke(2tE7(q2E!2k-BThvc)vj(5Yi`wYZ z%{PQ@h2ET6wCH>I@Mpo#;=pT_+o*;YhKZ!UM_@0{u-0SBR`Rx7M5hy^A!ZGKH@z!} z_3@t|rD&?y6Y22BFln7-dJZ*X2LH+NVb33{fF5qg5Js2!k~ErXLrIC!*~% zpr@}YlATTS(V^D)b-wq0)Lj9T+RG?~FyCt8d>t^1`fdLDZsnEsxzYXrr_iy>&8hmN z$Lg=-ggY@ z7(uC~vRV2C^bqO8A`mr^5}^|L)(*<<_W5Q%4`&E9bOnR!8PM60J`R0@tvWTC0taXB zyKiX?&lY|I-+sLdIL==wE3uofwbcdLHR{`IBgF~1xk{Ow+sz#ZiOxcnOKoAw|5{0; zpIH;SWL|!<@!>xWQ%V`;QJw3Y;un7CT|PloBWbvECbc;Vyx@X-kBp{AtA-s~Me zZHV1HL<%-3C&(*;?^>udec?TGr2GYT;hXfm(X(`&EsCd=_6AVG?+r}(nZ@H!Io*5$ z+bBzhz}`1d3eN?gqdA*TC+qeY!HE%%o+@(B5=ucaVeAk~@TaO)H}SQ^0j>IiVU#jh zDigFan*UAtA2yOd;wy}AC2J{64t^561#Bhoo?MG+3=x9rU<`V11GIbmtiTudPtzOy zGFYUxij`nhRARU3Z+2Uv8}^tBK~09AM0cmDU4%CZMijQdpmUc9rd!X>%*qaY;Ml6y zOD+iBK=S-ni!_v0@=9($*5#4>YXlL&Cl5H+UD`U?8V!5`E})!V&U!K{m<9}(*&%2? z@Xw`U*Rui~1s?ruGch&g8C8Ofp=kj@L63r3Hd>~pB)EQjGh7oc(%STjGZ1r5J!d}X zyy=U5S9Ib zCyf&wS%xWEGQW&LmeN=&aWux@yV81F?k{Ughm*(j5bO#bk>>Y_pKNFuI&XLiXKUt= zSCZLu(RXxyxc_Hi>E;X~8wmBp+FJP;U)mLPRe*H;L=p>!8~Q7-34oe+-1ME=Q4-?YS?upUb8 zy7#V83h4tR*9KOs=*_f}BV_BavawLxL*npTV~a)vz4jbAYb40-^%{OCSI%z^?}q~y zgV^Ey0>dbS+W_bdH^{4h`Lz;%WGgm!$v?GlIZR}rPQ~KcZV~Icg0nvfmXvW4Ng-!= z!XaHvq$&MWr@=b)zUPnDZg&C`mMlV9(`sG8r=c5sUNjK9uq)av*p>OcV8m3ua)N&8 zxqzAXv1%0a3lm_lwn0x=pGV2O1W4>lO-6*uUr|8aw#q z-+yl*^?qHbZBUU63d&v6;(a@eI^gAzf^AKpRyq|=zMZlh4V;;cU26ftZ4oDCXJ!FX zeQN6Lz}*h6M@Q4H<~L2SMha(QNi$s8e^`$li_SxIWK9;=IPM1=*nsGG&CSsujvO#vxYo#Q5BNW-gJX2$)kX^DW7&!<~~x?h5T5nQZq+C z)mb%&qpcP2dzUp7n1+Naa3sYK`5;-2b+gAr@Oc)hv9s{gQ{O>55~3ClBqZgAP^wY$ z*sHU011Qi%Pmar(mANnbU#Bh4#;RiVEq|pFNWWijHNl~^!8XcaKKp#rVcOO5mhUYj zT0b6LW}VWZ{^Xn7j6BJe&A0`|_~zM+*CzSAptI?I*_|+#Jr2)Ul~d8GHu<2GsR7SZ ziomyVAP7Q4qd9je^NRhZ^p+^By*_ryoh!VwvFL>5JKrmho~o(${&VM;Z!qgRa;eQj zCxfW1;XY`UpT3qH)Fq|SY8{8)kMLE4^v8vJ8~Db#X9hsHEJ6wbSia{siNJoEAOUs0 z-nZ5lWC)eI*p5{!X_vb6F2Bq5(pWW43Tb@dqsHx!3k|;0dGeG8F<8I*2alLABD)t; zKM&3kpdVabNgP;xQ1TQ9gFQv1CM1bL%+&q^L^dm-Xz}c}2QPk9@WCE!LbVH8nYywB zSdDRrBv_v{SxUP1MJO`j{jJ)Q4oizUlkvIVrIP|c-L{Xud`W33Phg!HzXP3G8iFOh zBwa~}TN?+2*TWDbgyc;u#meszwd8;pHzc^&yZT(ddzJlrMHp$T@x8yV-siqc(b-YZ zllhRrF8}hf&;`k9N^AL~0JpBA5o{2(!hA6*5O?KLfP2*`@hBmNW5%4(EX<^kpeay_%qNnuAQ!3r! zQ4h7L#Q{zaWZq4nutMKJu8Afbe?(LIM2SJteQWK%g97xEu8)d4V8OX^!ON8JO!axO zl23Xb%AKFNze@$P!h-3KatN5b4dbYxZ=>cJ4 zP$yvbyCw_0To5Kj3lCz0B^Ch4VTS`G()qS#H)S8{=Y=;ej`B8CM^W59D1!tiK zm?r-a=##3v_R(O{Rv-6Y@d6!(8lrsjcnhX!;ib@XKZPIcil<$es<(%A1Ps>wUmnJ$*%VqTb%AakG#q>G2rxH7r3790CSex5E)@9 z@nHaYehz1PUewa1+Hq?#9*Ct=7DZDlDi-b(jZ6MvkB(E;$DULEkYL{D~ zticA7`MUa&OP)T1)}*eE0^Cb{Aq{7uNc3@WUnEH4^9t>1;oqs2{0>UWgh$6<0mxgT z%DVn#>)*-^nIL}vHH(AFyL`&1XUi1p>S597oz65!>Q#35H~RR44zCqqVHID<_q81= zO;-0Nn_jMHq+efeoWUH>$gx_wk;=y4Y5f2v)jnAi;4pB)Xnp;5uH{plE3Nsv>cy|d z*1<1~X;Qn?!PS5n7dG8;qEg|{a*d{XoyMz7xY18anb);7Uv!c4M*vtE9O(^%(<+(> zC8L7;n(6qd4;v9rl{G{bR`9NmsOhTIZpqATo$l`Gjk>4wD*dR4hYVCg?A|Il1^cq# zNC2|d*F}D^RwIIM`4L%QYL^z=d*qN@@JiEVu&eI$(Or1b(i*-;m@e~O;jfrr=FhO3 zE6>s6Nh>SA7i@ncl^wt?r}Y#r2omd=sS*OGxO58_RgN4G1$|i>ZQ7dN&vVdGrKj$r zIu$IyUTnmcPM@uVs;TY+b0T2!NIvv#5CnX#$fw#MUrAi!YT0vo<&O~r#(?QoZlSsn z3~O5`O&Bdak!?$9XLxE4fZm+km#L+Nkd25x^L*e`VdmaBYJA~c@buNII+p@pgtFkg z`N&$=8C;TpBjr~Tf`mp$g=jC+pS90uq=`S=D=h9?=vQjMoIfx8^O)|WXJ<6XRxN4zpxS{&m#@qseHdl(G}^h2h5GUuZ_f)p@OQev+s4 zAc^$_7rSfu=A(>Mb&k1CG2LZ6gVZu?Djax2+=!&j;v;>Dj;qtK>ch@B(Z@W-5P3w;00H5?Yf$-ASLn*K^pO`EL+#0RXX2BMq42WF2iTTC)$awTFBlJaTfI$AhfaIivxq} zLfWaRd>-T4Q4#;1rx}TylSH7#O&jvU}Mh?5R=;f)0G|TV4kq9uO z5!L=g^;X4>pi+5NSQRB4xuLqaF*ahobYDuT7q_mp-<7fS!PfL!TlqY_Ln76fmf-F8 z44j~T?R|2Q8$8Mmqs&ps!Y7?;`X*gEXT85AZgmj zC(z6VN$NUFCwv<>I5XGsyT3_E8w+(HN5Ml*0kC^dojJc4_4ptx-FYWS2hYsdV0s>0 zf^>+VK&bAY-p{S1tp{LCu{m+8_j$`7Z;!j&pz!fKoq?4frjakeFoWr&`3wBVecW|# zhfqsJgiI;d1?1}o$^GE3Yp)hvT+(&yqD8zO>h{!Ixl3J!7V)A}>&SWEJ@_k{!7pW3fjOfAL4@zcN zXf!-gQ%~?VfPM)fg<|kC!^5_Y9NI*79++)+eL8m(S`@W2Mv(MDS8j|+cAmx>hyL&{ zm8mT${C;fq;Ik~wwXH_xu41I6l&EyYYOB1KdIhNWu>8`C{UDSRLiZ2whz}u2RDJ~{ z%vZn}vRg-0UAE%Orlb}Z%y7ZmUO4}mxWfD!yWU*d3F`7Ub*P7mi2`;L$4~d)8Ez;h zG{-EM2OZ^}agH5Axx${5W*k87EtitG81@UY5`1cnF72+;nnidHECNSP#{b02Qqiwa z$x3D}^pPg#mHxM}+`FI5{hpWb?5EKB=ju)N49{#93-}t+-lvb$g9gn`>KYc}NpH^q z;Q7pSQr0h5kHl@&g6lj7kMA!^Ju>CKSaj|c`jt+BLhV|C8cy%=HH&H&*$&!Qh_>P3 zjUrW8eZgmB1o+JP`;0urOB4Ptd@aMe13;ZC6RdRDx1;HtbS507R8HI>i*Eli*NL?) z#im>dw*Mws($Zz&Q|mVcvhh^i$M;9wPRC1*Z(oZ!`r=nZ2Oqy5=EjYtDQPcc+@Biq zzIzJ@XTc?TvcLr=NLlS^T(oBEo`1{i=F&mq`^%y!@bjTfnvQ7lLQNm@(-X|Jqp6FUhXVl=-P zW!rC@(pRn{l!!fZn9M$aL#m{ayHx!Ze8bz65>1dclcw49q24l{>^jMulVU6-+<%|T zFcON4H@syC)SFr3nrf|(XBc=LmlVtmH+kKj&P-nN{V)74vcs0rYh2yjD~wp97rv_t zW0YXV9jz_!f4(y6~8r~fLO)rWhc9jnmtFfv&5MB4z1yZ?(`J@H5MPh z=MZhrk>X0#=PELtg4$RxOdO zTcmKL$(E7RtZ)KsUHpsWWq@_=sw@VT!vnzt~6KkKb}@0h=K5=*k@@ z!GqJd*mp%B*U%A^r9|HqGJCR~3~rF0tSC+nLBGnsL^O(Me4??~H6;zu<0k%x_eC@U zdif`z_Zh93pXakav{4~%2?=Xq+NB!7AXJ11ggVYzZ&RS|QS+caTz$*S*&NoohQ~j> zb0~dNFxZtmCm7{zRA&XT6Wb4HQrCq0k_>)3mG9e=o87oVZe2x{$-LJ(cJ7NX z2!-MNod8hw9!mfA*8Pp>f4UL8?|!eU&1$dUAKPRpb};2bn3v5ulsV`$Icnqx$_QR1 z`(g+jb_*CT63K84IcigS9_Bw1Ho;;)m!D*yPtSQ3owGudg?t~c`p!N68HnEQ{ngs; z{*)ek2R!m(x1&q&l_Yhh$jn^kA6 zBjm()!5iy38d@B@syz(B1yxKBF61B|x8VLRRTm;ZGd;9h({(Orb0nD;&U>G-{?EjdF%Vx~;RAktsTchKm9=hnYiY1<%x<)O~X!Q*mu zRL8^z*&d|c{2((rH%F2~a)0I63L&-1eQf-`O}#xJzNmrFD@~-CSYJQo9lUifS5;DH zdjH27{u)ef!})9L^w6vt@4J*oWlRykgP>1}JV-qe95X#RS1?qV9$nI02Wu!FoFJ&iw?&b4>9Og#InEUvOaB#f*!Wy1E%YWvBh!cX#nCFTlXQ8&6B>iTN zIJYRh^VcVY?uXY=mbtcLB+c}w2*wJCZLXcMIrH)8$-R>M=Vkcw@{<6k!Nfvfu-56A z+t%4v`Hpdxyer9{0D8{kLUsf3J~d*=GKEQfUcRV}Q2Rk7ZXMW<`Zwg2Hh1G|cf9pC zkiq%9;>u6fv2@94`ck*P1R*Rd_TL=BD~$iQ369Y?bNIEr!XT;s?I>!2 z-CFcy6)#KiI740QXn&KqogYG`MZm7Vz_rIbVWRVM9Xp<`TTK43{f%I`h^(-)Hcoe6 zMo8)vV7h}f9MLMp?`zKr)wZ?Te6(i2FwsA??LQ;)F+0EsRu>&G6Vt(c{_X!HhQn@; zlIB`|9dYhTF(2L`hOJY~dfYV@QX{A5OhV9PO=&He&Ta|YfMO00-=CGl^D;Ve zmg(##w4}skICa@r1?O$@rI2X7ys%4q$6;j6|5X}lqSovxcP-t@BcDOH*X*-AG|>A& zU7r@XP(OD1P=n3Ocy@IVEUWSHSM)1uBi3CbB*(5EAe2kj-GbwZAN44^U+{r@$8_Dd zjfBpPt;)Uz?l}{Xt=jbqcffTyS{H{m?B@0XT)Oe+Kmr+{>>H3V7mVDIC&s8YO*rz+ ztQ2|5$SB}H#W)!m$4)0_?(0QM*B&GvUP*aW%u%V-Q$H7O8SE3KG6gy0a&_EXoc(IbW+$J{Y`8 zB>;l;rOakwwX!q2G^H+Y3XJ8j8OQxCiE#i$U#*&XGn|K^=9>j|$5*d&phMm6XZ`y$B(~5y>#C;KkA$FX0Zo5=>Dov zvJZGT66oY=E>aUhmW7*xq-CIHHzZg6@)$ZbEeP$FSzQt8zT}V zBRZ5!!MYdPivE3Ex(Sw4hJx={8bMthYgO5wjB7R4ru7Uc?ilbcKgP~R7?)EMm2uCO zO#n^pa6Te)uAIHu-!M)ccuWFijIarFLaQ8@4T(xmpd?hiWUi#I8(Qw2sq(7WroyBJ za?q<80}ztV@yaR{3VU^Bxr8q<=F95qFNAvM!{)E57nzoizDcK6C@yb*zZ9h8-Xrth zqDm#URt54+$$oWtOQCeFQ>}7)^JY`lsiGQ9lI1Z~ou(gURPovg)grkv=O z(dfvO`L|MB#3#UQ75&}{%_exoE$g(Yiu2nJ@J;Z@{lNJZWm^6$KhEGD6dxUd)?*^p0V`kL0gfG)tFv!m3tk1f!lg`oyqN0S z1t8S;P{>%|_e`G`O~)eDYumGKj5OgtL(9)w=7$b;@}cys)iA-4MbL5kcg>t5lF5~U z^Ub;}wgF7OBDe&=mv@M2p3tC}BEqM-3jo@8xwF~osL2Jxjs7`VMheVWh$+A+(E$0K zW$AL{c$`wEJSpc0&xu}+HQ(fwj>}$m8JHstND}OSxzcpnKIadn$cM`7i?cb(09*x& zjXcCfYf++oobLMI%nx;9Y&alBIh~0aZVum!85x-D3M^N$FLnT5fo&gd5(4ltm%anks9%(S`Gx%la%&7|sS<#%DegaS zdMNg!q+hT*dXi8g->E2E2J#aZ-6PI*y%BrRtC3a6*Lk>9v!R^pp=XcuN_xqezXn>~ zdN@$#zW98qN1WmIqI+RD@o07mMtYE}2mFOJ^>o zAONb0c+3c;tFmt&XldOo4B z8uk;vCHM*F3We5k=h|+VKW#+qo}ZP^_*~;~;@I83Gah^Br%zs8o!%LeP*$h$Y%4f~ z?{H(pXQtcIO#JTV_01WLrY=kiRe;oZ6QIB9r6vsclEyOr`hG^MY;4P|^Qc@F*s009 z4rw?*_U_Y8G!xPd4v$m^NG3f#E0nat`be=s;p{MW?V52;(^Rx$q`SsCDuRs^yc$WV z-5*LyAiWe9JIQpipNwN9T>T9rTyc658Pm)<@SInWv>R6&O>Q&WaO>c+_U7a+MZ5&N zd|=&veonT!!9@5g$hHRkYKNVXXi;rrp*Fb3q`>v6atSR_{<}1xWy;vqkqg4=?B0Nt z?AIka)7?`b97Qgza^~NFiARod#*fVfMjZ#*3jQjyK?#gfD7a)*OYU~T+BHmV!tO)u?p>y!S6Cg+8ufp)Sg~Nx-*Jr+QL1E6O%l+ zUh;m}koeHY4mqA9xbET6m0gL^HMTdW zGS_=0KcIJx>tr_4tqXcX_t}=btBYdrM*BZMLcJ$1lbb|Hs6Tf4Be|DC=peo;)7rBbF%>_MclJg2T8a7`E4B!Ep#l^~Y zvtHg*NAX$tiBf14*}Gw-8!xcVfE9`+&ynu$MgrwpLehpQ?MQc*CNr3df%z_ewe8JY zv`<%CN?6-*tt2wv%7Js~`Hi)Rq5oYdL1fyTT4WQkH21-QU->>M00Xr1Kpv{G#&-I@ zmR@irclVKs3iN3@?!1TLvg=8*N0=u}|6M0BP+x*$J+|GAuzDkXB<1exrSF;=iT&S4?Z06G=1$F0GrTdEV5^+ApO{4K%I=z**}Nkl zWCpX+d6Q#c;jPGN8Y_zP4<}f9sp%}^wHQ#unMiM6b25#)S1PR>JV5&CDZV<+;0}hy zYuU0#lI%A51nfWqAp>l&cLw$^OH$>=l;ah?Psu|sp})1sWykY247-W;`o~$XzAaGm zrLE0>eIq3K?9v#A98z-$B%f7^s|hw|Zkgi!4j$n#^C2jlEr(eLtrsvUx0_93w=r|f zj(iyITM`bjd*GhDWqjS~?%KU?(t7=Zbvh%0eWW;JY)(AfvOO)Ipb@WHk-L}-j|h8r zojkK}-O{G%#*NyNi$9s{)AN{MK8(apTS`#z)rq+cWAW2HfPxvSp@D@&&n^wTZw{Fm zCoTOA9qcFF^V?KNS73oP4NQNV9s2fi_juGBGLz%8IJ`68X7|SBRmCbVBg)e5&Qt6v ze@J27I}Vm9&hd>NcMY(z&$>T@`bbh9TpIl(Zy^0=6wYWgP6| z1ry&1BT<+<3LCXWqMG-ZKC*7C;jirQe~!Ez8)NPH+O6Y(HG@@$0VlkNN4;^fR0%pd z^R{wDhqE=dzR!@aEx}9wSN*%K^HWNtJ&f%J=hYtRr|Qn*q)tZlAVbXkZ}V?~X^IB5 zj`_mhh^$fBveGj?CSKDzw>tIlo{u)KFWAHs{8Sm0rIwk3S2V)e)ajA!wUNDfHMLRmW0B6s6)Zwi7 z_vxKf5!)+^&Kt!hzJJ+RmhtHuTf)x3fy7$_3ctKB^PD-#j#o9of|+$`_QlU+dHNI9 zf|hE;cvXHELG-K(mOl|)A$B!74oOY=GcQJn8Xu$ACrMjPWrSa{YbVDQ%{7RZnI^y^4>p5tym0D4}`_39Vg|Q1G9! z95X_q+^Gp!tYYE@5ON+8+=ru>8aTA1BagBZ_Sp%m*QuAjXigiiF303xL_^_k zO&*G}L8y`(Emo1F)advlxESrIUiZ*5;OVhi7Mqk4+qYN$j>@mwlFWQt202XeXV>tu zHjv=u|HQVzp$DT8P+R-I`CPxw`E$e84H_4eE^J{RV%?|AMeKXb|pSB^|_x%k#|GYITu zKQ$Ln!^yhB&+qRsv|5#lCM$dTJ27DgMST~&Ym~t@zi;38u7B-P%~|TXvsBb3GAWEi zOgM*GSBlrclu9~fRWT(gx4;RCkTe|WL$vtGLIm`~V}%oQju#rQ5nN})$Z@&nzQi36>{}>rjhrKG^0`M~E#>Dx*NAN54f9>cs;~Y_=~^Hvw<5l0O%`veiZ} z-4OK+R-n|~)=y2zy{tl>xkv@_R4!(+V+G|5)D>mOA_QFfvl~9`&h_cPJ~eK9ZwD`Y z(vdWRD|_@n$460eg(r@T>gVTZn4YZY_O_n2azdG?{FW8ZX$21W+sLFmooEUTX2+&3 zhx}|UvVI(yr16c|TyHfR{tsd|v>v7%NOx!CTC!PlV%$0?hfMo4VbSeYOWW+z4&}#} zh06(VV!9{##1O>ep%FEA`vBq!^NnPs}?gOp>I5_Iv?OVY`+8o zVV&0GEqi%>HOw;p<6{Ber(KVcJG)~$45W<6A!*UD-jAAdLhm}zA_=7i3e{{HzxW>g z8kH94ziF~&)nogJAI9Sz!i!lKqQ0>F8oY>U*Xl#<9d>@xk+gNz8A3@dG4r$N{!FuU zJmVCZUkR$Z#EylF!PY5k9NctYG}}iQ&u&KF8iy{@p{7jJ3%67+_%#vHzMv1;F$90| z4Am~kcTAFnG~^6$QDZ7$F$=F*EmEeCw|(P&5w1&Y_?ZX{-D3X16y^Pj9MZGJVE3YV z=Y7594jNgJx3>pM()1=tsY3uS^sh@>3^#0=ets&K>v`l}F+X0nNVu%mt_^2GOD`r#_8yT2-g2^8)c|QX1Evwc3!IY^T^+bknoE9>DR^8Zq|OzRsx0` zmUsAr{L^rT-; zN|75~tgxSy;)#6!HW~huX?Y1V!*yuy#(r^0aKGxV%2ot$@kGL(55tF6iQt{P_d{|N z#R-$f29(9gyRlbR79;oOjv`7qtWOehn6OmB*?h3I1XB)4Y5gv^4Xw8RlfgIcwaxD; z?rTLmcE3KcB+@2a{T2B754e|$?z1m$9c<#YnoB_^vHmlmF0UFON1Hr-%2T)q#Oj0*7?jPFOQON{P&7;P>QD{BSaeDq9T8SPy zfMOS?|1<0n4u4c_45JLR1B^j z1$Vx$U8X+b{75}L?V$xB5O$vDH}r&!7jyJ(IH#J5E=q)ZcC@L)9dICvHZ_ zhr|i>g{MJBUJ_jQ^-dwi4V&${N>nsuZfQZKZdssJzR43k+J5(|^Q&$x*t*nLu?7uY z?t5^-&O@A>TF=7(lpI!yRzcx{_5+osNdrX>YhrOJO4|0)WJW0E+^*bps_S!e!Cs11Op-%kl2# zEH|G0u=RsfVA8LQC)C-r33{Zz)7vGYY)yt@EpYePwk6RfU~9uFSO(JIeTz8p@AKGR z1qw8(F03`^;Fieg!T3mGUN0YH zjb_8RT>mKrt`WbY-_OSggI<0rhj;Dt>)S5OOH&MyF85kKXIU)cN#`JG1B&ERf$X@Q zj}r_<8E{~bL>tr$3$!ty45SyS^t3kywO zAD=K>LYGC?0?L-RXYqnfUZM9ZXn04O>@#O)qIDJ$eEk+N?`^U?$^R}GBL&IKW{mCN zd)TyYwnMw2e0||-dNzZ&c_VuR=3cn-T%CL4Kl6(-l4QWm5PO|PcLguVbOL4c{^Eb5 z#d3VHYr+I9*~d?1*vUa>97ANk`u}f5q%JTY75sNoK%ViC&wzto<$=KBx4932`+gks zu&NI6{|3B;pUylJ!P3=h=VkvD6cN3E*)`j7*6ybe*zQ6Y$LZ00BxzD-Ib*^)sCok`q=`%B2U;u$Y%!UTH%|RfFFc1jLKt}`o zN7$c`2m*P0HN35L?>WWR3}uB>5LegkUzFz*>h)dVhy2$WE#OzVXTXY*$1Jg~mOpPc zZ-(r$Vj=3cI}#bdwkkAo$E4*^{_`Z?G;NjE6AenR#I^Q&*_d9sCXg&kKqJwoNgvZb zuE0PbuGLS=KJ?Nz1Dx9JI^rLnDL7t!ds+Tih%$0Pwfd2pJd)LwU_)-Wz9J?6apskz=q>X;jBoY<3!V;sT)+Ryv3~ZG$EvB0|6uza~|_ z8#zM=f`O|dS$`o|-74+ikNl|4T!FV=?L|+NLj$AcD1`}reWvCW595c(`;!i|bn2j! zo#$<`MEAsIVG<~TOf+|GU}<}9<{ddlX%M)UmAU$Rg|I!lLZB*~ql`raHkkRIUwetv zL;0*}?#~SeG-^U6npIe-68lI!s0o!l#~B8q@jL1gdshE{f4ncWzLuUeQrmI9#P#eE zp-W_72{jQCmC)StHdy|5s5tO4QtRB&-;#Mep2*q3P4M*UpqNE!narA>|Mx*UGFMfd zKc*aN#t_Zg8nHG0mUW@=zbzt%WQ2nZWn0`6>DAk4vquW0I2W4#_s$~q)Ydp8W{wii zaU{R=`g!?(djZ={eSFf4;sv%%8G=b6dwItoT?&8|!bBBgM)$-;{;+6Pwj=Ik>i_?L zf?toRxxJ^4Y77idp{-FXASzl0P4))N)ZD7eQXYde0x6I!Bty!eDuV{b0|%uiZ~5jg zS;4os+GyCjKIwo293C^_eXXz>H6azvT68odb1lWVb<$ywPCc?$N2B_YxZD42 zX>S&hK*;^3^GpFL@tNBa7|y9=jZ>(g9BPJF;+2qN2$jvD6Z(!#jVC z2%-=p-4`VRo^=_69J8I?zBf)t5weT6V~=LlMi(~$M-(DdGHSdyL&$uTH^ukq;oEkj z6%5+CCLLA?M(?ag7ZQHb+dyGE-1QEC6x*@qr-8xqOBtS9 z3+-RC;iQL;qWisGo-W{?=w9xyQvk~nU8YKq!_7}qMUtVp8x<8Suid1N6+?w4d+W#n|F`3+d`vDM{V5!9NcqQp|ste^c9mSQq!a&tnCk z0I9e&<&<1IF|I zj}h?IsO$i`0MmJc7m4C+v|B3#ASux9iPrE(*u0fR9egR+0p0vp*TGZPJ@Fqe`^l@w zq4Fy-mxDch23fcAb^ZB`FX&APxX|d>%QszcdBpP5H#<0I)XLs`t<5Vt_#nGIPw|Nh zQ?*FhKSZGg?h4U=S}g0ws10_3xX43*u9pvg+YcJ)g4vm&e>BRFj|vcZrf9(@C|?PQOB7siM!#=S?Z(y^5o%7we?lI^^o-OL*l9*%4$@KJ`UDhQ!GQH z0m||mfYb%dWLoZDB1tykp9akWAH?~}<95N++IuyLfQP~(B*IM@> z{RhTjc29R6M*DtEA?hr;<{M_AjgiwYq$wnbr+ML5PM^|g%XGS_=XPPxMNjT4VGA_6 z8bTjiZ9-QGha;{Jza#AsI=|No`xDCpKFFr?S2}QgSRuS)EbBXnUbT=V;TIOQeZ!94fIg50~qMPYlRc(8HEl`x0KEe-Gm!1zO!WF)JAQn^^j``s<> zeZP?HO=|gD?LCDa-0JVw`r7QYCdWKvsU-J<`1+o_wCz+g-Bw5%T;KhErIC7UQ%~_9 zQyk2LldQ@{>j-Q(SUY2Yt}3-OCitG1-}0?|@9sYc(uiol(n+{#iU3zO+CH0D@$hlQ z8H5~0X>IQ^C%g2rn=bl=B&ZxRN(WP^vj{L0P6!_#M(Ui~fBAQt5)Zz6%%ie@9ahlzRSl`Hu62VU&Eon|c-zWXG_V|dhB@hS zZ*d_rDsl=~E?33NXwJl#(EcW!M=UjS%A=BY;J3eM-U)3#joIb*_?P|bT7|zc4mq_r zHN}t6kP`*A{V(O1g*@qsrCTJ~DQ#1@vG~@| zH7W*;jd8}=DkXTH~dz4*asENVX&u%caK$drz<6xKw7XOJXXyB^i2rJt$hz~LQJqfI8JDJlNx(n8JW^8nQC)fD4>=O8@*LKL#Gb+aEKB+ zqgnRXz844kBSSej>DOGOp54_ru>Pk{Rt0H^aX>4y8;X+X@-^r6|An*_#%$_Ucd!eH zo}dxUT-T3Zbml-=G_u<2K1{a*4mci+6j!P4ILh;EI9=Jk7kVA3KUIKP!29uhY`vr$ z)t<7Kcd9#8-H|4r+Hmqq+#UO6)t!Eq-g;5NzV?tN6)G+h{rmrHF&}4~%!jt9i@{Gi zXHQ$dt9Wc*a70bmy(^ymhQuo&&nW7dhAkeWct5<0RPUDG!Gevy$Jj+Wgq-hk5bb)N zUBTTDG^%m%5XX~jdIP@2de~NN&zH}3f8K(S+ZxeKABS0RL|&tQ_Ws}V9UiMH#-Rgt`$WWCK#G>xOr%5sQlcMXLCPEHYRmqOSs42c zUmqvd53a}bLxKph`dgI13k%X+NH_)Q!djgYTzZ^wN2N>wd8h(s`e)I7{^n(O44PH| z0Fkscr9k05HGc0e9zKmi7zd$P*lCZ4&)>MgP3klI!@N`VmF@BS--hM1ToNmJ?+l!O z_*eCsYYcP_1wdcYyrreiPKTh>4{?7a^Q6oWb}hzR7H%@HZ%wqL3I}i>O53G7Ia!RY z^%ll^$T~dXlCSRIQqOrnz=0b*1|g12F-qZenQP9|F_kI1N@}y*>!*2xCpKKc6duK# zS5CgXLWq%=pVMVS$Jm;snE)!zWVZPbN<;cL!rE7xvKwRZpGEfxpXRdWer#hL0<`K` zd~b5<-+XN+lfY?mdaYtfnPkmd-Ld`h-s8|4wNzzm2A_>09r^dNX~2eIEdEyc(2ExU zOhcVG>%R4E<=LvQPZrA}T~XPX1=O#xV7Akc?iTab@c>=@s>g<|RqxppQFEuPFMrtL zol_rNhi@_WNEql*C{d?ldEQDAgQCP5Tc>_rIU4&lcB301|K$nn3P$R})!hJF7D(Xe zO)ikcMzLP|`zo2f@B8%AorF~u)qONbM}{bj491kUm*ic@Uqv3%+; z7yKoI&7U5!E61IE`PPEfTm6shUz%bTR!GFO6%vY@4WXNuMs}K)kdI}Zqj_Idhw_rW zLS@F48vj<4BT=(==^%Q$ndp?rDx^>dg*>}AiUo*_ZOp^RA}mzI3C&w2L(82Why9FK z9k|;bzeNqQq+4zdsNFw)>dqL+>9X{gJM}g@iK5=a2=E8(es9aq32>i>51q3HYgr6l z>V|W9Y`H8tC^&%iHW5@+9<34^V9ydKbP*Hri*OL#tTW#S5%MHR!pkUbp=ys5h5mtH4CDqllK#Z z9kph@<-9j9sK~cMy^;>V6ZVb%QsiZZHigy-iXJ)m7A0a9E0x=1>X}j-kPRJxVFsht z#v^?nf5p4rLgTz72}xv3U?_#`xOQ+TPSV z_K+DKfZX1H{2NmEebyR`k)d7Asj_Tm^+(ucm6=x!aoQLhaABBIWy_%A`VI5MF{ z;AC{@Ai5vI4{9e$d#(@n3E0{GSyXNQ_W;p?bpv?r_L)Br)ir;Q)sQ!2uKBJYr(WCZ z@&kX~VH@cq44$wp@W0?+DAlBYwnrZv%NNjW&y*BZquvj3B&3gc_*h0<7v&u7ty?mi z+MJVk3?zU=X<-55e0dyR&WUxpQ%SSu;*%u5qyd~R!K)ohcPr03L54c6wTE_zUGFx5 zdd|Hl1+$>q16f@CE+(WW{`61Jv5IF7)T<2fgQ?1ndaSUZb$PUT{a?5#asN2VEmor} zmURHeR!YSvbMqHNBH?7XuKfo;PfT}y1kC1r;9zm6zn){fA;nZHlEi2|R9a5_eiLDv=ubEYedix*Q z2?OIs)H-y0x)&h(?4-AUPQ|E_4oHjWai`q{j_(@&a-BalS|K$ zHDQrpxymHLEd2!=7Q#EWf`0C6;VmkXc<5wl@#M*Q2mSv09oPL_`AQk5LcJ@;nT0zu^;iwTUW5!Mf-|eS zdO?c|UlJ*^O|5&TCtA+)7X%x7G61ni<@zK#YY%&=An01yvRBn1Z=HTg__!c7oIzKH zr{#!j3um!zLqrsGuL|_qYGM@9eS*Ut6iO}1iAI$}@wzGw$U@M?FKNx~UyGxE=UQk_ znIXGd?%e;R4o||`nTc=A+5_R>4MuJUTU2*^6!$adq0?Y*hm|sCsIgV?oanD3(AfE7 zvUL<$N&i}yud`~809{D-rLb4(7fv9Ld>sLY8O@O^rVbx5{BvFA`s>~UNsRv3u3)u~ z^=uf~ZdM5FNj4vof3^!@rBB4aCl78p-(UR{ zgVbdCLKQ*trd1+FM!9H9gLPv%8@j@&rg1a;cBk;)!k&iD|5leAk=W3=ub-no3glxP zcx*4g#?JlQnuOsIo1*-Cu^e>%wG`0_3c4NH-HH?EvL>{+> zaF5QJZeI=aX`WNIZ;*!fFQ%j%Yv0QqoZDY|ebycxPHouZWFGO*8)bF2E+qq(eiLq) zYJ%ZhA8Ec#61<8mazP{Uw6oa-Gqmbt&UyxE`omU5wo(fcSGYI+M*S+vLzcR!dZ)_C z)lxK!Ol|m&zwA^&&@!*(2g9lTQ&he0R>nkcza)KX_*Ht2mUgmj5KDvYK0>HHPmY&R zebujp4mn0q%DqL6V{OED97Ln3cxJMpE?_Xma0eB#wB$0;$}Uj2}L zc=_^Z+J`jdxbpejlCK(c8j_fGn?N^JNgD5)cL~dmNWFVW`ID6Gu=iUpPuA55WZU~@ zBC!HhVFMzM`z6_W@uOp>``3#ac&9X zK#6MH?t)A`g~Z+R3Vck#PP0JOBT@--ock=DVL4SMN8q?sCb}Svb!EDTTD_@;!Kc~& z8^0f2Q-5RC17D4@{aBeEi2L)HgQ^Z3g}RP2miw|6QPSD*%5i*b;nN`Wp)Z5Taw_et z?F}{hXoMNY4E^nImD$DO3Fo~1`3@GY@Q4bwn?I!COql6v)XtBDd|QF)zcOJT7}IP~ z69Pt^CMi%sIkk&bFcFUV_!_MDTY7Ati0h-HaX+7l^#~fnC0l7i)w0Lod|?kHiU1{)=Q@W z*&wuKs>Piqtb}}~_sNyZ%{G9U-lfj@8isGC^u&+o@Pg0U<1h~0IPs~Q8NP~lr7UTk z6FcYydZ)HA3wjkpe?5HutnsmTd~o%T$ERNjV)c`#x3GaT0FdY`(;1 z!mci6=%cdDoGIZm9pa{{VHIJJN^7{g>?)WEmDZVHNWhNd#EBKG5FQ>Z?qLLJS zS5A~s|M!-)Vu&8cIg0+}Ii4wgzN_R!?vk~*U^E_a`<@h346jPYaEdV4UJC18Z*(4Y z<)0~p-qItTJew*JinUk+z+Wya50`erQeR$JS8qtI%A(U)BCGdBDJ{7QR$^J}tG)i% z(}iSKicu4ZxHj65r75-VG@Qk^(vgJN<&GWD6HCxGJEF zNe5x#{QwAd^I5u>7_^ybFr>A-2mJO5x;ddPADu-P@rjM=b4t2b4}>2YhUi{jQm`73 zk5XxVk6FM1L_EMRufkA;V@|trtT}H%3?zn%glc1?Ft|FtH#!f}EDWBOO%Rx!Rl=D= z6ljASkQ&^#0Ma_$)(ZOU)i8JDQ7O))sER=UV5E3 zBG?Z5xRncxNdTr>M92(EvW+iu4GVF7f9%@AYh^S6Ps~59f7oCgd&X){-R_;YQ`YEZ zf~q%3gWj?u9aVSaxsVDQQZE7ZPMeso^=P&mW5|XuPsMNJ4qaBb61Eq!Kfd)#R*hH1 zkG*Q(V5P#wqbNM1A#LYrZYe11O~t zt2&fKSM_uV;hN^QoZ3Xs4$t2@GCd=l5ZBOiZuNmO{9%?}j0ZayW6 zH&s0C5liRwC~y@}V4+G&8J$(Hr`K7f`a}dKk_Fn`MC{2Vr>b74GE2j($EhQUJ()E$ut1Yh|0TCU!}>v%k;U+k7)q8|(WxQ~egPEq{*mg%6#Av&vZ zW2VXWjxlONvRz_Xe)S!3X=D{w=ZBh*$tslybH+G`ns@AZ>^7QklRMe>9d0FOPqDVc zrvF|(ZZPDSHzpphKc2uz{7{QmUF#fHg3VKzd%E5;R|y;#gV zim+?;*nPpie@W!nR`^YogpxLCSgGsO$D~*UX}^NNovo7ocD~Bfc!nG=ga)SKb6ALW z*VTdUZy#5)oJ)Cj_`)TzG5gBV!OvBFP2!9u*jY&WM9Fu)50C>&@l7*L0=^G*dXCDq zh64a3uyCECW_S)<5;wwTmq%%(&oNKQaKmG5ieOo-Xy?9pjQZV6UlHwPu3U~sh4 zn2M9_RDzzpTQ`JXBrI^zCLOv$+wv>Co#$Ivj~TArV^`mloBU&*v3Q)h5X)Z$1GQC2 zQ1~S;DZ0=dH)mPY-|+6V-y&cDcj}-fr04x;S@s9|?Q~=1hfSH8zAmY$2QQi8zLn`nY-1x109w za_R_<=Q|l(&U5b_RNYktzC~}>Wf3^KQay}~-+-RgPq#!PxtN`%=OaCrS$fsF23tvYVL z`!v73TskNc`waOFz}(=*V@6Re7@YH!Ar~kn+*Q@5d_SSv5&n)fV zEC#iQgC=DFn+ll)pw_X1p)#0L@?+-_z$Dp66li&Qc+=B~;)Kzkkbtkmz z&0qR`yuRTt`b9p-7DH3pYzP#by&+(Aunwj?{gsht{#zk_Vz z&P><#REyHVY@$aVs8EZ$CR(!C2sHby72C2QRAYtCD)2lLT3aV( zLnn+gDodTa+E_g5^+~E3vtUCqRzn9@Gm3C)%7WU;`ukj7W5IM$h=g8pk}34d*g4$G z1K-WIc-D=Ga9Q{j)h(jRY={kf0$78nceb+){|rS(n8@S?Aive$oNe{kWn*)%*iwl> zJpYk`+=?mOS#GzC5U5^UN=;GC%0I=XF>925j zGJi=6{rIThBn11fd)fh_j0M0yV0|FjuQ1yAzYifjxyXpUq!vG14R=;2e>M~mUs7y$ z%gONa8pV7U6uAZLScP2;h!ogU&pjqDckE-695dyDn~N4aDodWXW1DfWim@;sJ@iJ+ zhuHG7;pZ~&CjahXx*_f=9PL78x;>_J8h3w%Ye~t8(w9L(!erpd6>J8*A@=$$px(ns zs#DnE^^Ek>M%6gcvv7+DJv&T3YGL>}XLSec-8(3xDup`yN=Vfs;fNnu4A?%&2`ui7 zj@6-!!O@p>jKjh6_ElZUG}BX#K@2U;-1RPSkZsQ| z-{pDI@5>SrQtg%~hUYV{+=*OS9rbSZ@2~pkAafBM=1LI5$BjWmrFP)y1{Fd^d@K{? z=JqLHHU&<}%~r}w%z4MhhqLcaeeB%YM6G0=D0|9=UdGqq)PMU|*O^y7sdDjKyi0%5 z=blK*zW{e`Uw2mdf>7_XoFi~5aTy!W~=M7S<3c@Bl4CR!$blYrfl1J6n-^{lPQ58sUL z_|t9JQPschQ;tcoW6z#RA%!x0fwtH=dF~b)T304YI5R&qDY7f3szB47)EGw&swYdL zSQ?C|!wQt+Uhp8pK9*kbepJzm8Gyl?BtJqkWu@@OR}T;TwKih>4mVl!ito3|M9qFz z&zM|z5`}Qcd~085R3Y>#*1p_k6~Bf1#PZKXYs}8A-#7g)#4m*kF_N7AE&^TlG-54+ zMc!o19AZ4Q{b6yn5^hN~VfhMxkfQ9Aw&A5YR_l+gr)eY26vv^tU(rEN)>0`#w@+tz zmE%xvrO>9zu^wJU*gJuD;nJc}xT|iX886?j-0hT?bsUrybf3Ypmwi=BK)0P`B>>wF zM!&@DQXOk$yr(*OpGAGTWP5OEjx2hwGb`^o>)P~*y4<0;=r3dwLLb(YI(jizRiF$r z@gl3NpXckXF`@aEUjnRxF;5c;UjVO4NicnU1U?I&N`jv0Tn9!ro+Y-B_QBrGf+K1JwiZ9i6sA_45bZHyf zUYB)E0Ega=QnSc+1m}Yz1owKTld!Z`1?BJlf%F;7m0Mgh+@;$n95#CW`hmcYNPb8-3q16ivA1gp)Oac;>XvAsOHIz(ZFmqJ z&fwgwdYh7Tf!zFcV7}a94vUpZLPV6mx3D_vC$JX!tt7uMXn9+XQtU6V)dQmykSiOm6%VYL2 zMqbD%pJe;;U;Gzao#&7NtZvuf(;6qbS&BKE$h#pS zjle@4)WqHD&Sloi6Ra&lHCf6)Y>t&?>nBKkL{Z#Z(Dy}KK({lK^3sf7RyYRkkgMaE ziO_kP=zJM3?^OzJRV3?#mho>@&Kp@*4Ua47HoyRWAox%N(Y)b2WCBDJV5}00iId5b zlmN(zhL#$6w`CmC*9zW1#(x?|sjk(QN9q`WH8Rv_Rb-IR&P$xlnyl-5NjX5Z5Ew^EyjkY{e4HfVw|Q$FJBJbrL0dpd>Am$bCS0S=1$HJFNbEf#Z`mWFE?Qrqi1WZ zc^0Is#G}fo>KuG}kU`5Wd#I2QYMwR>BVSdb?`4_SuO;#g52a`d)MaGZI|0*SX9eDrji{D3< zf46Ok0jCV10DJO_97jhT)oR%dXt$p9_T=fGp=ZUS$}OKsmLfKNfD}Z_+*rdlcd^SY zU({#F-=Q_rt%-`5_aB_V@@pa^zibF_#bwXNz5ULckvgP`y5U|J3dzQM8k|I3pQ?i!(XM7OY?(+HqRLPl#$}eJY2FoOC2gP-yH% z9smU!@QKgUxm7(&S=x0$%_FU8C9j6EmVl*fJ4n4);i+TGLwZ zv+1V_NwPcoD$sM}1n)eglvN2Oq(EX1j`GD_xS3p2*cD*6O;3%juMTv$n~_ zwz$UiFzfrj2bM3P{Ay|%o<7yFfw570n9WBWkFca^wTC_ zDj8^C!fyk4GN8Ql_R@MP_r!Q}qVpjCYWXvPrBbNZ z+o5FP=bA?fnUBNL=n164L+>k&&s&Y=62CxEFTX<(ah z5npCdM23HCLMrpqdhK~s$k9lcKb=G@K-q1O)j?`RsjO;|f?Xa}gR8dVABq{9@O=NTF!8;`bIq;1(d> z-@ABTE4uYH#-Pl$-%L7CI=jZaIMx%r8G$R8bigwkp8Mk(rw#n1H$M(wG7hl=d7#c_ zDeH&jM~mbAZbl9kTPS7}92k3-G+(uH1|Ct<L8$wOF%((@DR zt@)?BcF74;EkLT$hQEnR(`SMZ z9BAGl*mwPvh!(`D`ikW5+wJV*RhA42^w{b{&y4$Zy|0X__*K*F^H{KTV|k z%^y?Xj00`G+m#OahKnkE|7Qj{7&Tt-9|E2v@}v0l;7w6`YVWl)P!j;gKYj!gd7|vT zFo$8V7VTgBuBeSF(GFKd%-(-0gOoKMd78Lu`(DbOA`;Q@Jn_cyMbcNbYPwtZ5BAR8 zR$8z7G%=t*(a~61CK;M6I*0WCc`^8Gy`U03{>3l$k4X8eVwMIsv!hiN2G9K`{lo4L zJY{dG1=D8;jubF467%H>qkjDs7ChY=QdJ8a+9Ne`km|WNg&T5anpLc9Lc0wn7c7CA zAS#mteJM64!HKt^BbD^7+cKZ4_Lzf1y)nrQbkm6rGM^e|4vWZ}%63kFKI!m&MXN?R$egqLGTT^P#LEWc7 zj_UjQm5U0tkJ-NsuI-50hDX~-At@H5qoRecPCA&3b%)Y$kX~oly<$3A?aFvqAOyd_ zIwUG{wbgQ5Nn~zQBiU`l4%uoKFK{=%+S_y>#V~&E^Z(pvzqbPIFwCbXB`7|AgWV_A+skrqK8o+zK4;*+yPLluR-p*ow{Gd+ALg2e~I*?u0MO z0|3m*Xmb8nf3pJ|LO5DG{IjorAD+Z`pM$tUi7v&a|PJGi)r1Dbx>Vwyc%U7OvTkbqzeUM`bPfAz1W>h(*Fq@})bGIs{9Q9lW@L)u( zvGjFgfDCu59ebrSsDMh!0S098WUEY41v(2*Ee*kEMY4S^N!IV~!v%x~O~yBJ`ir`l z)LnLuP2hR5`d5ohsZInl(65+6mU3jBeW;F|80=y6i~cDf0u#BJ-oM*qG5ed8U(1xW z#~|ag&Gp1D?p|D8BNkbX+y{gd*{F+kPs3|F!qCQ>w*b^+W*H3Sf=FGu;}Froq-!a` zF8O_b?}f?3Cho<38y($k%Js+?={Jy9~W zvjSe&Fv1f50uxsFjz3vU1UoIIl*u6J6@^KUCMxz5&gu6MvEkXh~!LA(8Q>++Ep?b?iskzGVBiO0^xJTJLXgIK1qwWD0fj zz-ZX-d|J5uC#NaJj<3+nhL)5_nc5Cm($^}swM9uon8=0J@iITFNST0cp2)&od~d1Q z1jEi{!FCa90-ze%l3Zx&uNh!z?J1{CiY#k7=Is{J+&=wEq&Yb#c2DG<=rT_Og;G`M zb4`d&1|)CcM8_lrdV7!ak-u#FJo|lhN}d5($PmCS?TTCHY3a3zXPdYifAs}3;SHdB zy^2m?8RzzNFMjyyKu36a-jz=xLs6%;VS+be%qZwT3}Aj-UriL$4I~gK0CLl*u#=CW zCk#nwerOH&=#MpPdgM$S-d5EO^do$c&2+7YcWgO;S~Jo8baPR(bb+2^cy+=ZROl&S zK_|FJMNJX`{)%AN7xcw!<_IZoS2fQ;gYs4$U~hioF@p;jWrd5y{2-sB+I24X9`aec3;)Rd zjfq5A%Qi*~!sVg>FOlB7asQT*{3sWwjo>XT>xqQ+W1vig1u!HO0ZHO6 zP#yeuhi3pXnKhjKNyEw+>?!N0ysGMb#j{dRST{z%R9j{5o<$1}xQRUq_sN7CaAxwk8as;Jv|g>Z?G^Cp1s({2cUB_3zua|C)J^$( z!Xy!KI=Nu(=~?;U#_IgI?h>n2PIJblBqY-2zf9S~Z@W9vlU}9(V~Tr9Mj>DQ*ria;$F5ehbL3*8P_tJ|`cEj%EfTd@{7XDV^~gIJ7QxXe=y(Rj|rdnfh0~ zGWAkSfEjtZ31$J)$O;MZuIOEwTu4-=icCLST`PeO*d&z*(rd%(+ZbI%=io|^}wN2XEZ}~=8u#q&{qDQmEhxSTJLhhJ8cE+ z_g1g3aeZXrQ%dtbW?g4P9o2@0lt2qQSpYE!xq7j8Go!6PbsW?Zw|AkVl%@q!+rc{h zz)@_X%Yx$??a8yF&`9+k%GA37-J!!CSlksOQeo<1?L zflEEeu&Y!a*IF&X_=qTnO)>l+f!1@ETsQjgV*R&UI~0+&JvvY4 zi~#8A`0+kJQuHG1rg2BdWytDs<>e|YOgUDRxB6x+>iPQAJX|`f>07B|r=_iMZO5PA zKD|^%(pCc8b5cVo$myV>Z&CsiR`Zwb57wz#m=1lJvS zd&+8}XSH8auDu2FL2m=ccP<35tIBbi@+j{>E|tBo<+XKFAf$g8zERjVRkb0A`)qmZ zlNiV88y=p!fq?MVeI;q?+Ky_>0Yjp*L8PGOFhW`!d zev4a)NLX3N?q=?;t+QvPPCEE60_wybtd^8juldREe%wDG++!L*Lb%c@M9h`GKNB(% z7jr#q@hcxj3$fTUupILmHGxB4T_A1+uoy?2)zAlYO9>4K1`fLktx5(6>{-5ha4s># z-FYzK~5_5{JS4j+VA5n^XB+30NqW&v+RHwgk6|Cq8 zl^M@(GlvmR)i3-9JeAC;78AAb3$LwdOiH2ZO|0(V%4?|<1XTblyUGvt#vyk zKL~IlvaYm8(PPFxujCqe-cW6P{RG%l^VA)f={LBv|K|V3={?J5qT;pO{Rz{erg)`U5_DvQK6AiCznM_-Il32ZB zEWDY^J$b%{7hlA_cpoWOp%X;F#jE*J5KYL|#<>t-`cY(nLg?SWC&QsR5-N;~n9J0wve-i3F z$plE;g@K<1f+V$ZC6^JAsh=A-Rwoq%5a@=wybq}7S)`*!-ltX9rxgaN!bh*={Q@-U zmXy_12`|!Rg^y5(WO4hf(M!s$(NjfM85VIMw__U(=D)wtU5O z0v609&tNGCekx-nnN!oj=XVf6f9?DfO}>M{LtR%ZEGadBZin^5fEFkO;JG(rN=h;l z<;vTE_RVwjUXGdYuSZk0VZW7WKjegoG_n>1KlhlZ>9F1pDR)ktT<{U`to*`GNl@@Y zv28hmLIC9cIXZT|G$Ml3gU5&U@6^8uIkI`h(sp~&f%rv`SP)lEL(Xg5YfV=6NCmfA z*LHNabO98_LF%4S@T#2lTg>DIBc|1GG2tlzEXB$*`=gS=kAPDB-k%{XouWw z0g9hEs(`IV*}T4)ri2bXEc3QuA1@GSv=9Fjyj3J)B@R1i9*0ESjTkcoB61Epq-H8GL`#*@r~(0J7K`->dsu%h0Q^VUH}YyEet;Z<{sB*$$yFmmD`1W&ej0$z`Xb!`J%2ef0=RF9R4duE`Viqq&A(;i9^E$crbXz@ z`V)-{9`1*+nle%4uAK$6;Y{`s`gFvE|GOPh_TBHHT%O&ww9m?&@R)+xyvYSrbM~XC zt`|K{FMIMdM?z*=cw9D{`5wEmU;h>Z$d+xokLlV8)pPo=d@b+yzwXcxW*R`D z$z-(%!Kzl}xIMS;?5>IV<&tX6fN`fs0biW5HB(yMggi#ar|DVE6nFT~UJTQP>)b$W ztix&whr!KM>VruQEm4|S`}^lA7l`3RTQK`^(&4XZ z{cbNPm}SzT6PWN4!oJG%{VWHy6gCD)44;LD^yUy3+kP6s%9E+oBPQUIROdNJJT0(B z3b(N27$QM)b=>s`_KkJQx8kD^A&;oH9DznOLsF9qwL4#CO-85nmaSe|hqZvFf3UM$ zwAiZI_R!E)3)de^&1zju|KtCacI_>Q@N`bU5sf=kj>|8#DMS_^n4{F^6p+9$ji#cK zC-Z;R=&p>^?y~fM6CXt3$PZ?oD*WMS;V4cM=cyr}I)->bP2eECiD~(xT~TN5aEhYhE9lK#jEs1p8SxGdWA)rcP}FwxAuA3EQpowG(iqh9yMy)tWv?=& z3O$j~9iAEwZu9tOU&&5*HvJ*vxkIK9MlqMm_ChrH0KTr4^c z{ggqrU=5G8k@ZvUAlR9bUj(qKLB2Mb<52%e0fHiGcJNz^TN$?*JZYVOFs+U^^hkf# z7=)&)SNAWHw;zVM_K)gcjnDe&%)V1IgR%zhIG+|}wYp{xP>^yRfZJwAl|VO5lMe?`4(<>Ij}?O!Nh5vHv+RU@hrz? zQ)T~NNkyQ6Z-0|TqXtvqi$Eb3!R7WId=D%&s#`?fx|Oa$AnY|10WCprQQ!|Hqa!M2!~GB9g74Fk{V< z7E2+LHEJYVjL9|(vQ%o6EF)WzrED=F>sXp588c*SkQw{V*au_$AD{2`a^=8j7#Q9cUBn9aN2srG7Hl5^{&h4c2t02*za(`To*I$~j zq4ksrHd{;wA1oAh{LR37>E=H|g1wyqOLcwnaCDOq~ItWF^)yD3ee$!a}PC_E&9aO=2j z{p=RxTx<3*E8V8MyJ71`c6P#`G0u#nnu>;r(hpx64>{Y{`$~Lk=@jG|Hvl>30Y%AF z!6%$VE_J}7k@E@$B2xE9y{$asV_tIWwrl3sl5;|2dw@fW88T6xs*#r_{CJp`8dTr- zyca&BHvU24Cs^ccDyero3?KWnQkdv%ox8p{3oCeR-gf7b#hyT9Bi9EJ;4H)mmz9s+ zD$1un6J-vHpaOn znyA5fn;6f|oMY@|H{cH3nhSNfu3t{in7cuSkh8+9<`2`-8T~zkAoT#bW z(NGJO@9W~-G+QpSM{f8tr${ZL#H_9!@LTT~xC z*|VwWNOjd0-&y<{DwoxGFv3D0@kQGO!Nd~;FTXokjTuV$E=F|Q$t8um#PI;~Y81I9 zu^W2rAcM{RSHP3GT7Iml8pP}yeQj99GRxh_c+m3ptvS@HLf+)wi|WdtlJuwfNz7V> z^ob>Eq;IL#_Q9Qe`ixyr$jb^skaeWO-5S9>zCwFxri^_?W|`<$H;3H(#(szwO$Pqo zFq2E&#OP&iQmYS#X=oFcP8SrU8ycn(Kn6wV3DL)lRMG-I<*AXAP{T)3cZMK@dWHNf z#JP2+K+?sjm+g#c`k$*4hlhhUi0#7YHeI1kU(vOLH_d?3-kKNc2RQ0Cpu30#Iv>diS3QGqY;Qvv2V#fT5S4>Y@03qnCo{7prp zj#rRh2FseK#T+@rhw(gR2ZIhj@iv3;t~!t3JgB`Z)YIYd(YLS->}Dpq8zd&@DM&XA zwzhkd@#xW&l^%)ZmsygrQIBzw22gw8m|K%C8z$QY%`=9{wa!Q$D?qRo3(s)VO*l|Z zj+u)a9{!Y2Btc>_AZBXHEN!Zx8lJs59NYC;OV#!c7wehx*T69&d?xR%NWjDR8ZCnn z@-6P43mhm1MvUsiF&rT|Rmp<_7Pqii2Re#GnqHY2e?fk`0+_SN_u$OjS0e%0^TLH{ zI=bHMtzZz^hSI3%#vIL)Kf5#9Q$i9{3rxDm%$Pl^7D^4--5pU$ivw+2G1e;Q(w*^M zIbO@oLk&*+(Srw=e;vMPOOP%VoC=<8{{gEY#B8*KmmHFm^1}dSZNzLbKO#*L-gDR= zw3|N{jr^(yzw(6yJ8y+T(b$#)`2&IhKk_~dR#3_HB_6+1i+kN4eMQnj2=4EFYH`Pkp~ zZ}-H%>p*ws`a6!9eoj+Myr<)bJg?gcpzY0(eCyQN^{*ET(6cS0?v zzOf~?ck()EJ98PCFJAkuy{QZ3+=Lp2(``Ej-`PNY(IHz=4IasdxY^}(mPNXT;YaI@ z!-KRXL99C28)J`o^q~?HaRje&I|zF*3#gN^{7}k2J+qae#15MDg8Y2eBvOG%D>5S< zo|T+la_@n@{zT@W_j59)#J*Do(`_Jor#RRnLW5DcY3JU?sd|-%b}Q)NR2niqjXqpi z4-`A!U@$5iom3t6c^jw?k1J8Lum6(B>D(A$0r77NjpMoAIwj*WPQuj*>gIhO{H9mM zd*-jLracLL^$-&f@wm_N?a(%)>08VeQC@2G*h>sQl3c`w#evYg9O2b?d|A5M8%wRY zp$^JFv&F^DJTAsf3CXsv#*!38jCR_c!$?@s#ck`S_vd5khp_Ei)I;1ij(x;g6s)#%`W{9C)}_fuRy=JJr564kL%rDz;o9DT=it$Pm%AYSZ|ipKcXx}VIAD|J zmQ!n{Wr%3DQz7DvdDA}~4mLG|HN9FB3`yl32amu&^&|8$ES1(a4Ki_b z?5(K>5=NQrZ3#7?Cb_^(TMp*oyU z0%mV2;vlZ8q-kUGCh6G-*RU?J$vs;Mj=nGK9uIA|NgO=HJRU%zdGtbZ@C9>NO8G1n zgvud9C(01{+o6RB*D`qyY0I~Kq3fr!l*~26R=8(_btQr?&Lki6f5vldlcAoYJ}SZd znv5^72deiaKemX|sqXQRg6mla$T4n@V6xukS8fwc#pbXHX*q5u&%)p?zzG?|N`+ev z-j)LubpE*50vYijbp%Iv!n*|3{y5Wo5miAKX11hNc@Ly0jvTbhQumEEeKnJz^fTj3 z@WG>TP40gnoA1lqWC2O20TO^smaxOWoiaScB%3MJ098AR%9aOo&Xq3O_(X_wx0ZT) zRwk4nHCokTlExh9q}kU9%6F65!$A1dJ9Qfn}JP^MZ(~fZLrD9pz!-Y z3z8#vPL23g{Vo66G^K;={Qh!${By+v$~AJkqO zk`E}`*qP5V!8e4oO`imioS!2}EgrB*S#KDeWh(U!gdg`q za>!dfckgez8*nIgp3ZUBIKH9G*?fw28xfMPgvJpTAsI?=YbAW1koGHCDXSjZ-VYJh zG1%k>0Vy1!7PD0ixDewK*kpJzOm=P4&Ddj-3%zz`W)Al{uHo1-=TwWhSyTI?X3v%{ zc?YhYcmkZtT{(SSyG>^{BJTbl$R7M7hI-_Kn*kcHmD~gMm_?cvB7S!8>el}ie|6ks zT~6?BUo>wmgP-fdIihMhMW@1Wz4-#1%Q>k1E)GsTo8b|UBiyfWqg`96SEt`M_~iDK zi6q5bk`gzsR=q`(Jlljn#!1&o8F&@5yA30dPgad(dVZPdpK&OiH6OI5J8PBg=FCqf zGx`BKBkxWps!Vg8yn3BjZ*`A2gr+$k?1I?qc0u-oQ02}>CKThhMo9h~H%UHsb`Tse$0Db!LWzcw4tRr2z(ezKf^4a{GVKj=On z>>DT14+u|N^-ALP6;Un5#o7Q8{!uRkty18oKGy}A0%mfHSGO`yx`A&lb-<)|R&^%u z6Yl|%7AVi2kXGe*V&un#3?Hv){5CfN!wC@h2&fuRKi|wBKfJEl#bXV2z>ppA}xm*g5*E`xub^Ti2a=z?BF>h=?kFl5J0VZpi3*fsH2AO${aQ} zR2Rpm{v?@d4*jdYQ6R@K9fSpq2%J1V1{mCrE0)(*jv7F}cYShWzqn02Opz~6@1s4# zP)kjSB+EVsO?gs;x$#qStGukeK%V!#n%*~_XT)(e?_GCFi=ge$)|&bJjNPBBE8v#D z8QhM7CkpHKX0S4@*K88;s9lV16~&S?x+B1;cNa&{v}X}J z_mNFzv75ZJ*tXMM5byT^fC&sw@i7-8J5~Z|CGpgb?1L*vOz5L@(&lN#o(5*W9m$sy zNgC{(#-a;mu%o~(F>&51*A1>Pg9WiqN zDIH)Sj&nL+C9=GK1(^Dw@h2iwa@?|PJ_D9(5wRD1GGd9t%6Kw_(Ha4iu{|+ZJg0oO z&xmdqO?7S1(IJD0BVOf~BQ3fhBJzM4%ydEsySo8Wt4Mh%ghuXu*Kv{{=?GIQB97y- zx|(}X-B4w7SX>!@D1DIVOD0oYGnB5dnH`(LXn?shI(O@6F!?)!iP zOO4mr1GeNho&_Z=S1C0LpOo5&xD-PF zliN%4YReLUvfe0&nYGKcro|c%TKMX0DfCoiAp*&){mRYe22d1X_AF3Tk{Ug|%&qiJ zC*;I~0MqRm8z3VoaG%9W`*Wg}(ptaGc{u$HC{`?JK6#SfVr2N2g9cH@Ao#XN2~<~1 zB;^GLs-jPP^`;Zzmb9-Rx>W@Glgvw+hIH|W_j66@-+>r-KR33@nd?r%?XPh)4>Odi z{GU`Ejih{Wga4YtTJXc*-KJ8j*`7wQNiY&N$ytj@lP_`$|G~@P(YG4&^7%;XRZ&_F zPFTuwc((mhx%JR1V@-Q)Ej8Fpc>E#eVj*ucu2A^f%1$P^$`~Kz5H1_NPAO&H) zuoR9cLmod$e%g>fgR=cQ1CiX=7zn^V16&OCU5XuGQcc-Ckc2KIDf|2!mM3-^``QXg zeiMZWwrSYU)j=$kyX>5W7rQ@ODbG`jrYVB-1s7o0;rqk({fgP*9Yd0y0W)?Z9U~9Y zBd;{^o;o4YOz+hMg$9N8U%(NhD@cG|FH6I>_Lchvk;P?9G`(a-ELOKBW&pJvTy#O+ zloRhyq@qfz2(hgvZ7w$uH|~BaAW~f?0EipT?0h%&wGvE?p*ay{1N>F{1)DUI=WUV@ zmW85+zUt{Zw3n0M=ck(o12>T^97^En86bB&t<~ZMa44-Pg&Qt&yOHhFduDC(6>Rd{ z!T(&H@@IBz8<4`O!1=6|*4`C;+q>xhwDgon3gzC10%<@SGL-s`b2o-d;Y>ccr4;o+ zY)xmeeo+{zOo^KSLf|*xcTm@*R-b(-E^`=yKG4$fU*YmGuRlJJk8fw|0Wd$E~e}}NO{0b%Z zXyp3(XR9e$^}?+z+s6%G^um^krhQbkSs5pB1c^+ga3OfS$z7(qAKBTt7_;>;9#~jS zRLo*yUQkWW^^e9x%UyqUHd@6uRym>QWmbZ6&k0)nR!x1STf!KRC$a=buzB6=wamvc zvS~|nu>I^-MV22bz-*Or%wbjL>o6W+3`6u=hu5Vyy0pxgiIa8CR{{+bHqo3KC$#2z zHE8SpB_F38!+q9oRk}QbMqgNXoS~$gVj-X9(Ftcw9-~@zFW?Az0#uI4ciUxdPQGPs z;W;5vs|}feeOMQ{O=bn|!Qks9zRcb=%jw-%xnO&JbBXYxi1ei(`Ah(OZ)p5b6#AIn ze1QDrc6$;0v%)u$?B%G-4LAaPJxPoj`Z3k$9WhtkUg#7Hfh192&P0crAgNWz)Essd z=!2GLNi4?CL-pP^P6n5jXX#$l_HdO{Vx4ZH87=Pb}K#;9{ zD6$$ZDFRGnF0Bg`ru8y1AZHP9)>Wi;KTfsO& z95M{RPw19Iylym7Sq+ZT^`v4TK{%IlFFb(Zn|F@Fk2hCvse`^XCjInAKv*L zjGF8V@v;4tbyS{JrODf}S`7bg*v;|3*RN%tp^^SA8D=9EX7ol)%8tn5 zgU2^Fwd`r6(G+?i?kYCK!!4;u62TlvDxd-9u!hx^2jBMgm_BIGw**EtQ2wkse^@2K zT=GKTEFhqp$yTZlvYjIDZe!KLN#JK);_O}^@1WizYNyh6?wMD=te?%)1E*;?ov>3; z=Q-?sDBLUT;)pxf=eJ_IfgAF;hw;$pv-<+7QJ7dO32xvjn+@dI!%$s67&C1hi5b(D z#5F&uRcP7nFvE15KYfl6^21){de7h)*2%4~kFS$duES*vD}(yhTT%hkhn^fA{}4bw zS=L_Gkb)_P>`1ZRX0X?u5m=nl?)cgnUF79&h*8U--%k>&CKuPX=m9ecoN7Yc{@_-R(rw z&Zb!~GNN7~8b8utliOS;(2@=~&eNy24_^JExR zjenJ9iVaxDRt>uCH|n)4VrXE<)8cLx5j=`9b)oC zcBii>^y*Kj<-SExZ>1F9&APB$uN4i*?Hp6qhwTd0fyZ-1s~qwp%ju0}gY^n-O5x7o z_of@VOHQ6!3H3u#wsyw_rMJv!mdiX>d>5|iD~1LmXZ*j_?}@g{3x3``febv1FX<2;*fS3Q2(1; zEvfZP8eG?^2ZH}@D}|Ve{Oh}=Q#p5KuSzs5^Ap2vBa8rDK$`kysg7{kEsG_;787)) zbB<`aR55M5gK&hBA8H5X$fvov^?189#rU!hAxReCt=*}YEOz^YjI*md%Nik#l)#4V z(huk<;r7NmOvkYdWhCiA;kCTGFBG3WRC!~&IqQcx8;#!Xs{RYFVt!CB24FoihP?n^ zA;iV!d}cJm;s}zYG7g*E$%d{ToV(fA&B@L@!6~3>xnaqCxfM3#{0Wt0>?^RykH48Q z1{%?Ft73jv&j4V*FnE ztbc~MNQ%6)iXheYm$n>goi%LUGWE=aJh3K?YVd}~i$LtP&ktgi4wxGnuVu?J1Ftyh z|Ks!JU*-+8+mMP;8OtcdE7sfQxki1yHsbjT5v#%)R1jx%_7MHI0~>qPaReeTwZN8K z4`}x{yPEqp$7fN@ywxvfJ?Sat@1$3JZE~KQ4^-axVLYQ%|=I=}MEuI$0;v zsH5_76fjrk%{7{AkiI^bxYoxfK?w$p3H*BCQcK-(CCT&GdhAOVwFI0`lgrB)$5K1M zYVZXxEeY68H3ZSaKkjih3-Xw8%tVxNuBL@u_&QA~ENAS*Be0RPSkXppr(0z3+>c+E zt^(l+J)~+cq%TL^d-dUiOh62;d$IqsmubLBm8lymJH@moX^eFWSRq3{hUyx}ptlO?Jv1i)TzpZ_Ud$^=NGMBAnbIR4F>aPdr)WbcV zsQJGEu?AgSJT-H{Pkw$;Z~MLY=rtlSOX*(N4+Z0;tN(j`w3IqpupiD68P1BQ;67Bz zfoEzqTiD`&b%PcPkI;(gb{^eP5w(jW2s|kf1Au6_Kqwm=0J^9N_amEj#Mzb{kB_0PZ1FkD7Z&HLITTGR8Ys%0k?aKb%di5_syop0)-%m zkgbnfw^?fm=uN!t8u)JUdnmI-y2Z=Sh_2ezP93GR#+`gs%ShnvdC@eo#3YVq3g389XU{amDq)V6iUCfUM7c~q7tCJ z8jE0d=JpeK{J}sY*zIQDwXG{4P>Xx%&aAuGc_F`op)Z-17b^_M)Y5R9m(0iudv#M_Ea9i1_(t-4F^?~V;Dxn@)B(yqf2f7{)UoLXz^V;%r_0wG77 zXL&V!)gJ|S{JF5bl`NJgxLLut;gRt8>M*zk?+Bzsq>j&61M?*YfMEiNiGFKDPBl)o zv8!bbl~pCSvzh5pI6iB@g2KXktx6r3|K&YUPmA-DYXJ8kA!8*E2;Mh&Ml#*zT$G8v3uB`W`dMh|}noO~P5rm4AQDz30pX}WFBw&5WrCdmo^ z{`{Y=qU@(LEi^LN!$ZDkVM@vU%hdlmxWi0eZV-$Q_xXI@=;@`V*qzfI%HT)-+E*tu z8d;XnXs`E&8I#|3S$3BzTU*ZgxlgWfG+T^4+98@4L%#h~jRzdfYPceA3sm&$Tw9;N6~jtE}pQ5?5Vkf^^ff;eI|(it?UI=tis0Zgu+ zu^zhRze|TH!8b@_h%OfG;%Yg&s6tC(VXXUeuCkC zCJ6#X>*3xM1Z{sP+az1HpFP9Mrw5nrE1*6T6%lg2wiRNAMiBb=_q5Fab0r{A_WQCM z1b?d;$>EsZNb_0$0k7pUy5Wcbz>b8i*$oMw3%&!zTNk*7xydfTR6I%6XVw?iBoEn! z1Bm|1#$@`$yV{J4_AF!ODNQMW_u*etC8!Cz`Q$ zEYeost;)azGy(TqKE+TBoYrSMX0}e4oC$qSbv*$BL<4mlQPwt5t~H`0n*}5O3J2|z zKN>vZdv0Tk-~P4z*0IL6c8ySoy%#SCB&zE>@e9iBs90u)Li`0NQ}%mxv5rxhiVxgq9IjYNk+Yf6?oKi3 zjxCRT_TPj%e^J-!0IyaxkeRXglW4~}5*QDxBY@|{*+2VyJk4nUira+g;N8d4`@i1j z8c{>l(?^k3bBnKsepf#(c$%if4r9e~gT4b%CSp+AUt#}2kOb!lLQgLf?@>SH2-Nav-Xs@ zqokdOex_ExuCc)D0tpUZ_&*%sa}0?ZKVHkF+WQxP zEcQ&>MD&}qV*ea6K}o= zm>yD`r46KkG7&I-wlI#s!^KVWf<(~ugb^gLgpeo-5K9#LmzQ7j|1nKzIrIOY$`%$k zydWAuPY2ctEQ$NU(g>kWL~tPV3aR#`Z$6vBDe_?WpM$}d%W}oi|8$KtmMp&&xp9 Mjm?cp4P2uBAKbS0L;wH) literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/pic-404.svg b/apps/block_scout_web/assets/static/images/errors-img/pic-404.svg new file mode 100644 index 0000000..0b4e8a5 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/errors-img/pic-404.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/block_scout_web/assets/static/images/errors-img/poa-block-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/poa-block-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..672403c7f73d624137551db7486d05ac57fedd02 GIT binary patch literal 11331 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

    99_J?wYd4IMbI`Afy|5PMFb!M&rscAxNznJn zJ&1Qp?nF860DnYddT;aom6GO8FhT*H&f*XupZ^m6O4PEkY`D#C6(5}ue06b2cE}G# zMMw-3xO+e9csWXA_tN5fJ7aY*cz{3AJDY>n&_#=7Y~0TiDb=@-GrE>S*7N)M_XL@w zI-cwUf&@>HYvesJDnWvhJCEk@-sjm?K4C^OuUYTZ)pX)cggwGlWDp=y2-3Qqc5x${ zsE=G&*Blgiy-yl5Q$)f_mv#u^JjsKB)bjnpu=0OngvN-yBwePgYHFQO-yfgUh$1{i z2JvjcZ}oIW`BS)WMM4`cnZE4yMTleS($HfzM*+9pv&e!x2G64O-RH!!ai9u45+CbrkYm+Oz~n>TK-Zu_irYUF892tH?e% zR5HtoSQlL?HPu$m!50xH?d54-IGY_;{J`MxoeI|6M>Z`A#FZVJY~#75z zB;1_>h61>+Bb99}nOYj*7fYeBM$LnkEI25V>|eI>d(?_D2~vXe#CSK$}wH?);3V;Z-4(9psQZnS7Nz|sPX`~*x~X3*RdBV&lOxCV0#Y}_(|^fB;Bf2J=d)WYdzvW z;IPhJQ@f-8A36y?jXYCYzn5%o0N}H2kGTKC;aZIi2U(wAe45wooZ3O_#MBVV;e$o*3MY-|WHV+7x4s@qazoyqMl zaE~WWH`H1QqZA~EMM`a30z@P7M(-%#d)ZKsp^Qt3u`Q#-E5ZO@rS*lU?ENmJ9wJA) za-5*JAIR`jb1Hs)q!Nysts0{)K6~|QB;0!Nfgz43 z_5S`l_|}Cpd|%vUip?Vg$zfgt(|^6NZ?i&4yVJ3iNWeLgJhz6k7M|Of2%EQ|>xq#N zL3@S&gk4=YYdzuycItXj^ivx3C`Ocwe=jX-XPmhZYVS1Vbb8?9&`3HgLg%WX9GpOp zfw&PHnZBEA#hNNx+PYNh21VY(LO670w+pbtxS+``rw5fBDR}lqmNPD_8e8Z%H3N!S zz6#ZF0OR_K?ky!EWKtF9c&p#jpm8<+*u{{)B3BH3_<)h8U!N7Lr|9J#*Xfu{ODovk?rSPjJSi|I z#;7w1p}vNtkung6=e@;c?pQmM;X4f|qLW(Xv0X9o^fI+NCVl1Kd^@%AP5 zk*}e@H{PXy?meG;?Jh|cFOlrICY};GMYnpgHc0UAOwDBXU6TLA)YuTfuE*R0T`hE^ zp2>s&H{%~s2mjx>nL!vZKt&vt{#@dBkZ~DL-N|T za8nLS1{PlVv+Q#l9J4;h<1~LMmrhk+v|yht&CIgjCS>!Cw>+Q;uJUVsYb1^-> zp%VcAnvPzA0~&Wzs#J#yVvWF=^X#0c@eQ+R*ep$(&?uiQFqGX|4wjH6WG>nWb4#jt z=nwa1$bWGg5F+NXPyJ0ak9Vn_j4A(S#1>4hod&>=2TVzs9=`1y-?)XS#{z?YCWj-V zJ86FdI3n0>Ztz*=51Gy^0Y$Go{#8^zpa^7-DHmu9XHA{4`Jfq8GPny+^cuuYvUEq5g2zlJ%SEa48)ORJ?G(G zgkc@i{NN**KB2`&fAuSMD&ylQRLVq67s;{pESbx%E{}cA;c{4ylhzdAY65WUU^lp@ zo=xndfrlUj-UotQjb;>hcWMF3Q5&`K`lP(~vE@w004r^~NH8ZOU|w)fK%-z? z{^EAR8;OL~G0U$GkvA3wf$7J(U?1fY?5_*KDWgL%ZzA2co%q~B)W0XsaY{B93C?ZQ zDWOjGE$I+If@FB)6Zt85Y>~^x{A4XirMGxA?z}(6j8U!6uY}nGnZ*#Wb&vd^YSc_j z(Vwo3kMN+?^yu4LvZUMf$xd8(G}n+kkr|dc^w_$m^{Cm5#Iwy2%xT&^F0E&W07ltI z>f$JQv@6-sJ#o@T&toL$JJi$Bz7$dJ#2iH95ae|XvMK{2w>>Uz5MH|7dh+gzOsnn{ z5+S!EOl*2xM8~$WtyZ?O0f1QufFUu7xom{YOFY|#6h+AdH--KuAYnbdlspN*--;MZqw)Pw_B${jOcW3$3eBkm4ZN)^d*I(W| zXSP2psx)y^@fPre9uR<%F>`}oEwLYc@N1Y>EFZrXpg5F01DOAAR%lMYxq)E#VEg*n zi<=-xyIw_4s?d%J2>=C$7n8}cLOc$<=u35mf7LgcX&*f=>|eFpXC64b!RT&?As12l zf7CJk#H}Hwm8AWEYwIPI4$1}KlG+Lc?@Su%ltT2@Ur}$etsJ|m*K{T* z0Ne literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/poa-block-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/poa-block-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c5463d000277f28b710bb797a0f0d84cdb656d90 GIT binary patch literal 23998 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

  • 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 Xx)QI9`8@}wGTqQMzE-Mz=i&bVGl)Ta literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/poa-page-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/poa-page-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..3f018b18de69927df4f0f786653310d53d5481b6 GIT binary patch literal 13372 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 AasU7T literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/poa-tx-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/poa-tx-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..c68c2fbca4614849e2f1b465b5fcf54cd133cf04 GIT binary patch literal 10192 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

    lZ96&H`4KlZ*FaJVvq1K7n4G^$hTMb#{2&Q)7 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 H?E3J3NzgpQ literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/ropsten-block-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/ropsten-block-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..232a0e0974c1fd8fba6eb496be91ea5b4c3fdda4 GIT binary patch literal 11245 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 ZbGqA5=E(JHKpQWSu9ksjvBu-5{{x0BrsMzs literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/ropsten-block-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/ropsten-block-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9d7b2aaadd9b5ce1b59ac99ea5abdaa8dd142a0a GIT binary patch literal 23815 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 D*o_dt literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/ropsten-page-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/ropsten-page-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..e7549c3fe4f85b9fe20b496d81e1e82ada181db4 GIT binary patch literal 13184 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 F`akha>7iY&_O;aR(NElpVvS1~r`&l~I?(fws+ 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/ropsten-tx-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/ropsten-tx-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fec3b7cc32e63bbee74e267722569f6ca2e6873e GIT binary patch literal 21564 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/rsk-block-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/rsk-block-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..a7600d6f4a05d16e165c7989b64fd43d2b784a11 GIT binary patch literal 11360 zcma)iXH=8lmoB}xh-d(T08#|$y-N#4ijg8nHv~j_??^A20AfUt_Jg2^QloUF6G9j1 zy*DFWkj{Pm&ssD0&Yd+gU$XLHzx(X{?0wqvoTLXvI`lMbG(<#1^m@8l4~d9~5y1Zq zDhl9NZsw4hi0H0>o|d|45b@S5rJw2ex1BwD{@W9`*G_$2%X{m#-`VcCQ$4I(V|P9i zKF?#s7l1{23T+0|yK1};u6^WHgb^j9IuyN)f`*%dNLcP8rLX;nXqI~WZOkhjnl0t) zG?1H_H-1_eg;swYrts@z=bQHJt6i?k_UFgO^DQGj69F?mOxIN!xbA6v1#ia^;g?Cg zZr-H;g(4+EL&_H9vyk?UC^KQmD6?Dlu4 zo}iNieP|bC!6dBjxohcfL_$Jg{5+4TDeozkFqlU}pqYtA9;}Ttk~rHq1SArSVCH#D z_MAu!FTdB$h@5bAt8mx$Pv}FgaN+OhTcD*mTWa`LjZB}y_9m`?Ta*cAXNbxa6~h+fJ9>F zTLkg9C1{%C;*dT(+y%n5)>p&|fyZjc%r$_I!ZNiyx6Bb*KWZrJhH5G(1C_7bHo#DjZ@D z)Z=TUrBQy8IDuz749I9*KelFh9?9I~e)V|m2JB;mp7xcg@l~PPD10XL=`(B{+?w;L z@-wC+qxPHHTKTSBYmjyqD4L$NZVNjmC12)2p(ioyZo@q6KWC3*UCrQrlDa;~;u!S# z1vagr+(b4IiTa?QsuW6ULN5}*@&u{?O+5B<3N>j z(uiYzsq#>;rpLa-a;p>>nsdq{tH!z=Y4#0V*JDlZvmhm3$$}l_;bnrtlc8bQ%2iY> zBeq-sA`w?m&@0Hi8K)lq-n^jXm+-Gw9J80PFTMY?>P=2K*?zJ|CrP(M-kKklRj@ho zi8Pp{oBv^V#U_a_@oPe70BBVt@R@aUUmk~H0?$Iubk%YE?@px#(`B6uaOCzp|@$=LrW**Gg^ zW-ffBQt&gV{!=}#H&?zCs#c-RZ+m|f>0=6*+GOTRO*JIXC4(7bvPJO6-Ylj%tG_y_ zY+QfoY=nIyE)G~$5b3MyT^H}E21(#WI56Yf9jF`&qYPoZyHJlrnw=Z+JF3gEqu-U& z`=vYj&7B~sbfAR=IwA1cV+}2N46TG}aB9Bq-FTQeub(@%(dk~mAIe*--mZIwAHC{9 z2n5ks3kftcmQ@0e7!BXi2;$mMlm}HXub8u}%Hw+oE%iHIe!L#frI?^u z5uKEB{6@f9Vpwq_p2#y&`zUs1D){;$$E)0Q?oAg?G)kns6%4dM;?pjfMkF?kQuueW zB*=#05TaQuu7h=SP-d0m>7zG{!On$}MRC&5OGY4J0o*-3(#jO~4_`=vL=vQhbxff3 z@xQba37|E_*vapa^6ctI;yoTfln-@c>)8=Jk4Xp+q932iVxkh~M-Cu3O63GZsWaqw zhe`(XD+0|TX3cpe$UGEgK~JeZ^*&vCx5G^s|E!w_yoTe&esH)qHp2A;-JbX-^ZOdF z#xgU4N0Gc*F)$WEWzUjP(KiZxng_n!W&rqvD(drJqv`=he|jxeM^B z_{hALX$HszPJ=6zf7I54PLYINt}Oi9E)lTc+ZPk6j-!RHBa0CC)yrHR(e2_KT#ETi zZs(;P{fBG)ySC8GhZ4A#i$+wD%A!Pptxwq1BPK2t_hNaQPf(k5vB3inI7tkQfwwQ~ zv(SLY+{{*jk^|6Z#Z1e=6*Tqg@k6Aa?dS9g!i)=hnT$%Ek07@vd^lwBRY)I;#b!0) z;Xhqfd??#m>NP%BbdtI)@d-qvCxOKwI_xF$YnJ2w9efCX?oVWqlSyiR6pW#(ZT_hN zp}$PT<$5b$_jSY*6=|)z@^bgVM!( zIw!Ij2>nvKblI7B7(XtEc!oj~f{`YxkzqVT`8Sb5a^SXu$gl|W@UpcdnAYTka+_cR z{OW8uWjy4da{IFTmgm_q|I9$z%kiIaM(6AL=blP?a9sM9cG3;Rlhj9T#HmI?G<5V| z87!~pH`hX>+Ca*rq>m&ediZN)l-hO%lkOWal*@6o^I^-mo647EI7wNdb|z=XZ`G;J zX8;uY9kA@u#}Dz0*B zyQByYH2m-sdIfoQZ^B$uP|FGl;RMU=#3=)@4D%PnGTA}Gyn<~0_E zfURAR z*5vTg+njKre@n$Fs ziXV4Vbmu`u&X05Q^33wPMz5ZIQ@De%PI>?0Xge}M0>|!uVev^2=bBB-`l%p5%V}qm z?(9&woR#L{e6{Uge?lzZrEU3mDIX^mZIQ6opbTADb&Ho#2#Y!nB zj&p84`q7E?EQBR zRgMmNYum}+Q|58H50v4JOcA6Q$ZK`tbZIsus6Of#`JG!Ama8ErN~?eBhr$9r7LXtv z4%cm}GIn5NyI<Ok+&3+oTV4&F zjuNn)wo@{A8XJ-9slb8Y@n1L({O6{lkO{9}P67#aC8B}es`?MhQHOF*BIo6UAz;^> zVzYTM9!TbRd;e(qY|3Fv;rE?7=DDRHbkdl=X+|}4_)EgOe&X*Tz12mX6H#lv%016I z40lS0UiIRu{!rw(2+Y^qljlSf1bSW;oqL=l7iaHjUYe%tZSN5W8#~9Hd$`BnA={h6 zs(?nP{LN-T!n}UzIy2IH2l&d~LguV@aqV=+LsPmcYSCd^z zi`xYJYcCKLH1i1?Vij8=usVE!@+{O(wPQc(&I|eT3EXgP)xtV>8;}#I>y#<0li_*Ql9+YC%*)`0!b!dmxFKQQ^C_VXCoeph!11PiEhU(IO`_-e zA3%}o^nAIPA(3Z^b%pUmEl(__C{A|Deqxe1S8h^R zt+?mT3N@BNC*uyC!jiq0$C!bB8|cstGU=*;rbLezP-inl!^TK3CDxUJn6U7Vyo&>^ z6ADwp&xap=jQ$*mF7=!YxS*cPuW|}2YazaC=J$!}89(V94e${SrlisNpm{~)jmvP| zast0!iuYVcWMlc~hY>sXKD;PBg9SYAt(I-rtB|SJaYv1Pm!6?5inK>pbppB1?p2(- z-XH2vRY^I>4*vCExsQ?HYwF0X*fVBqJ)AeiHaj6}`*dcC_@O3hH<$?+O9e>IdO&Kw zPSe(XEX$5-GyC!MSL05zE5U|F#B^Ng5CW)rJaloqf{6^1?FB}x-nB4m)c%licxuD$ zeelQlvI=9MJ$fP7P(P>qetGOE;}|N-lt|_`Pd2%R1Dg2;rXNa)aT?ULVk6j zLQQpNkysosLFH)VN(qT@dN>12F&Naq`yMBu60zRDW-zsMe6~)|pOV6`MC4Z8Nv) zFgD5l)WixSV@CZp`oTldo(@6b^_|CJJ1)XGNUvCrwQk~_zIXA~EQUb!MW)SbpspL@ zGc*V4;(%qs@&5Mu3T;A`NV%LR>*l*t#6iR~>_RQB9RE_1^Yu>G2?s9zElXxfx2NgW zSLRjX)NV3Df3n-StUF5fiSma%id6cF>#=Qu%osPH8(~FmO3;ndwr!d|PBm%&8t;Y1 zez2X*e*O7wa)Q%yQpqztC73M-EgNZgzrg_M-=C>>((OE;b5i+sSCgMf;0s z_Jbmcv4FdN9b2z1Df|~25%Xn+Z}TQEO0v&J8qT)Dj2!Mo4n+B-UY}u`1qv?1)^!XC zV_b^@?pUzo7Cw24=bD~23-1??T!4>DA>)PY_gMrku3ASQRby^lTmSqItE14*lX+l) zVDm)fMxQxQQ^f(JE6^m`X^9Sm&FrtGQ(;QlfGvX@1F`8U9n$B_W(0bpn-95v^QejG zR3Vzs{piLwezEzhho-JQkT&&?1F-@Q6pq-ou?>D?ZI#QdWloFt=|W(H4=W~2^Q3wq zE%x;I54=BUbdUO_=V)Q3T61Hy-~ADoZ0E@}L6S^^3Yk+dbVd{WL2|Jj;@6r=B+(;h z?k4vDG)$N~SZpJU0t(L)O3y+!V9dskXtAoqM z7U46L)w5#F)mPfUP{Jo-xx2xY+qb$ysuIaaYHn5E9!qZSW<}O6%!2zL+=VMsG{?k2z^i0B$bFpvRkz3Y=QcvnhTgWs!I0VbFT!DK zf;ZkU;=j6?BpUks@$VGEm$yg)D^B)5i7?remOFL>^M&`({k(pkTh^D@Lvi?NDqX~L zy=I!7>>s{UImzBCVIW{8m|U|*H^*~?t@(;J+5Y}y4pcZ8bWVN_S*B*!6zVs|Q)ZpL>Ng&w|GzCfWW`VwPG6M zH_P2jt5>qgdto*l%hbDi{t?)4Qi$|FOJVL`YjTf6ND%o$ebe)BVZ23X^(k7JXuc-I zR~pj?7K>Lh6bL3?5VfG=8MPNVdY|H~0!sm5H?@-Y`4o(^be4EBTQVZwE-FE!X%Dg% zk90j%_Nr5y<8V^E~u0$n<_e~%@gnn`TqmB1<#y6XQ4VR=II!Vl1L0pzV zaN@oO={s!fL4S^6dPs>q5YOM+890-5t|`@GzGba zu0QO6Pubf1L*hD2HbY&6l|f?PfW@NKI!+2j)XqUS`1H^_)ywQ=O5--!Cqcx}fe+Up zJrO}S%HoCHu2FNY#R2R_a$a7eKs010cJ*Il{_9f4r&=pYU2s0GYahGKZJ;RqjBS+_ zALH3F&U5=(_KSn*MLlqrP1t+cGI)JjPGvOA+{!rcUo7kE7TBexbW`@#k_k9psSt+F zS`^rQBbem)1R1>Kb!K?1f@YAUV?6&p=Wl@Sk+3EC(|%pI-=LMW;x;knyy0p1Xa0+1 z<5lG!8CzopH7&C_-&zVW29B9(3G0db zMCYuk#5Wu60SJ6l&t>$Cjw|;pw;UOjb3mdFqYS*i?arP$T@^!SB6kLbYJgIot#3VU zW>(LoN!q`yOqmzQB6`i7iEn9mj|5ch(hBfK)u0PcR;-@n1a)_bxG=rG~ zTTahfa^;XIwU4u>{ipoSa*YyAbh+1bdC$?@xxKCB&_O7h&yJAjiO^+D%2Ocu!kyvD zI7s`nDYBnN|I}%hVxiIzC6VsR@7>=(Q(*+Vd|~BLI?(jf?yN`F1239KcJjj`CG5_i z)Jfl5{h^xJye}wXAi?@JklO~NJMxlYtvo!Xz&-jfE*^IIq%r(v zQN?Uc^GX@L0t;!1()1?9y8KqOx{(jlPaWCIw-p`{gYN{>3mj7yF#W6wJ)Zl_=#v4%47y zP5-p}xU#%LC&2HFSrj)*DS&`y1s7{f3)xwEztsDmLmo{eNs@U^iIi^3jX5$qbQs1XYx(riYOMF?%e(7;Uxu$f z50(79j}nj)UcJR+x1%SWtlUtvoow~lcP?e{+g)5NixxHON}u(PrAX6IL%7N%MyNUf zvF)K#8}Bzq#6uXZ_kN;%vSd+L{?BhpAf77axP`}iLT#?(OTWVX?D|p1SMtK~f4$sX z`B438G|{`J+}S$d_pzBHZ~EJLKC3YJDYEv^P(Ovu`(?D%y6Ey*NdEA);N{Mv44jdGg=Y3^2rzR*XG zhd^V2k~}77CE#xIw6WSG?sdn|<-jG7ShjrE*Q+w5*ao!)e4^D$PmQ=?l%Oza1;vdH zNY73cDX+&)s83+Or!D>?XPD}@$BHh((LZl@oQMNxQbRW`naeNK-2Q?ITEYHo3gm8N z^yA2;0?s?Gw+c!eSN-j01Lf;Ma#ieu7lOiv6+adQo2NhN9GTl3YB=>r-DLU>zNec7 zC-E)|jxhe_J%`(r1&VD=zkl?#ewb<63bd@6dK699G*qi?Icf48b!GQ*C`J_CQ961H z?p{#h7Qf1%eUc;K>*=sbF6KDuF_&SkLMmxqjgag5LAP|ie|r9G+4^P8V_+ro5y89X z=rn&17k@x6`2$kp*P+&m@->HJ18+-KywD=aZoVgMG%L6^ydOCWDqnb^M zawR+@G={5Z65tv>|KzF+YEGiaC#8ExgI0 zJhh#K3D3sL*OxL5v&k*H?mPX(X5}?cg{Mv;*N%d;0*87kXhCAror6Imww;_+jk8El zs^^dug=&oxCr33jZOdehHop~}>2dZ5P3rAyoX^swP4z%gQnwGn! zJ~<(I3t6dvMj3%>(f_hz*7;iU8l+QN21N8 zkYz(Pr~=)g9Ein<_eVywxA4H53dz1ST=drFoQ#Dhr$MA3V;h?V5zmc8qZ`Y?USjy5 z){X4FuWonn{wSmm_`|ZMfN3_W`Au!zTqEloyOSP#bIc|;0W|DFk5Q&iX?e(0spW@- zsJT7Q32J*af1WWQ-lpuFDiPHxm;hSqH)C;@^V~jaGH;zO;mSOtvjj0*OII*?QG z)L{N+v8f+hDB&3zGAwl0#jg&$Dr!M~aIb?FRtuS5>To2FI8egoCKV|z7f)Vyd}@zw zuh^Gk8xHy6dY?_(OE2SatrMKzz#PSq0!Irh7y(zgxkM-0eR6T)AH*LH4W9emXPb?H zH&oF`>MXT=340}sTl2H<%#Cth=o2UO_eyBg@G-#VNP5+4{e=zo?qk1Vj}S8t203-r}3$kDHRm zgXkQ=;f7nwb`%oqVL3OS=)pzCvJ!43{!3toB>FM2t^83?d+Fw{BEq}yE}ZLMjzOmKeaL)7<4OWW0XbvIW*; zZzCZ;UeWCPWDv934Ga#3Sk0#@gofW?Mi{@e3pgr{{7L#2AF{WyowFtL)|B&A!$CvL zedA39_u~lvZtPF>gTY=>ht@ri;?A?XF24srtF{*O2kK0loU6?-eo@N@Uxa?wp4yel z2_VpfO4Fe^j_;zlAWRw8eU{E3loUkMY$O@~OC`slIbu~tGheNlEB1-VfJhPiTQEoT zEvh6RqOsO%0N7H|=T2avP0{srXQfbq|D8BR0!qrs?B|-re&OcG8{UO6X(e4>Esq|d z&h_KS$#XyCcoRxf&#c;mD#b|@ZF?SzyAs}gY#|Vc6|ye)#xCs*23;60YB7++hbYjh zKjd)v{M=1*II*vXpj#^lS0Dm0WQ;A$s@YTVdX$2jKj#ptZoaX~g)6nN(g{JMRnZuxocPcJ-PKVsn&Vm1%$0-Imrj%aPC3 zOiu0$-_HfUGJ2l3AlF1`a=S%6pt%`kdTD@ zKDZ~$q*V-FrA}r0Qh`uf`p?=^AWj$jUu#cfWUx;l?}^T$i*)z9j0@c!RK@{YW!jd& zvyMZ`8<(^B_bK^9D?O}T4#)Fz(}sP2g^T>D(J|%6Ef% ze^s@M8mJCG@Aw#d*oU03FnxY#F!xA|f!|_{o3U0^fQ?yT#}BHUPLP~U4fL&|w89f% zd>kS{L#>)_EefJdrCg z-8BCdz1qLo>R84FQ=fz4@9`5oDpP*834R@XVv~;fux4FgKIzZhik_SHPM@8~~C0#Q~&;jEp^FTuiyQB4PU z_M7$gRMwMx8wuL z!+EzQT)O+WfMo|)zmzGq(tMZNn?}UyTCcPxZ>@{*@4J0D5GboNc1L-*v5P53xF(D| z;0@y>0|5`-Sk}lZaLO88BGYB=DAB))gfvZad>kYZKPi~C+<2WczIesXGxvwL>sLww z&X6tMA{aD4qOMhm!YT~h)ZoTvicXwCVQCI{YB|nluv;5rPk~oMOuF>_EyKG;Oj?CH=zecoiY$AI+bL8i182{F z3<1r_l+T(bI`KCW7%4AC-(xZ$$o17a52T`WMFbe(}^2LjSXdYE3|c0jIb$ z5<8OK1%ROjBZfiwgf#EifuzA<4>y#DDIlQ~Q>ju!Ctg$PS(DEr-Gtn!m8l0*Ay?_- zJ@sSGl!@E=O+e0JEYdLs+A3EKGW8bAr9&oOYIgxB^*9j^{*YbLK-KG$tuSwt<$)O9 zYoOcbLnKKhEdkt_p)*r}4!~HVKl`4B2MPdxKW%S}*$t%{52|56!EZ&L*-KFIJVE!@ z#*_)MhP%f0IZgoS@TNF24> z*@ON%;zb>-KFA}&ct{0F8IMCeV;G|$z`#lWYd)R{g!v`yPw1w|peUwwqu6^FV*yj* z`SQoF$Hubsi%>c13k0%jA+RwyDG`?ZDErZd4}p51Y&R2QVX6ul#u{?VcF&Hpe1>8*H!%b-W}b@C<;ulVlV>WtI^S4Nw|h79=3988kO+=X)|p38y!N!ZN2^3 zMitX4ES2WpWNz_se*;1g2Qvs`@TKd-y@G+nS1b5d;X`wk>#{h@VAbp!a<@eB3SJC< zJ7;sHCbuvy$wv-_SfRc4afTK)?I#l|FVSE wSO+&D?Yk|E!9Z-Cay|byZEDU0x_G`*payf2`1lfdIZULdZKPGEVITc}04?mpx&QzG literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/rsk-block-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/rsk-block-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..eac366b854c17b43a9f8cfb6e72edadaeb27c234 GIT binary patch literal 24074 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

    g`vUZB0)|StA;vA|e&?F1 zw{Gtp)!7mr?Ai`P4kZe1ZqXOFC2Edb~)HQHR3|%Urx-dFxqzcQ-=mV4sQz#GM zB44vS&-;MA{?A+AWI!?ZC|q`4vKB4O(oSZFlb!jm!3kU@GOR!d?as5>#cgUq4@;zT z13VlqqYT&53!>=7kJ;EWbA`U#QC{8tZQ%}UPFi%;tOrcyvu1XR{Wt0l~x5Pk0mdnr@rfg!*i)j^~c=qSXm#{>m}oo7enpkV1zB3 zLJ_hz>G@wUx3G>xu89+eCq2l|(ZHc8^x#KBZ77{u2&zsLDQcVBHC6OB| zWiwZJp|2Fw>^_qPb3g{N;OtYExPnnIkUBZhICEZtH7}`U6|gqGz)bLqI3CG2)LTVVa45D+c7c_3~g{p z445M{xr&P8Ccy4Oqs=qw!M{yw&Cw0dx- zlc)XOsF?t<0i8T75bE#e=UU8ygYVmLMl_jRyaKfU-dcxe_wtzR3beLNC>>u-%BwIp wvN?WMU1dNqrHT?td8u}LP3)2c_~#MJ`_#pPw?ZjEdK#LWdd9jX+76-r2RaeiO#lD@ literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/rsk-page-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/rsk-page-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..3bb3854abaed62abefb43fe4fff0dfe62b3ca0e4 GIT binary patch literal 13520 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 Rz<(;Cs3>U4*T`Ch{vV4NM)Lpw literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/rsk-page-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/rsk-page-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..46534388b74c6e297cd68764387a145638aa854f GIT binary patch literal 29527 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

    TRQ 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%0DhUq@^H`Q%b^1qEwTXoa zlegt1<(>y{`|5BDIFHsfW4Fac5ucbEdnR*g*Rh{`GA(uI1Av2tPI#tCGT|&~xx-3L zy~=cQO=&B6dMmdH1=FNnxKQTvXL-ZZG~E)k4za*)W~|u8T=fC@r?EzzTP*hhkX(Cj zUT*(uzrJ%xd)VHZ2RSn#e=URfK*hhLy!fo8(oYj=Ev(YXlG$&w0wUKblksG>HgpF| zmg4lNAy`bpN_+&c$hA59CVfZIcq_SI4$D?2&f1dMvQ2io>Wgt=$-b9t6)DY-ix~Sp zo2C+i^r)l_{!hfcl0xFdo!r20q&{h~&Q`|ziUk)Soky5Nh%*Fn`PDu@h% z>Do6uX!9(m37wWt^0{-2FcK^PKb%SLj9B=Ulg&K`A&91i`X;$sL0WQIkNdwO<4lhTF2+6 ztczN3ad>V8?kUfK4~-w|+(LeIw6`+#5U8()Zr__;?BXM5x=}IE=hWifPvrL#G@8-|R;jLQ68DqA6VP!{Kp2XaYf+XV`?Y(serjY}{$t8mO`rMYMq27`Ga2%zPxTg+Vr?vs z-LqAC4urw-3LwzW*PFbGN$E_1K)k#UjuU+IMjc4}*}m5^Z%6#J4Rb#l_V@+2ccw6X zT?Y*3GB>O-UBIo4FmP+L)6(e+vUh(70vZFM#1*T|`$!ZN-#R360sUfgz_?N2i^1=D{OcvamlWRv}uX=z9 zB9hI-=H2O|eR&JKo<$HsfCKb0d4#iRKU@ku?5%Y}6&~VX9Bn)3DC7h^cQ>4rO^BWt-T#Zv%Oo72z3K^9E={N!hl`!q{>>he`-0IlD#GQg zNZ2y`vr}04AaF1A$N8mJxEfBMZw7%n&qIJrarR7Vguq4{%=c)k-C)8YZ;+2Y_Zj~( zJ!N=tuN}njurM_Bp+IbhbPmZ~C09=+r~YM@+V+!4Z@XeX5Tdaaw_S(41ZXlbaGXI( z?f-+k?5lgZxpHoYv03frSAF-x>Jboge|3>}D_v9TF?g$kiMf6LD=O$WO4v8LduQ)O z2!9+L5-A_U~G-y#78R;~G# z4U9JU$%sB#yR5I~FYQs52xAUeA!j8)1Rc5jTHy)MEt(S|c=k=LN>$AoP&R#aM-nq0 zGIg9gKTUAu?A%9Lar}KyRbI5=fv)JtmY=2F* zdHNZLln(+kM^c%LvXwVpM{{XC^Plv&UNZ$xO!nwdL`tAvdotg>X#uK>Pd_k}^8oYe z{8up>tp!EGThBM28t4U3zB6^*Lv+UI6wSZTQHw)_5>JL3#^y7 zuS@{x-1pyD7NEvOBj1Sh&M3`J`g^;l=ige37^uKO9DL515ZsRfR2`m4 zo3zF9mfWV1$Q=N=05AS+z9< zFX4k}Atf2#kCj7LKA|9fp9XrSaXHYdqkA5|3_cmk(%T-r^!u?f+QE6l7o!ZrnMRZj z;OUgB--UrWz&4kE3kNXWS-5h>6n&gD4i-uw)n4!U+@#Bzn0=r9NX-=e!7@#RvpbDW z5f^-d6*efy$8h>Y+Tevm!e5*D_H%TL(2>7OTcTnA2;x!>t+vFr!0a*^2%Z93txbcV z_dJg%#@bUigGu74~C_1 zm}s9NhZiiNgvlDiq_!Ax)O83#}dxH_`Y! z2im^`k#Cvb#izFftC2-7@L^YcG2hmBreCME0Pp1h_qqM83AUX(#i?J_o?mRH0Ohg83in9%R8M4LRxBze)ufKE2I((Jl{9(-!~BLPYu)J~tJW1_@^}D1;5nWc zKrHkHhbU|<%hth#GG^NtvO^6-mlE>%Gx-3POkC355LznPq`%lu{6|^#;;L`u`LjeMXaQ^*J0&hY6|9<|@hW~Ge0yXk0`v2uK_y1b(KQ{}V?r{i| z?d-0ePa)pTLi``E;lTYJsUo7HNMgR4gFZtydDJ)w{UPsg`wZfIL<2BTv>!~&& zvJn7i9})4Gnu<>((+@$Q-z7_Wk9^;c%I(Fb*Hd5+BQDx0c9zY`1Vh5U%3pdEsnQ{> znNOEx&*R>wjr4;2*Dg)Alyhfin!8Dhw{f*Fvpo_-zW)(?;YLryi0_~*GfW|2(1gPY zv*g#RS<`(8ME;uy)fd-z<8mKVvTAjjnkVaTx#|Trv5*kgkY2$vIAwN3!<5zV&oCa_ zVc?;h7>NCInp$^wiClC$(c#ihS8SPi<6di3vw$4bmK1yQ?`Raz%nOapD{soVcvfmM7c z4l>=^$O+>&YvA8|f}ll=MvslD)57RiMc`nX1pl^=7clbz72^dkkYg8>oaN_SZ$5{r z3{g<>zdF4;+JEr(;sXr^RRVdk--TW$!4d;)Uh-#4zVzLgo4RDHxNG$FsxJCKFAw1S z#ZG)a{DKu2%0lDu_%>G}={5xf8g0uC&K9v8DMLMYaFTlHc^4X>vVulGTS=1$QE z!?kpMin06RlzknM6!%`!C@;ysrn+fQ$|QcuC!OcJiZ0n6msA6kx=kSO-gk)$bq@NX zFHp2b{y=zyruI=Pi9lkB*RZ2|wN8U}-_8RN;$5>_&zM2U%AE*3(r7)>iqOgWTu=HM zDq*ybk#=*gXzQ3D>d5T zvS)*)FWb2#T}7dSFoEq zn;akqQ4%7G`-mYqIU(NkZw|gp?Vb}-*0D~DKeHO&-tg}U#?~W&9#KOcNL;XRUB9n9rwCVt;KsBMfmb@V4pbu9*sTRmz(mu%Ct`iIE zwAlW>Nf&>a%+!^Z*7NT=H%kr1^=#LXopk`)yA$p?%KolD&=?s180p!KDf{|;JUijo z99Y8&gez|H7aAppF&{$4bMw$RyZQF6qV)%m@B0M|t4oMne**c8gWgTkC`roh&$VNz_pL>=SEj%ZlM^D7MI&lJy{>?I;4Vsx z>*D*gHXC?piQUdAyVd&v71x1L+1H=`1?2E0DcF^c)-7FmVpyJZzj}Y@Vr-O5vfi5A z^sfTp1cv1DB)&8Ii+s@KLniW$!%rvIwXvpOlRUX!|mnlBEDMnJJr zwJ*COkRTA`5Wj^9a%O|2bl*{E{;A$y4BvQXj}D{o;{fU)(4Qf#1V?u7t%CP-+otJ0Dt(@85l!V8 zI7)NS{1334p}a&J|134%YCpW+>0P_YB5(e&_6i}?q{8cb_#cwzO{$ap3(rTJM07{o zG(0~hDvB^Ryxr`eDyRGiGD|#yqbQrvk z9Ifi+j9!Xh8myG0CaK@ivK{XQ=-p~d`UeO~C_jU20XXa|Y{>FiW5dqX$`t0p2ILIw zKNt+_*;=;|rvMC1)3p8!NxfTHRyOT*LZ~b9zb?BzKRVc{6SvubXtAW2>0a$=ymYKF z0^bq;tC#D#q~O&{?#MA>Jo2_=#RS`6sf#paZyy<`M2{jl-oMK{AxUw$1y3abPFM>X z`&(?K_ud1Q5U)b{U-`m4Ij~v@hIjwqrP~^JHSSyhnVF#Zz6#2bz=rj&>w8q|%O#uqRKa5j^}jd#(G{sH{;1nb9&GXa!;>h>FEEFjEdY2{>- zsITXG=I-kyVfJv1}Z0|Ul-RK*p?wJTPD75Hjxhl;Pi&%$1N)@0f)pQ zANN3@wka_1{xIwM@{jLtaI3nXa$M6Q>>~DOH~B-t`PI|4b(7u!hB>EG0K>owh3tm> zDg%-m*9F2MtK4Z_0AS!9YXER$QgD7Z=FV%vvSk(EIN-NVfYt3zp){WF<6p)52})i> z-R1HE9SwM6PCo$7S^=eo42N&)M}Neup3I}0RhY&tR_L1>XTW=1MDhvDroV^a4?9hr zoJBQ}ReqB+Y>it;m}5dDm1|~(;9XG|%V3dNR=MY+|4a@WI>W-&_OihXU|Ttb>NjXy zEEau!6~E)VR4`~D6sCU-Mkuz(6=QzTNu4MX@Z+6#j9b~=t~V;S9Epezg*`sGs2yZU z7DNm<0OMy6ner3?K6q_m>YHPX$F}L8UT}3Uq;d?TY{(b>M*7_UM+5wk-4!7rRC(r) zzgL(%Rca%nP#>43Cg#)3oE`OO>~}dP{Q3Md>#n9=$pxSs;Zua74I?m~Mc8P&O(ubd zrFA=$HOfy9i(@Bvd@KscQMHMGskb0{EP`%{3*B-#d#Bbsm9xm*eTp8EgUjE>SKNYL zD!)e`zjmi&2k%?o2K~F(%?Fqbu6@^4u%alipN{{44|MWfISkzelA|&MfR{aM_(T7$ zwKH@Jn+i(f{2nhXWS|Is*Re1=Hdd@L)R_y|WMgU46|I%1G5HTLQ;kSImbp=M4%TV3 zHp;LbpvvAvZ=$a>-0@HD zV48&~=;WY0bmUbAxBjOa&!njt?WA7}&y!9jn3TWlB<>vhdCOLA|~PlHgsR#ITe-S&n^y0C>??MgD&v6%j6Bk%^Nx=jLu!7PeUz37=q;Yx|rf{7)c>{r@+S(9M!s&r*L&W}P;FI(+ zp_di!VI|*g&FW&)_&bM)XLz3X;g1Rla?NunclVh({SQFKUXeBOccwy*grrXT6fGSO2Ax_RB3jmUNWcXXa6l z;%)iu;1h(dLe}SD?};e!48o=;MP2QKDh0wak~mEqn6Z$s7sKi;N%iKR5CINZ z_kz8C`$eXFa5tq&H(R>&*{r$`WZ=Xz6Yen}(}4^i5qn7iE|Z>F4_hY*I%8EnR}Hfc zW6Q&$SjB@Hsj+0}n-2F`!dpCFh<>=zn7Niz+q%NSd8Q_db9a%fb62@Od^a^r4{vFr z8}5s9D4S!M>{W57Nu!bK)2*nqdH^zAq61A)zTT#S=S9RYa@g`lZffbqN#i$il_<2R zf$u~;&X!2GF7xcKZ!Z4cffl<)TVF*uTSZyZC8Ha8V9%P*U$GeD7o_novM2P2Je9dO&Bs71yADv(R zY`wj@-)Dcyqdot2A;Ng-HZDj#EiN0qD(GRJ`v(+gY~ZJNU25kbh1Cbz{#^Fpnm~HQ z_7~;sDOjCH(td#*a!mBo;&H*m{awI0PN@F=wU+u~LM3i@sjuUb@5|@Reup#+*qm79>sN)OeMwQn zhXUkX@qEaFKXGwJ6!CG?TzZJ=aEFav=24SjY>sx<(4}f;*s`s6pM0WO?3Nbsgodz3 z7%xr#zF0rNx@5Lcl9TR1k7zrp!~JOJ`su|BV(5#}^b$K#n@(K9D068krpw=#HE&WC z6D)^e%-b$Bi!JVjWE|Yu)I@wXmu6CuLK`J!y>}J;KPd*_!Sr zb&rQv(U`lDkF{t^B2NVsUStgER+!U>#&FI?CG$WDYYh^Asjgaq)lj^6z)B=P2=$iS z=KOHK?iU7!n1RE4qxjIGmy8A^a3n&$Kh+}HnH;s!($X`KQXtzh1}hXXxbQ&GkV<&I zD^oo;z!&MJ=0g)~$Hl=g!6D-FSbA|y^2Xo+6gz&t(W)sf``TOHuQEGr$M$7zLjgJ( z`_sFi@E<5m2RMlJ(|Cz144zS+e!s{gPDoxd2I~pkX?>LB#}r4KHqH6KvU13u(Vjbn zr+fD5m88b2+W>!iw1sXbtDjlm(yvo(ipb9dwb4zoMAmBAbABVg>zS>4jIye5fJ}lg zejzB|E=d@bYxXm%WMtCjO8)wHpJK-3kCACbT>>kvT+(shT*9beyJt1N1Wd19LS7q_ zq($c+2#+rDj_1m5c&!?PqnX|UzH(j5O++)mq=;L3l=<{BZ}5SDhvNHmFr2G-20A_Z z<^Ma}a)X};>Pr94i3ft~>74f)L?9;6(%ri9{2F6H>>02=_QsArh#F^CvDwPgz9BBV zi9$huuxhB=K^Ckp^ICmZy3=kh^k%94Uc_1}%tC>oQz7hmPl+wb&`tC;90Mz}wTZ2G zbH8ROz3WuwBhk7iJa%E~$>a)BX9o)G2X>V6abQcPY-KtIEKRJE0Wa_ZI~vQL#pPZY zIwAUcbAcn$lGrzC?tBJcfWUkh)&3~y7T>yHNSP}k-C;Ht>9vd@j}Xz-lCEdy!|$kO zPO4wyvOSNhV)6Hl!ANA;SzY=vcuTT#;a*E!bO6V(!gjnOQsf^VB`9jF>~&CEa}NbM zsQl zVMuwR34cg0dC^|4r5%r&`MdRfcV-k__(FsCnTt%chMe4{dO}A>n#Qw0MGm`6VH}q?^@Q9>|QZoUPak4@+UDt?VQ0O=XAhYSdMA@+9yk|8Xq%gp$7u=$#s{G5v5- zpH!xtA-5Y=?8FH<4!QQ!m`(=%mx>7D- zu;q`(P3m+&)MuAj-@U427?)!zoDlTLCtcKDwgTItcSFULIl|LIII3d1=5!R+U_AORw%sCBOc52O-l5PzP* zhtgQXEi!#S#)BIoIH>c%xNDn)`T0`~b!F=$KaU^6O=i@@(69EM6nS6g-D8z_nJ-8B z6uk`xtpJhcwk~|-RjJ7C`ocvl7;Kjr?2w=PWV@uHW*ibX!-03Um1sJmm>^w4Yt#Oz zN}@pI#1#4h&_q%JHOt7xUWS7Q*lZ-eu*9}@jdee!gBK#Wm6mp-$?<^KNcIyTc+QyH zFs1_*V(c17N)7Ame6ZAv1yJq7^n+hZ6*jNlWq3FWL9Wiy(ky<~);;tIoYVuV(VDtv z*i$epXLRBXfKbyU8>n&=W*m6affA+{I>XKN6i@+rUPYB9Wl}T+V7>MfhJrrEC2j6d z(nT_me)8*_z`l~!wiklw9^1EM6<68Cjt)q=&oX}6AhIEs?KWtegGl7qQszc$)z zm&HipP%}R{-~j$;~E3cg zM9n^?%Vyy1G;JqX$0zL|(GC(Jz;Bn1%HCilocY0U9~Vvt+gduj@pX~an;Unn1)PQK zfvb;b$;2U!wp;vBC;$a>urzI(Hc)1q^2I0CQy&w243m27)-1C3u}b?ntbl>&G>(}) zh4kh=F8==Cf;n8gpG86HdvfSam84vCHqxkz5}^3F>H+99HC;og8i zXP5M1gTBia@RJ<1i+qVr<(}Akn=#G*rSW_+;_<1oTLO!D!H)o4-JU6IE3UJG3MhNJ zhodOOXge(p`m@!Lsvjwu;c8it8tdMs$85p%4L&i46my zz73YkA;MNKxW%nQs*;vr=q|U<`75KqvZ1-W4SXO~SnTDT(a~XqeDwK#NumAJ`x-z{ z`td(KCkm;DxLd)Ffwvo*>Afuydv2`I=YO~}dz9aNNd9OC1qWuWsp`Hn!=`NY5zS-o}W<#2C}r+{ouA`OO-+#`X>zpDB;ST(>3?-sy9uKmM%!s6OMp)ImwbAoSxr z%66|BknwoTUY{!bUi$XuRwQgre!CVCJ7erjlI>q!M{8k@KWrDUluLJLW~6(}wH-^yS zaIU9VobWc06~BlPsBd_L$?9%SKB-eGM(PzWGCKY=2H?hY+_TxwJkyFqc&3N4F!gg?CRtncCWVo>tPg#j?Xh zGlgAPav5>uD|wVjDJ3;D>+pss16J^rbq6%+>52vjetxGQVfW?%Y@jiz&Hh*~B<+21JDne}hIRU9vE_EJCiO117@oF z!PA9Tbr34aP^`W;y8c!_{ zvB6CtGwTa+S2S(;nd;&20`w)Y#76p7z3g^ZuzQicD&|@Wtu}Yg0`)6x<@Ehdf4FSi7&ZG8MP zievesMI&}3w5cy^XSiDHci&@eFE+;}9YW4$;>oOQnFpp?AoM|(<`)~aV{ii5vo#dD zOd)4!Z)X?r9f`*I*y*mX5eoct{uQD|xWMMQ8dGe6Bp^P?We0Rrpoh7m7SQzTH?Gzz z^tc4kUSBRphblmY+U7FM%VkDWXwWNX2e&_KesYz)lQI2%&mhUs2kT8B)FzjF>MC`R+`wM%ubFHep2(FyG{tf%DL**Ut6`;A6zt}$3=*o>X6Y58Z*<`)u+ zvxEBNPJ7!kKZn+hZ_V|rAbsf;#iRD6<;MC+o05@`rIF0G-pKK+V9h^kc9NCv%8qcv zlikp4ZwFHIK)yKD?y`BNPoF6?L|c0hMt;oIwh1vI#!ei4>;_lLn&kK%=Wm)UI%zc) z9`Op0+Gr{SGEovJU!1fLt;4 za;^6#1)S`7oxzF=X`M{<1NlCPn&qnt7J>lUg>5r^nYQcV^lA`Eo;%I>F@w?nd4J`N zc;z$k>4m0n-ZE?uJ5KM+mn5c3kKWIy(A&AXxUEuZbye`h@dox9yKFkCZozbDmX%RU zd#c<6&}mFCIpl^Yvx@AC0NF^~lDKY9mON(a$es=zxV3=SXG2a3M7hFKQ+V4Jlc69g zIl9wcTBT`SE)!k-Zg9s*@6$K=Qku{HL%Ip>%6<-2>m5x*xS$Zeo&s4RjRQ&R?|v1c z1aC&?^NSJ|4;clx>)oQABYsZw8E9AHiSrX5`7b~ogI*L*@9a_acUH9q&($u|mOaqa zay|B!#Fk`sb^u#;zW@ijF0`j^JO#b-taY}przsEEMw#w?Wa9H?Rf8dsBv14a?MFv( z9R%>^Q{hdk!;03%i~h1Kityqd9I z29B2bIGc+|&JKJ5rj^n3U@RYsdgg~9vl;=;=3jTt5m`Ywj)_VY5LRhH3B3-2v;>;< zjuQ`n2^0u-5e@`JbogMsHq+Y#3$#>+5lSDn4g@5ztSsQx^4y|?rBb%}LRgWWp0IRC z*VF@9+|4fkdnSGKBT%m~lb1#h>e%FgJ8*+SJQ$UuTJ-)BScY`fDeymQqpm!#fYC)x@_;1c(;P8;`!|tZVTFW>nD9lngA7AR!z9 z;Uin#LoMo>INRRe#i0oIo}#5}p+cL}6^`kcK)%X-HksaJ)H~dU$r!wp6uO#UwAn8Z z1~mp<9>Ep{2;5guL0KQ2jgA=9R^Copi8b2OQ(s6po1sYJkH-57P3Ozomysq-+6z;rpS0@#ne7d~CSF^8biSN1p2!#`&%+lewr>W+FWRmKnV%5z>L6? zr9=3(-=cfhT#=Da?(F~>6(ugvzNwk-gv2k*jxc{3>?UX8U)Bmm)9b-BoyS|a~szS&CDi3=vfy--|q!O*VK z11uo5G@5<&bn3Bv``U!~l}WE^%xUcH%>Q~4_04^O$9LcW)agy#RElG=Hp{=h+dC#G zG=dBCmu|R=Ae}%C-YHZkYd3{LZh}4%115F_yr9h_ld072l-ubtt>FISZr*X$^f+wh}3K`c$@c^lrZ!mnUmNQZ=onWpEra%eVIfS?fD5%n7}_3r4~H zkEa1iAACtoMGgQA>sc!muDP+$fAYt#hrXLm=xf^5fQc$IdwXqzc*yjHc1`fbRl*aH zFfh^b1&{1`bt|_@2bN6kXkxeF-%d70jaj|9q5UVynT1BJjr5}w3b3}PRlF&rI?OLJ zg}5v|kBWrdU0U%6qcf?Fv#v(xso6UKnat??H!p>x(2U|)XX(v}>tn~`FJEQWQ6Vm+ zaf*Asig&(us@Iw-7#>F+(-Wt1h&(l~9YSy zRLP$v@SYM;JF4(i&0ArL;s__Nj046s#nVuV5_%+iPB-6Th)oC_t?wTBzIsbkzlw$a zl^*#?@z9Q!hweo&s3k1&z8RCM*hg**Y8TmtHp%7j2FC7%@tf&7*1xs_U@K2*^?=nB zck;&R)4&`%!diV5-mwB3O<^oQWJFd;6zPnKPzCeO7Y3eho69cXQu|mwLLmWuBTaEw zCR{oRmB=d@oW9Ni_h03g>y>0dF65j8*=jeEx-V(fXS>wW86{Fhy66$oyh^0B0IaCa z{B6^snvFG<;;Hm|2ICrARh}NgKCVn9!-O#>{kNJ1mR$#^2vAHR(Nb3z4Ps&PD;;=U zXGB^~n4~Fm3wK3%ey)<#k0ArI*O>O~aYa%KVc(2`ka#OoLw{Uz$akr^Is-1xD2YbR z)X^=vZ&_DDAhqrw0s~#nP!m}Aq_HR3g6Jc~ERL)S%E8*+3$Q5`(5%r)Crzne{o`_X z@zT=;eVhomgL2H~T#7%J!9`^Fk~+$G6tgFlA75|?1XWGm+xV_hXnd6>H5!o#x=T+G zGeY{;*KnXCc6@^`B^9+GzteSe=->s#bMRNQbbP!JJY*D9RaUcRtD;N3NS$s5Kh0Gq zyXPb&S}1$Y*P23u2grLx`1>PAq01w11%{|Y(U$AO%of+Atde5hrr$qq#n3$|O?mQJ z_8g7}d@_Oh^rF@&2rSa8uf@0Cyx^VP@z^Q5iy%)4t^K}6tU?XHO4FN$Zu~brcrXp3 z-F(Ck3BcZM2xasVJjgn7;x{rPoAI{+Btc)ls2)wNEIf5f>L+a!Ycp+7=T}VsGkTfO z{sba>zTKjifQ1$4oy!;2nA07fzn4iy=4#ftDCv1zG=n3}5Ogs8v6U;zdrl)W!+>-k zseH7@A2(HPL5fiJTpYa7H?r<#&bqB+-$!(*SVQHxxFNm;xs$AK_fe^K^3J#zY4CM`tZhCd=A!`18<;m0Kd{`g_jpX(bdMN{EYOc~72_wZ)R8G=v_2AL%CimUa@5JhWC z%$h7dTIIJHP?28TcNTU6#^P&hrSS^u_kP;ayzEx4e$pTQ?23>$c-TCdadCl2h&V(N zxywwFcM2au8!A4l1QYkcxuCc}zKiB*ET(!47tL&RIK!v)Q>cD?fvOkd6K;s`t>j$W zc5f$P`)knCK-=%poa=)RU)asM;+!8)CIJEBs2eG7LrA=%*~1f zkomY?9&yELG+S0?loEgPpVMe}pxbblF7@drzwpDp@-GiQko3}|0G@WTmB)O8ivj?H zOv+bS5GT@KD7+i zEpMEV6Y_QBFNf1QTP1ycDSajHtJDyli1pW>W7^-%IXy&u-SlrFZ?83?!5wl+ z6NT709W((QW7T|j-Rf07fXII8NT&-tWEl{uGa?EsBVL!*vtNM|I|^y0(h=sz zZ`>i>Om)|wLIl8=v4F|ydvqk-D4QT50fys@I!eR#R*&VZ5*C_TY|7EiOQCh-t&G9Qf2Zoy){ZE1%qnTB<+5U$cpJr z>ly*i#{3V9gFM-DVKf#1bE@HZtTUd#_-@Yc5rCmts>k@m?6=9)rB=dtkt!s zN8fS2U&(joi*F^rhpd2+absJKMoJgPlxSGe5$5mX^avZs*gCmpu9{FDNVMRYLa1A{ zzucNB6{-H9x9~5fSKJ;bT*Hc6!bNA&90CpKPu&=(c~^B}sY*3U+}G3HJ-~C~-`dph zg@{`(R>v?S7i58Y3^3MB$(VwgPr1n$8x|?oPA2x)Y_%@t*4nQ8r~-}Dl4}4C#py`T z#YdCxePiRq8WB6~DVUb$f2t;4_PQ}UseVApXp(DleFByN0?N^ri@CUHIIZh#t?6S` zfRdNm_OUgPw78+JLXOTk%>5;!1J0trG0DkTS+8ySx{t0=HpSId#!1hs$HweJiV}J< z<)Sadw_etAvtj|2J$UF-N-CgchABnQg#5i}<0G_fb4=@4Y4BY0%F0#d` z%oliQ7Fys-eH?fHnbxSAOonlxQrk4++J~Uo+)c|&et1*UZS4d+SoFVoC3}91{h-R_ zJhl6q{VuDi!jpB*Iv5*q}cveH3i2}tomZax5?BUS6(`jd+{!oJMOX$Gn5P&&@_CmZ!N zxcKE86cGSe`vjqQ=>EIjz=7gPtULL8v*J;Q6GBQqlp5Sym?8OCH-g!LP6%kYGJ*NW zzuV=)DHH?G$$Y5`^2S5sM^=;@Gi~SjEfW|EC{EdGN9cPp2H;U@6gfUx;|mNsE0k|# zEV?^#mv#8tqkoA8ckg)@n)$kn?TByj-1ba_^6aL8J#K>F19Fu$V0!v{Rp>K#qG1~) zN+v-->$YxNcb#rN{|6`>IoIi?c4#2^3*g<73CsgmW|3os zku-dz=VHgU@ptT~*W{7-R+O3!kQKuy4QVe12q%nPd}lmNPCRuyJ$T+GUmtD`ciNk@ z`31CQu2x_z8i_yxnm!T-dKj3q8VNNAIScYe{c_kX+lW5~yAnfi^{>!Ss7Nc@@pBdL zJAjmY@PVfi1gmh;KuBaWnXK=6=ljmi23fgw)XufHy3gmqj4t(3>Sp&aC^oQABzs=Z zJ`QZHY$i@VW~NW9xhGdXtM`Uq?C3{2%02Q6LvgD_C@IjMFMFPeC&SKb^$2VkS%TfR z8Oah%*C!j@@eylL!Oi~Yi|oJDz#{w!q+JV8_%7#Y*%@_keNfrtX;+)LUL4gF{ca}? z)Qs?9V|>{%(9!<{0;dMPEH$X5Bs;6#)>O2bE6twAiei3$x1&}ab_6zGM!z9urUDFq zXFX#D9@?2z)7QF3QhDx{Ss7Ih@J*mk)|x)N`xBqQ2qceZweSKDOUJ^r(i!KP3$FLH z;`*m_2Jly=$0uLCaRE&9f?Z@)0a$VUL+812+PP-%WH7$T*JbpFN$(}Co5)Xd-4h*` zqt!hMprTZ^@zEqOjtuc;$_<@srl(O0%?FL2h5o237}-&xF?mcR2{7iod(Mkf7*Db! z@f`l+HCU%6u1oZlUV5A+?M9Y)dNyD!2Bz+3-N~xd;j(%Pxy%ifC<*0&PUi?n(L+O9 z^R^$%-EdL^X_UZTL;lD(l9Zj-CO=hMKfmCz`ORt4FkD^5JHd4t$jg^)Wc2||%!+Bw zFcv?4n`nJkgU-D5U|IreI25Y%vmQXKVb=--dQ@yK?9zY&;EwwFB=XW^c&MtiwN4jdqS5i5Y6ie_j-cW5s8ns!(JRI zD2f+W48SZFX(Ze^+3{#mF2~DJcRUmDF;e!-EYtU&DmJuP3w<%RT0!!veUET46(6k= zPA+qbE|f~iW9(t{hj)^M}Gluagy0j^bo z4_LyT0}X7Ip6NYRemYk0GI)C~Up+dhHOQysxo3lW(kxgaTZ`lYXKbW>((KQ%2H(8+ zw#qcu40V4h=_zhe<2h9x&qHn-Al_@LY`|L5zLmfIz#A>N)tLnH_g4x&SdEbFIMCO7 z8^%^nhP$SO_N;+0hJR{FE*`Xp&95_6cb=CHku)?5F+5Y?V?mNq7upJp6 zW;5jmJ9!~6J7LQ=CeZWkO?Xp}-3jB#%U?uRAkpbq))g;_Rmlf^zxEvm;BTaaxL+sF zSdv8J9#e5cavT^MZ+1T@H+7XaUpYNQN=bicbG`C_7aTcUArY**mLx~^jD{N3Uj zPuhPjV2=b0z_f%E2~G8QTx&j+HZ|fy?V8RtDru3UyUlTPRwZ) zwCZ2Dgm!ZVIrleqj5n4ydh~l1brtj0J86tmDtyR%-uEs%!MZeYbQMpt z-Cdp;)w~$~u&MVBoO!{j%WvCSJeh@Bk=ump?npv0Qf$0$E-U}L;#2wGP;R=Z9Ts-k;`s8;SzY(tvc{|yI9I(* zox1u>V-Ke&k;=Sbb1ls|xjM?>CY5bd%+H|*D|VFksu1k%dfvU;(U;2O`O8BU-{RUN z9AAMn9M{t67iZ3CCz4OZBAiwtQ)nsP?+S`Da>sYYS@v8LmryTV_pPPQb1veQAP^PI zk(_O6F4eYO>8kR7mufUk>L)2zjrxMhzkBaP@VpBCp3MvJFx~t9l^j0g|HC46$E|B> znY!(WOat_0DUX_r?!#w{{;mxKvjhabXz& zljL_;Nfe%i3^@O~VqG9}!|jl{3Ihv6u!KDN#>R^G#9$^mb{8#yuQMlSX;z)5u|zoF z<5LPYae@lrG~B0{l6hcQV{GYjAzKa^D>=L zz~9k+!u;(`SMhn4ud{itPcP7hW|uuzYe5oIP|n~ALNk*pkvXH%1LH_^VZ6`c&*9WN zaR!ck%QXyn+7SJhfVs$F20LW)Dg&Tg`X6{v=o~jfLi`I_8#Jb!h|Gw)8urSq^Bb3v zIKmN*6!0<&a=H!{>lLUC%%2Oal4R~Ua^0}4?IGycM?cCYTRGAn zumCx;^d0RslTZiP^#i9FYz5t-@!0($BfkTSy7~l(#yG>aIysY*m7VG5r#RipVDm?L zKwMUudze`$u+H+2F|I{AZ5UR^g{9(iu7hs^AHYQNXdIazeeSyS6(8eU8#`0(0|E!W z;A$d7mQEf)0t&_W9s?uL!T%RZ^tkEOvFj;{U;6qfYH*{o>>!K40$hysH_V}dyAFUm z?GLL#jjXWEC|dF@${* zH;)ACKV@MRP1t_D5$DY#Uh&kaRUdNRo8^vvSNB#og4+Ci}rRnY@By?7JklO7E%A(X^WH<6SK&t8Rk>oA-6cN-rdOcQ@64cguJuR)2 zyI+FnC#_V)$uSo8Ifz5L-`QBcN=OqN2ixhOAkfh~C_qQE<9IzSp8@|ZQS-0z`T^A0 zR5{MIacAe71?V_n5AstNXh^mCJk#zjxc}+e11fwQL~Lah$n2@r_X)zv7yTMr-h}Dh zB4O&DZ^{>=SL5cvIkb3k>hWtHMX;M? z(WdTbHs+X~wo_r;a5qpw{MOX~!@-g+P|MN+gzL;MMNR>+HIy=6v3QH84DMnL$13^)ZhQJ z_;)(h8-l@A(er&}#MpLF1F#eS12pV);viA^HIm>r>r?gp#fBc$ZT)SHMr0

    ){bN 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 b`+7j2v}W;=4#o<6I}K1)R97gIGYR@%3Vk`C literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/rsk-tx-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/rsk-tx-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..06efdcb4a2ac6f9d85a52d4528521bbf94c50331 GIT binary patch literal 10994 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/sokol-block-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/sokol-block-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..eac366b854c17b43a9f8cfb6e72edadaeb27c234 GIT binary patch literal 24074 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

    g`vUZB0)|StA;vA|e&?F1 zw{Gtp)!7mr?Ai`P4kZe1ZqXOFC2Edb~)HQHR3|%Urx-dFxqzcQ-=mV4sQz#GM zB44vS&-;MA{?A+AWI!?ZC|q`4vKB4O(oSZFlb!jm!3kU@GOR!d?as5>#cgUq4@;zT z13VlqqYT&53!>=7kJ;EWbA`U#QC{8tZQ%}UPFi%;tOrcyvu1XR{Wt0l~x5Pk0mdnr@rfg!*i)j^~c=qSXm#{>m}oo7enpkV1zB3 zLJ_hz>G@wUx3G>xu89+eCq2l|(ZHc8^x#KBZ77{u2&zsLDQcVBHC6OB| zWiwZJp|2Fw>^_qPb3g{N;OtYExPnnIkUBZhICEZtH7}`U6|gqGz)bLqI3CG2)LTVVa45D+c7c_3~g{p z445M{xr&P8Ccy4Oqs=qw!M{yw&Cw0dx- zlc)XOsF?t<0i8T75bE#e=UU8ygYVmLMl_jRyaKfU-dcxe_wtzR3beLNC>>u-%BwIp wvN?WMU1dNqrHT?td8u}LP3)2c_~#MJ`_#pPw?ZjEdK#LWdd9jX+76-r2RaeiO#lD@ literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/sokol-page-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/sokol-page-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..3bb3854abaed62abefb43fe4fff0dfe62b3ca0e4 GIT binary patch literal 13520 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 Rz<(;Cs3>U4*T`Ch{vV4NM)Lpw literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/sokol-page-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/sokol-page-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..46534388b74c6e297cd68764387a145638aa854f GIT binary patch literal 29527 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

    TRQ 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%0DhUq@^H`Q%b^1qEwTXoa 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

    ){bN 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 b`+7j2v}W;=4#o<6I}K1)R97gIGYR@%3Vk`C literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/sokol-tx-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/sokol-tx-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..06efdcb4a2ac6f9d85a52d4528521bbf94c50331 GIT binary patch literal 10994 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 It?PjNZ!IIT!2kdN literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/xdai-block-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/xdai-block-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7d50faf513273202012fe2e3bdc688aa2952e86d GIT binary patch literal 24073 zcmXt9cOcaN|5s#W@4XLahwPcHjFV)Oaa2~$N>;MTzU+}o5>6;OGb`?hka}RQDMzA`-MJt}-c^d#%;J zJR^1|2_Ao^(mm{9jQP{IEm&U`?}PRf9c3e@$f#fh=(*y;VRmaHMaKa&IY35a1L!2n z^Xj^qmhsYhZ5X70WJpjXBh!oXV<~y{<)#qj&;SHD^zg%il!1rMRZuIsc7P1YL^@@I zZJ!Ved(wuDR5YSvnm+%m!LZ-k-f&5plG+ALPDa*YCcuhs9K?R{Ak*h2BcqtRiqwDM z5zX>U+N-$m`Grb4GO|BLHJl}4djv6sYv4#^}%% ziUxFg;65@kp*)<^grv$?D7w|F>6|*D*iNU*1LYKnga8FHfEm4*h?g>X?u}fN?_}~+ z)2;=GlCHYy0ABRSdlKLPp6oy83F%XQE7gMbtyei0iCQ7mQZeZP5z*UCmU0W!P(VWl zh@5MULvOAj0cuzD0YN@S5p1VBi7#xr&>HIJl3aG4?t~7w7D-c*L_(ck1k=SYHQl%0 zDk$=3q9y&VP^$aRqSNz`;W6mj^LIYQR%Bmqq<(dsnDF1vf@dPb14$S2hjr6;C`k`L zNRr7ZoP}I7&mZFU&?CJ&f{65G=DmN{y^N2fEJaVm22*-zlXmBSV*F%|RH~lobhqE~ z`tJj;xxPqo)D2^E7Nz;2>y4M{B8R01p%?I2;CekB(Oj#%8c|m%d0zgJf64{d6Ya58 zmyx&VNPp}S%9a?u@i_&1PqDrUapZT>w%&h{@U&D?ynq=H(iQKZdHbB>@194N%P!yk z6|e2jD%VB!ae2V1%ysRjHSNszg~_{V03OKIm!4r=j^^=B8k7uTX=2Z-Jg+!fV^n3t zkFrmx0HX{a@jy%F98cH`%BlqTH%-xZ_6xDx_v3vBow828wqLb#4X`o(W>fK89BvHp zwk5MH665db>sJWwzx#zhzNHBIwghwMD`Z*g(Fp=SzZ?m0L z8ve~9Qa++N85}Kb?tcp}Rhk28WBOe?#G2(5k;vP>>d;xalz)p)R3_NmC3wVGYEgk! zzX)F`&d1qrF~z!+fwd$ ze5kF}hWY#h!={djjOCCzXD44qcvvE!{re>zTYQS=&u6#h922}m99(h6+lF|p4!O~Kp;8!T@ZqZ?z>bU^!TosAvo1Tw-i`ecO$by8`J0BctAnB4s@Jk{^MF!&k zy@*Y&06&U0+T5v_RyJXrQ8igi*_-zo05* z53mHyKqcWWn6PF`xpe%}|HzC8xAwi}@3}oGPtnASnt(4t!e&sL(mEp=>Q>YLe+_}# z(}*yiF%^gi_=z+QT2M6xQHj7^d{yq3IZxH#v5;lHW-<1yLZkg(>ay^A9!*rp!{m!( zp9)N4{xtU5`7#hoF0W4i%h%Ba4$8q@N?MyO1D=KkUb_r6=^QCA^I%Fl*S}ni8o~Y2 z&KBh*!owVA%s1YT=hVpkelUZ=EfyNtMtAeRzD5t7c_hgc|0xNuP(uPDTK4SipH8

    -pyvetvG4PC~Z}s5oH4VbFR3z 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/xdai-page-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/xdai-page-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..47d170bb076e6f3dc98003c64d77f3680925dae8 GIT binary patch literal 28979 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/xdai-tx-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/xdai-tx-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..5840f379b85ead8566879565ef78e65483706bab GIT binary patch literal 10309 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/eth.png b/apps/block_scout_web/assets/static/images/eth.png new file mode 100644 index 0000000000000000000000000000000000000000..62e87137cf5ff7e0bc17128d4540d57d9cbc12f0 GIT binary patch literal 1292 zcmV+n1@roeP)X#QVPRs+%gg2E zIYO1h7k<^9_ z4!Y~Y1V=se0oUR5#OG{>OPQqAXVhf7mnRgGTE z0n_1r_qo$~8;45+fh~tKe2&0H?@R=?9WKoTb{)?6xr?q_3%op>;d2BYl{}v#aM5QI zfo+FNGl5-)OFe;Yhx_7lH}=i2H(3~p!Uxf#qOY^1Z7CK0|Id8moEsEPJ4;AR_I%r{ zJ@na)_Yz#KG)WqpC%93LB$dsG8_|AlXpoInBcPS+!S=_Y*RBR&X$EiXFlDu6lp@iuQ={utuL*8Om7ry2 zoRD4%U=G374PNn50G1d0VA-d2xR5v~!Y z(HbEYQ#c?26P`ml|AJtnF+z*HIN{i17QwQ^2&bgM2+KCo;WGN%wA3!NzZcB#cpdZN z1T|(6?7}~wVfdStHqzlT`kavn@J_rSSYTQeIdOt^9>J4+y6HKn;vMeAKIdtt68M7P z9%FhoS#g5xOoD5SvFu~J?U>3Q5qh>jy!hgR{q(aSD^57fBUn%K>d0;t12XRCIs%E8 z8k>Wsf^*`8oJp|yyeaFjgN-;aDMtO zkpeL?1ZTu8FF0f!86|cryeRlj@VWwk)sD|OeJ&}gR|NkF{wQzdS)KTpZSnPT5f!YL z6|CPvF!uKk*ka2mA!W zR&6+BUwjW4WrZj`?E->>xHk}tt3a0jaHZJuPI&-;)h79=oTpe)aC)4O=`YVIIQZAY zV==n5yWO}QZaG}L$OC1*@DBk48b*Rm^ZiJ)qXtn1~bJDiFoPSpb_C~Eb5HIkAhBi-dJT6@@>_A( z<#V=5V1x^esnGJRmMk=0{~1IM?`)ybMgaf-a63Q6A9Q4zw<%iy0000 \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/ethercore_logo.svg b/apps/block_scout_web/assets/static/images/ethercore_logo.svg new file mode 100644 index 0000000..993c43d --- /dev/null +++ b/apps/block_scout_web/assets/static/images/ethercore_logo.svg @@ -0,0 +1,10139 @@ + + + + + + + diff --git a/apps/block_scout_web/assets/static/images/ethercore_testnet_logo.svg b/apps/block_scout_web/assets/static/images/ethercore_testnet_logo.svg new file mode 100644 index 0000000..d114fb8 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/ethercore_testnet_logo.svg @@ -0,0 +1,5382 @@ + + + + + + + diff --git a/apps/block_scout_web/assets/static/images/ethereum.png b/apps/block_scout_web/assets/static/images/ethereum.png new file mode 100644 index 0000000000000000000000000000000000000000..23e2ba71c02a6d9bc75e03df64f71c31787cf11b GIT binary patch literal 9853 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/ethereum_logo.svg b/apps/block_scout_web/assets/static/images/ethereum_logo.svg new file mode 100644 index 0000000..6ca5984 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/ethereum_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/expanse_logo.png b/apps/block_scout_web/assets/static/images/expanse_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bfeaa105961b9a2da9eeede5543c89f4cfaafd57 GIT binary patch literal 35288 zcmd3N^;er)&@NEiHMqN5ad#+Q2vFQziv=j|K?0??wNNMoFIu2QLP&)o#a+@Cij<^S zN}=4G@1FDBf8p*QSXr#~zH9H<^E@;2%)HqaW(E|bETlL%I21;Px({)1@H?@;4~Yq} ze_zK9o8sVP*Ba?+S%Ywo$C9!C!%2F6+sxRE_s`G&KbGs|&%L*Wo}chAN`-|_%ijzf zO3Px`+4^Viotd&tDOp*Vhqrgarjr-IzJNMfU*h%VQ!j;e^^u!)sb32@| zES)Kg4fcmg{Q=FdCr#GPs_&{_GCsW@x3DlV;X*}46;G^5!1M2VofFbqQJ@kM99&W= z>5|Ztj+(mvX=^pOpVNHb;+#OYP^UytidT7W07q7*ezofNkD1^YBz8+40LJ~h9}8c; zXy{MJ6KLN1-}ilHB-49eU9|sW&*tsl5G4We{#gmwR2W{Mh$ia1KDsnVATO%L0) zI&3;d0^&W8@lveJOlC|L>mD z?rvW|G%4FHQ0b2we)Yt@{Y%C@Q~RTewIQ6w=$uxM@+2z7q13h$7?Q*n+paJ=MXjw_ zvj4fTaQAfUbT3`Q&fX3aj>D?x_MZ_jbLLM-A4?gnCFnO>$2!z_%}{##usz}zhv$CAK442!E4bg)1sCOwVW4hby_L;s>Nun1_``1ykW`2>s z9w}(>dGPI5068eW#0!%BeoP3gc|4;Tu|fWZIAt_X#?jzdpLSY_=>wn9ciUwJ=QUq> z$=-^S{pyj>%MhF=Y=TMuz0Ld!{-)9U*K-B?k{^EEP~5qg%XjOYsu|*OkIqtss6XxR zVE0||mfCP7RL&f*(5xX^V$CB836eYAey|2Plen%dFE39dK*j&}J~oecj!^9=hq7Ac z;9h$w(=X@nlc+!J+-W&makEOJxoivWscU`~YWfqdAr^xfGkH+2aBZR&|ZEsH} zDia3NNp#La*5Wj2{(D_tnb2@;T*1wi6+?RMvX8%RNW3pI9yXs-GU_pj0o=Tk9$oon zPiPek+v`&gqqj5zyQ@D_5Lt@ROIrv%-wd8fxI<|C{q_Of#aOKte@)fs(|Fgu8Yq6x zFqHg13xW34%#@!}8PA{JyNJF0Hm&DF3g=OtBIoL&>q=#gl|G(irMe9sbid;0^U1~~ ztP5}JVm@@p%S`+ew|%#W7OkIgvA4gb;MC*9oBi(^-_z``pU9g*o3p#JqlRw5pCA5+ z=i_8$+|x=L$mhG%c^#1Dg|l)(DK^O7-H}yEzDcdpc~#0s`(KRrD z3VBrmNMIjb=K2?EkJEk^7IIhV-j7>cTEbEsbq&4V98c2w!gwD3qhIV@WVlgegN8Wx zG2NG5nb#QmI36%kn|ODj_}jPsz-JF2Wff(ZPxCp(i3G09|Do5Qz<|pP{N{~c|6t!` z8ufV28MQO}6vtuvcpr7rTiEIc*e0_VPAq|)E(9khZdxu$(K97I=w)g+%@;BD=HE~bxFvAZxjw&!FZNz2EF3yO z0(y&l_POuQ&U~hy5Nfji?gP!u@92Psj%#1X}`cybNAvP<}K%6)ZW4bhF703 z^RKcZ7Vf8e>Sq6Xag&q*U<#s;bCm4o{$|V5p5hzrVj0Z zjSAJ#a?!zuOEKv>AA_e%Y)qQOxDAiLW%t_OqKmAG%qz-k{=(36bCUX79k@~(ME?ao zOjn^Ln_%ia0v9oWV9VkmnMm=+N-gzm6$faYkwbuam*tYIC6>VPcIJrFbuM{>*_&pFX;6wS<3cfSr(I9|*Lv~(@2^-- zqOyS&?^mgXf2TUYWa2D^a{hM*gv!Jr zSH#449|(eZde(wA9v?9*97ZbcYyL9xw8?nadZ*-_G)A35Als6gN0}Jr=Z2<|bQ(tW z0@h=H&>8mWx>b`)je<)}_9h^}xIHzl-)KZ_dubN62<$2TK3f_@6#~ zQUrT93S_Js{d&n~>Qd;lD*9tXiBWH7>w`I8yUge;-IccnUu@THA8W-0NhdZ2N3-Jf z&rD=F8`fEbzCYV}%BPmN{_WFg>w-U3c|{clQU0Sm5eF5&Vf>%{RW;f8i|FbD7mld> z`|Lx^o_^H>j3eJ(alP>|=G1a0!rNfBjzruG?2 zssdTYed9terAd8*#Me?9S4T1L@6dNEf5^C0+V2P5+`KKn0%Whs$gXb&4KZ&7XN+aM z2zlq_{e5$X!r2>>NDiByIwq9u&Xvi2bFeiP?bFN5%Fyo6@YE#bEC;mrwc%m86J#s= z@lnZVNspzWI?M)pExC2b%57{r>7B+2dYBblM{I2t^83y_tnVPY+2YZb>h}V$EHIB$ znS}V@zVBO0%SaM-K6}20XtRU*pE4Q3tq8)vapl%Y{6?vK4!q`6fX-A@`J^-(T>TiH zFy)p-2$T3xZGkU>PfMSC{6$%>u-wd66f#jY?1L zlN*YR>Fp%n-c;}!2yE;NRlkwEcJw>*?!iIiw2rxUCfr`vI>%iU-(4k)+`*Qe zx7AW@(D~On9k*zK)-g}vkaHY^FlxlC2zalWx~YpM!1A8`D@flb)$fB-bz1gf3_dBs zj=7>;%xW*;u}nLURI_+}8GI@hSHQhVx@1`rp*S5LN!He7p&{_ zT%7>M9o4`|Kp-sE#En5fDkGtkLqzl7`H9RZ^%;{ZrL1Dct%4ompmo@VX9SMKdMR?vRs>q&cfV0*ouW!37`K>^y#G$|78z0!WB<3IT zjB6(o<53eE_gng<@rg;=pdPSr(^8i_JO2~^*103`>q?io#?1;{f2+gLC8vD5%U$9m zY|9VMzLM2TOkOx`s)m2*MpQEy+ylmLo4^Li zrP%YOAcwx2&kUu`*QZ_cDh(J;H(HORr8h0wJ4_s~5yXsiyhJIf(`)1BFC1!1?FHVL80z zUETo>TenXsMgwwXnxJrhV^#!9AS@EYMd?OW1D^-cUwPE-wOM$Osb^}yfxfdMT!RZB z+B;GWM2&3U==H^ELiC5{e(#q0P7(!pBp_MM)c7y0ib?>lZh+%PS=4h3wlq$|Bry-Z zF{GJQ#nRa5WkE~ITZqn5v3wQ3oQ}w6|Kr)Qe0Rm@1RB%W%i24MV%6@l*mGCS+IC-i z!;e$Cb8mp2!d^2SfIzWvz>i4FCL#yCwUQU{L`J!fM~hbStQQ98YsbZ#Xv|!c_6En; z@?ABY;l?1`nO(2ru@}MYLS{sjVPx%ym&I>zYA5f3h|i|+;q>irgHSpV!9I2(G;x&s z&PFe;!tP_^A!gR?*LW>~tV7hcyYf%)6I}_*hZAp z0%2_f6qVu)cIk0w6!Rn3+w1nB>5z)=Ii~9gFM=u{Ho#uB!`L4mg^IXZ9qpVSKR(UE z`9cEJO8PiX8}O{a^$ks%<5)C@U@T9OVbvPi4ya&MQceil?c0pIig)vW{0mIX`Di3z zbKBg^Fy#YOl|q= z$8rSqn4jld8A!Mtad_*!c!f&q*C`~frys~F2EkayILND%54kyLykNcCJigiJ==!wY zCIlul-!*g0|6Fp(af9nT77{x8#)tJU{s=^_WN9z#hEkGOtI&x>Q&Giqm&Gwf(v|34 zddLpH3GZ7v@Mt=QT0;BVS3U{&AGG473LX8AuckXJ8~KP(1;UbtFN=V!lX)1rjpUq8 zK(Ru{$y396c#1V~$h7wpT+UHmjKd9mhr4svL#@(p^b7)KfAEgBh&Yc;XQo=)j;>2Q z5c#)~MdsFTYeZ7|iX-%9WO%B;NyNVeD8tMTbOmTcg*yjR*LAFQBOvMU63Oi?THG|O z5%AOW4xxRq{>5(dxhLzI}V(4s$SZ zf#bCvQ@%(Hb-o)Q+I{+shn#x;jQdbSF9=ryp2}p`Wcae%W#kh};8<)G#AR1v82qB& zK|23a7_PsdWT?7&ZY`3e2R#4i%eSw2yHUd#o1jUDdZE4@$|ofK$S?gnj+20H#SG>t z_u%YGfF@OZt_NvL7pWY8RFtml@HIt1n9@54QRtMzD8(9 zgiB6C5g;Xf{PjcV(E}%Q--@W)fR5Aa)6=7?zo>CCNerV^QPEu=v%F;eMU*ID+xz;M zK8YJkav9T3X9JAu=mf`{kG`yoCeKv!s;4Q6Fj9Y+O6|8Ko)BqF;J$+T4#$b4^_%V3 zS3$1aSrf7trA|*C;!BJInWFviJO=O>J!lbCpTT4(rH!0@>4-yc-K6ppmOK6@)JPi% z>C8vua}njRgyV5_wjYnlQwdH@t(~s1hTvcg!|ih;9~*z#5*O@sjlq#X{KhB@up)+w z!Q5ZoyvnRsOsjw8l^nSoe`QLU3A?L0-)=g({sM(gCO`A*A@*Y~=MsAXeSiOfy|aST zg^g*1xKM`(^m&|E>Goj19bO8)OcG8ulT!{qSCySM?X1z44=qOzfET^xr6ZWVPj}tR zF5B^c`r0b*PU( z?==N;3p5{$?op-nTRtz+dP(!R+(1*e$*Q=uqOy7fS#0Yv^7wWzvUYno1~~%n7+wSu zs?1iaH7ezhwQ6#9l0lG`Q^L;hZFhG09o2e*tyGP1vm4U9?&TPajQYG&<{T|##{opG z1OM>gcuWUGtAj6cnhTK1Htyt*!(%ukU_N?H8ZaB}o=oDHXUY^2L*CYJ-v!_tH!bU2 zq5czwqZT+}zW}HlZF7o3mUTbsBCVYuTS>QNfPYxe(3o)9V>z#~OMUA^xy!tfia^Ow zN(!~Iyp$09eJ@PRkSe})T;DM_0HyAj{W*eQIh$Y!8I|jnb&4#o0`leWxoto;4{*m< z+w{}@RI`afcY1{Xpcpq3f8BH)StG^q{ET9regl5}aBeSn@x9pAuROy24W4L9 z`vj?0T4(MPQDbP&u-zrMC+&+O!8=ji22$(QkoE7sQZuP;&sN{8Rtg9T+{JDV_HM(h zwnX5nKVf%t9o*E@W^)AIVLc8GdbXa|xb34Sb|F1sPnaZKsW`%;plIBiZibtgeeN6a zciHh~deW%|=+m(Mq0p5$yjZ(*;e`Vaa-@?$7l}hCNjrSe&dbX`5bnZ*N`%dC`NNfm ziPq=3SDbI^KG{FP;~dw|$=gi@-BhgrtnejZjA;CKIZC3o^hekjJ+rH9ZPp66!YKWm z$XUs`rH2dXF!#h`RG+0(~i-NTtZrE6YlOY zz$rr!%-fp_%t&*=z@3+$Eb`jx>%MHXqb$v8Nz^Cd&Eo1d_jU=?__StNqitx!ap zc;dtD#}{-qJn0JHG`^~rofZxJF!wIdBW;05hQmP_HcCMVhuY=t>gt;OSx+ST*Eo>YwVyBx-9 zs5_zLj@EJLHOaYD2F1#NYYRMbfN(!@_3K36UW;N{Vmjk65X>=+kUtk&+5v84kHA>Y zLkZ;V#X12BJKDov18vVy&QBPI&;g=W^sLx4SvdHyyLb)!?Eq2qF76a)zgKbTTcj%1 z>l{m|c1RsF-hInM>L{joV1y|}&6ZT1$6z4surIM8yWA%ZnWo{DQzKJT8W8A1AU8K% zwcRSYCseGBfEi%Sr789(lfSx$){O)>`@!VP)086NjxBAsm>;@zP)~v9zx091FIw#j z4z8}R-_|tx^|7l)98l$(nkV{huJtM`M&43Y$_GCoCaQ+=Sa`5CjEY?D{TXQb#IH~0 zn5r=vzUkv#r6vKd-6d-h4`LY^8<{8qD|jS5BCoEYO_e~PlxyIdc9~?Myy|HVVa$zU zr|vgOlzVr1^_0{e54{dY_&} z47u|p-Aad$jeN{`t+QK<95&`tVK}08iN9B=Un0#^!68V>8}+T!-V$46y&>CJhg63m zGX-JOEX)Eu&0yygCRb8^3bRG`<_@$%17x%`@yj(Qo<@_Jh(5y|Cq^$tsFM7^r?nYK z?#o!P zVuA9dm1SCPoU2ezB9BRON z2Gx$%2GYcuFMro-7#u06*cwZw4uwYHe%JFT&$zAYgC{Pk9C)n~{LRk;Z^cP^eQ*^)_#PD$v^*x`uJ>s8ZS6vi$UXF1oXPxg-U>Nc5Vm9=ZD3T{k0b(J9 z(R+!ITKA=Lq~0*tCzL5Mw~eX0$LI-w2;G5xS}am$%)@XRm|T4G?d!tA-Sg#BCq)g7 zjvy;BIVXm&hrDWTB;~qx4;zcgj7f;U8ZCHP(j?>HXF=;&9u&)qzNTdsqZhW&19S1c zFP4vbbKbA_#ptvu)SX0Yy{DqFD|S(2KWN(~Xmj|-%#rh5Ut`eS6=nn^iq+xsa@KRN zxN`eAIAJi1a8!My_aJpG4AHgr#XD5vUHYrpI-xa*OmqMn*0JVDg7G^UI48_e^n_rm zUKYmaBcV(bTg})UjZf#>EQh&%yFAgbdrR)MvX+)euAe$o{d=?~_F|zN!3Zl5-VoLt z%e&@bWU(dkklH<$9ow?V@60_+EoJf7+qsR`<@lzBjizqN{Z&SFF$Gxt-<-=f& z^|BhmxOLY;LRmJuvqv?XR)jarOV`duAgg%GNow3Zn@gmPg+a^LT8UoiRcW{X8MMll z)ebnrydE*TaTt^KyV2)eGkJxS#&LO2tT*8z`pAtHKl0_EKcvH$jEgJpnUII`8;spnu>n!Ai*S9q%>oehhW7b^`^TI9n(^MtBk;!w;6uJr@JCgW_{(R5wB2U32FCcg~7c8%>8at zU-EY?Zx|B5ywi@|AnHtMEaRt0*8>Nz_jU$a`q3^#09cy}`#S{;VXM^*F z6yJ26iIOD}xcWM#C}_QJrPD1nVMMa1(XSx;9BZ>WcsGV*D@R4jujc|oUE_q(*%>YW zMu#DDOz-SjMp0qpz3Jg!2tnlAqlAGI-qrwzJoCMneVPICQv`v2CBhg4mUI%fJu>PsB+F_eeisGC=eE#G}{C6Ae`p5D!X1pgDg` zTdAB;pxFM>#NS9uboWqT#A@KdY+)6m50Z-u%6YVbw%3bggSIV%I-f(EK0hnhCC+Jp zm#k$F?@l~?^(WHY2%pP~(4VD*NXHMhI-vLCsq}<85b9vU9#P$4! zZZWHkyP-l2y#Y%ruf4iISIU=HRZ-JqnDP#d_@wB@iZbZU?cm7*X(MmqVqaH5em`oLY8vpr&5Um2TXkgTr2*p%0 zo(s%bC^(IUwnf@JX7_M!1P_o==Xrh1wTLr}L?grg;2DZ$ zBT3pgj{Ry^iM-m$8%6yt`!fdLhB=5-VMk;*D_K^{l2k0>p#aT`<_uac z7tbt-pdgQC`|)QpXCB>t(@w^`Y(;mOtBTI`ved&UGw&84tEV|Bw<@+M6$XP>mceLx>@5YCEhrqOFuM#>Y<|NY?NbDBG zZ65ls+f=qWclh`(ia02Qy#8G)qb?t;$W~EZNk_v&uZNyqO|9J#xZ$3bwa+_3`K)BC z%qgl7^daXlc+>qo|I~`8fGH&iXTGl$5Stwb{+n&AT7mHsWx7&SW+e{bs zRL@M$8cM9zZL=EGTbie$DWCC(`D@t}&PyD88NBR*Bs(KsdJ^qfzq!8N-zGHY(4{50 zdk#xeLlMZ<{j0WLdw)p`_T$z{M4rA^h&CVfB5SpKM6&dOVBJ7)QgEodz0Y1w&THkq zx3`ZTH4Rpww^fBC=Hy{imw0FZf5@FDL_w5o^E0s<`I37)#sQ3M=YJebRAThz_VjK@ zZ9UX%90J9+GKsp#y1{hi6LFz5R#u#q3=hE#q=>pvX*tMepaU#&a3*p@1vx$gy~A#Vd6@ek9i zmqq1kCU2dCZm%i&f>v+LkL=5*K7WqfZxjaaZOUaDpzl>d)}?~61s@w^_Y^%iZ=4>0 zy=jd}@e`qh+!2JCUf7{Gny+TO?GwG70`l=j`Xl&bJVq>{NPFSlsQVm~&VoxpyyvRZ z2myQm3+yxIzB9rK+&#vNkq92uPn(l~+oDV|5a8I#f zh8$TR&0l&QA1Kx?1+@6k!H2ep{(Qd*{rNu6b_`{TAa5R^JZHq)n#cdPtV<%w{zPtW znx&81)B^WKk+Po=2{9jyroHp55f_cq(}}496^nzlKAFTn`yLNVMdO+9Nq0ldpHm&5=sh0n@#~;T+Y|S4l&l@$;3sVm94= zk~MydwQHILV)%YebTcW>GZ>&9ZU~p%tLN|(kYpfgbB`v)WydsQDX*B!F2ox*@3E-g z#+`G%4q@Dr(yxTJ1k_GDr|=^?UO`2G>;4%!t9?9pp8Zb&58Mh(j7f}Ynur5>C#`{Y zSm)?)zQ4p#a-vxzM+0R-e*zjB8L8z2 zQo=q3NJabvQL0FJ_^GuXK4waeXv7BobThEX!km?!SlOgDbwS9o*2zm))nfmb)c&pnbXg)WT;ux+`iHSjXdI)T{3y;6J@R}?^1c7;Q#d@ z(E1>)kX8yxNapFkwiK$ka0*#{7kP$0Nx>?$0APb3OHiL#fSx5eTIz4bPwMlApmCor z&O4TwnCOd8*1yyW&tLc0qT+9GhTh77dCgt>O0}pB!tq2u-)+k!5im+FKXXMIDn7%AU7d|SbHB7*1#-(IN0Zh)UQ+s>IvRdMtZ~t^%25wvqP^%!R`PJ z82=eN3Y3nKHl>Mw3tKZ-#{VK+X z)}YI}+pnOJ&I~8HTI;;ohJ0k-n!bop^Kf};(`wq8N!;EF07xUo-Eoarr{qoY!cO|? zW0j7&oLJeaDy8N)!6LZaC2Cht`=5NVL46fbh8c@3ww#h3^1+TF{*P(NGwubgKl~{L z!g6Olh9r1&xpUR`0$V{kPj*NjRrizD!b?MI8stDs+vA&M7fT{wZps~MdusqI!XEy3 zX)kIcML6g4p5I2ElcC_gP}Gi}V}>}OC5$W>aZeGd=rEiVvZs9`YrLK{vxjrJ!vKH7 zB{kR3*Ms}LD$aR4S}Nb6lTeN}{up7kxyDm4t*$5B$rWi0)C2wyc zn)ud#`g*HnDc1Ds>@0m`7CDcu=E3_kzM{bFk_Co95}xNgdoM}FxcEFDiVMNaxFTq^ z1Y}KTbk+6`KW8q?fqa*342a@yp$vsKd;k}p#=fA2`|9#;Sa}EWs$D+NPEeYO{Jv?b z5FBV(^`-_s0<(X@;6a=N9AGyHBc^(e%|_Cwkg@~hZhk7Lpc}$@@Lrn&BvtXXNwQov z#ev6jQ2nU)M(#L^-&4|jD#HIH2vVg!}Tm^TbQNvRFPccJ{guL>d}$OUeMxdH6bq0 zwcsx5kpVu{O>deaZ#1z+IQF5rfG2;K*T(biB+JBUR#bR~M@Ac=2f))> zH+=amtpGqv?b*=&NJYux7L83|2TxG(KqQ$M|I6zyc(}UOhlBDOY|EdNN?SDo+uKyHuf0vJvIhxd z@YB5BNv;X>mQPMj2Cnve=xcK@$%RyMQclfKrd#MK2GHwC=WV;nUW2G6bE9w*sNzsm zD@P{`1H>)|GH!n29AnYwmvx{&ajm_7TZW!b}SeICW$ZHW>5;IDb4C3BH_e+*w1L4cN5_*=Tt#Ddv zU=y@LzGqXuZ{b6aVOkG$^Fx9b#C~8!72{-2P?iXecsX$$170#|?|T5ZbyJhJZY|TR z5sred7Aj>Wsqv2!I8$LVtgEJx$^7rG(${GSxb77I9*C`~>E*qIQe^fzhc0I5cz=(r z*&3PaY`cJ#NNFFuH-YV1bhHO;b&kcX_m$hrHGH0&+NheMr zYKok0U2PaejtOiA?m*uS0$lBQZ)8c=JEc%z)y@4toBN7OULevUdp&G2-ol$KVlBd> zA1rKQ=f2cTYm-M2Sf(oQdd^eNS{qM9lS_{f*ViE>3#_Y&Q?6;PdkTW0g(nrjmoiGK zt<~FY)$4-mN>COx%_tI?bekkc)WiJN_HlQT?4~BmtLz7h1NS`iE5`-Vc$a&6ArR>& zI4+aDO?f3ChV9AzPGigAxAs#WN%!s3rLDmV6%)M!k=b~nU38FLAo*LROq3AExZa9q z7pd)@e(R)`PthKALW=21mT{iVZahY3I3u&V4a|KR&1|>DB*5^G7tk348F>7IDj)Ks z&&d?E=3QkhnX<{*%mic?NF3Ib;=g?6`ySuAPJ5w_qkiOpTAz_z znBnwkacb)8YC(T|+5*pyfpa|Xbwwj+3txWIORtEn8YDEevB{%y?7v9zNDcebD0u!d zi7S%MHuc5S5){}HsFa}jC)UHmgZ6XhoHrRy)h)QL4BipSAu$L*y#!3&8n8aJvjL3>*}{P!gNKPKbLG>? zI@yV;O~HVfJ`ZRJ&6v4Qq|7TKD7w79yAS;y}P2d_vydBkY}&m6?dAJX<7m_o0u9tWmYY4Byn zecfqVsH2TI4bh8U)_dsx!REk_7MJ89Q87Lb_3NJjM;xm>-^9G#DUH%r?#i46r1om14!q zi>1}18RVyx*C|6Lw{jd4vnusM_JSo~1r}KG0C_Q-5Of)|Qa4Nl*-s z<4Aeo3=rP-IDCM)cur!>2Th%j?&njO)ub*?3Y@KzJQS8z;$N40 z99$a}TENCr;O>^(P-wr}XUJF1=eqi#n@a?`g?^v>pdsiF&%l#;nm^tGYC|E_T17oN zK%hf^Y;PXx(xd+1>aH$nn7402f-@Jka1Wba%f8FHz=Q|hx2NKSyRl7d8is7q2O~Il z9vd60=A0tU*e<7q!K4NH0dvoLEy76DlSOl*=pj3}Y?n($#}h@^W_6d{&f_P9Gkaz) zyN#qLqQlhXk=n*_RPXX{2N0`GWn%$leZ$5mBUSM7|3IPGNHa;(x_An|ytpgYHS#VC zT{hpRhELp_%E$HLJCOgc658dZebb|I0L!O>s%(U2*6p6u8vk5^&Z? z(b(8Xo{?9+$}9}D#MYiwrC14ZtQf;Lnb(KQiKqv$OV)=S;&F~86876~IX`*z$?deN zu4z`s%TGMRSKN9d%X|;7{scSpL>s?kut;{Vb;^kE!ypvL?NqXek#So&`xU5NTj?;iE|kETB%p(WcenEA-7K z1yslB(K65^Y1-QBrBmZ3lWJ*leZU2pn*`1+(UDUXoN4wsWq8(uZTx2sNG14!Xg`pU ziUA84EOE;-91WTRv4Mim(`RPQoo?ZvORtFAGCPjq&RVX5PZLr z^DfKD%ryNBjlhs&HBXMKol4NON{f(xZtYINC2pg1hgI1qRw_mU0#K?o6Wk$hJwSMn zy?bS^?RHchGZq!W5q-inlT@MH>Pc(9jL2@#ySO_ypUOQUXE;v0u?i*PrX_NSKIrYtfQu^DrjfHZ{k-hR%VT3}2Tk_>N;1Vr?B0 znw!hXrYQWo!^7A57-+OD>EVV$C|<`D(XMG<j@jfwfhih+;b0f9%sFHudh(Z z->q#dKq5~2m6f!$Dd4as7hp6$TWewN*fbJ56e;btHahL011yGO8H`?V_O`t{z+8%< zm!*G~s*}~6r_C|%YL6;RT*NqzLSvMwYeuC*DAq1d*jm353vsJazV8;PHfcn5cR(P~RbyY6k{lh{N)`+5nvbR+=CS9AgZn z&nEh3@;1myNi==X-z1NHKIcUs>lB@?ngFy$Q&-e8+El@!fmCF3jXz(IT5K3_@ItWe zJ4nP@nP-rgW=?I%DLNp;anXSnY7|RXS3|!zkQGMbf#98lXb4U=U0bWw3D%jP>n-p= zB{r|>;7QO|{f9grtXnPp*AIhCnmk-TPt59$EcV&d34z^{{-8S0)L(p)FEAqS(oAiF z*%WptH`0WS*Hbx>J}c?x)1qMMR=7yo-1ktXHoU%F8^_?D31bl3pW*ln5IbZ~t_N_m z```)rx}_2}W~t^TF1NMKi3CnCqNH1Gs`8>=J;6bUbxcOx5d0+@`0xP>WfNBd5xTb? zch4Ttw8;n5F%TYDE`DXo3S_K~=6`CU%ZSgVP5eS&@PMW__yHMiQ-lRBu4ioO^FuNz z^C&$#NDe3h<|Ka(j!;0>#fB2kX@dE^<^?lUIas!n6_1efl>tlpuD4oPuR2qCTjEK^ehyxqI z@^^x7m|RBvjaxbi5_5qV%7oPF65D@bH{J{I{Y4EZqYl1S58w1ev+~P-mX1b!MR#ajh=Zmz)iz&`w&cSI41(S|@9F-HnF`T;R?(EO`xW zF!s$q9lG%;oXR6PLsBQ3R`eb#X&1!xpCg5d9n%7&-MW-ivkf|z*H5K5#^^9y@{-+7 z2eBLJrG?Y-dhUz~xnyYmz)rioNNnOd&T;%$UYG+h0*`${PFK=T;o{UOcRd7RpLhBn z=JI{(zoeUJ?`uhtt*ghCshgv2Sx?xb%ahF&AtJU>vD|Q|-oZnY?61XDuq8B(_d*9B z2r=QYumXCfzj*&Vr%z9`j{lx8FVND;bk;@mHI26X8Pij0r-ux!6~1jXhMA2WqC=6- zZb8&DuG8xsa0%6H#!f;cpmN**%|yAE)om#~p~i&3c3%8ScV@b%60!Pu&YsZE3I3Jb zN(+ZAdi5j<5GTi2>HyV{nDsxmN}G({>gkHjHz`l%;z}ky5Gc2Y*31fu+<#B-LzkH0 zpBk+`2i%mU>DATwu-s@tmv;@2#gQ!klA2mePM6C!mDqBz&YmACyr!h2syUrQZ2eG_ zwm|EpGy9TBBkw#njm^t{0Gf#{NRzIxgApef`kL|nuu<)W0ZNxK_L36CESDV(_snQp zOY0TziJezZ{R&o);%i4lmUY_g0ad@W{iv3Pu!ES^!_>#IPcd-9QWf_Ct@$W-&29)8 zJ$T36cS-YB&y2w7NR!)8iiMk+>vh21etW86Ue zR45@madh7Bw5G19lVf?u{amaBD=-z~8M{(!wHo4e+iR2_CywJyjj)rjzY1=R``iHQ_Sr% zKCBa(CTJKXwXoGeE>=z1ca5w* z1~mG-3Fb9G zUnDBj`Tb+9r0tG!Yxjz@YBnNlRwX29A5E{GvWS&huXZg}(AVVOphY?nR)P z6rD{MePp2JeGoqO6A}$Xo{a=f3yQ-r!*6PrxMoxbMB3p>b?HI*W@7mPyc~Kn;6y4$ z3w((^B$Q>fvA{4pi_hL#8J~+TnU9u&nTa+_+Wo;oK=b7G?#QXgf^UH?ar`-!jPM$|3? zR+?oSce{`M;)trn(ql`hmEMjS3YE5*X|5|wG30w6NW;ZrQK(E&*z|#~;5|1`Pfj@Y zC|g=H)(5DYqf4d6Q|LRSTgoT$S}T=l&XEAu=xLU#d(~hgGuBP9C_F;R;1$anJcr|S zdp*0(eNDCvQ@-JsP*`IMp7>pcpvR^;CnQhqMZ4{60_l@YG1Tf{GE3GVPUrCmrQuBq z49*glSRWN+FNS-vLp(exCXgi@ZilxL2NCfzZIYVBwk3W(3CgS z#Gz$)u9egI&?ElvLD=^rr2I{E7nKEf6*FFV*`yVQJ?gn-)lYlXywhD}$CNc+wZLAP zncWVQ&Q$CbHrZ`K`#GkPk`sS*n;~5GJdh-%>T0P?|msYZ#!33u7_}CH{jV zy>ySh0)_N*=+-N;UxbmoOJNPAzLo76^$#PSK^96N9wbN7LVXt&haIgqtjTPJ3%JrK zpBnZU9!A#nXP4=c5o>~cfcj+ChMIcvUuHiXE?O@>>Xhzp3gd5A)VZ=J`YxSGWZ@=x zDLjo&Q8RiKMiQ(w(`>{ByF&GYj4Z1-Bqjt=(t-MRfk{MYt7Bj*+)HsA+clkhySWw{ z%?_JiN&Y`9U3EZHZQG|rx<|K^bVzq8DWH^;bk`)M8#Y=*KuSOm={*voq(Ny!I=3mH z)KG-^fbiWu@Av<9`FKuhc0*6W^PSOHkk)jn-hnf zCU-5^dv*6~zb{q9l+6!F#jF&z*DWzm?7UmSesb@ARqRqcNQ~Te( zoFN;9@6qkdnT_sVPN8=G`=Hks{)$vWIU-Hb6?6^Rt?Uv6sC9y}sa56S5OBCg?F6D#~gKFGT&a|H`^j16DA+tk&P@!hT`Qbn!@Rp4f zjxukK2m0)JzC< zW61z2rIIoRb=9nyQwY&TKzz(9#BMB-zUHp2v|t-==ckS5zQ<5%hQIQPfI#2Ej{%Y5 z3u8pQTGlt;j7D&m##y#+5Lu3XgjjJijCj2sp%Hrk70~ZVi%n)NAF~n3TTWL#dDsL0 z+iqwOv}4k7K~c8D4Tu)#G8IhtP z71$zr4dIm;%Zx!dARg02LZSuRnbb$auu7l0tAyxwQM^&?j|cq+<#0LU*G3m9>X4TC_#Z}?VIZVwBt4vQ#!1j&kX*s}E1cJqr@?$h ztB{wif+rWo2$_2Zd8Q%2okk|MLKh9@kCCIEfG}U~n}M?~JXe6F=o5b^(bXpY#Ow8K zHywStzzy-JyoJ2U^(k{s8L@=rSWi?ODH6?{7L8}kLy~Zfq+)6967(n5U|g86LS3Z} zzhO!wd)tV^$e-o-#cyAQ$_BNOo-o1-L8z)){L%^8rTeGM8It65G2v#c$ec^xzl1+U?d%0uqFj-gX z;2ButSB9q67)f1|U&zJj5wXO*GUhOAe6Ublonl0rqbCiUhu+gpCVHZwobGBKoBvkk zyY2(C690(8QQB6x%z~3qRhzU-IOHCKM;GzWX?6^fh6y9Q-m^`S=Xc*%{)$$8xV3VW zAIVIAcP&{E`*Pj{6wev9FIMh`^Q$FrmCM+cr4I+;c$&r{8l+a`{<=3vDHwKN@??d^ zmxklpGJer2*|HDVTRc>tuJ2VH?P|ccOFBdX%{X*D@j}YOOBZqRx~A4JUiYqEz7em@ zsyurzr=F`W5o6B_$_0g9%3Yddg@^@NgN~=gOF5l|aUv}mO#$J5_!IUx@4ey?*lWy7@ywMLArht) zBLKTECxPd#TTq6PPKWsO2`U#|tuhWjODO=1ayr~i=R{Aky*-uY9MBvosvXS7QTUHO z2hPb@AdAmdXi%UxM)TTbM|+x8oK4r6R%MeKIms?MMu+v1u0f^5`z?K2p3LLKuP8a^ zfSK83n>D>Hobx=)Xa6*qVrE-vJ%!x#kuQ1EP~vj3c?mfcIu%K>}EOhYcVQwZXAm?z68fZLkda20pTnW1ItDQG0Ut9L;)&F@{A`0P7psFT(H6D+~}`W zKcS1HD{vMQXz|uOk^MTQ?|bxv(=M$I$tK~r`caq|xZV2Iw;>t$gGKza&YN$A=&fBB zSLJ4(iGAR+&=KKH(@68x+{uY}@VTC}Rhx*VOE8Ux*3*snflhdsZb8Z!v!dZBZ2#@> zN=#Pd+&12sBMEba2LyPjQ<~B4rzIu*7ua29bPNT9$1^e}pt^X(hqm9;vX-!pwD(TR z%d^kgK0XP6QWKMrGz?iR`?mh^n3Y4}JM_~diG5xuO#ays&vw2G6@N+*3p^+ygPv=0 zq0{n(Z6-s#7k%D1eCev@fx}*&$*ow_Huu^rfU$;;XfeBm$S{i zT}Ol3$o;^wa(`Zqq}7!dpXX=h41X}4evMuV;pz*WB=n?Z#RG=pEHND6rP`uG2%yqU zP|!=VdAN+RLib;~pNpyFhV;ERBn@MYNX9(t+L6-)u1#viKW)pIV<+rQquyXsoI;YB z03U!ApTj^l;7l?pNkq*t1W|O^&gy4E@VtUsP1z_Q?N61QbGs!z7I{+PZ=cvT@v*k$ z#^W(a?TPlJEsPhe2jiV}4Aw-edn|Y8U;2K?$<^Ub<4df~sr^u~XKb1|kQ0A&vNKonI5oG2>Y>VK^cKY;mA7GBzVQwi}K7F zVrgrWN$sRgh3Nn)JOtM{I?VoPgmdJ<+D-L@da0CTfQ*`;_$f+Q+)vm!2aJ(q3v_u^ zz^2=iW?mxV4wY!%e)5+yTG}>YdhU?fao4Q1aKG{=LD|?mlF7=EO?==NlBz9l{!iql zlsS&py+H@$O6;~gaJ=z(XYICM&4>65PFfOL2rUu!vrvryu!pcdAt8~T<$rhKYx>3@ z&C~4ZZreYlRU*x;nmKfOdbja!ku{L~qx-DZb8Yc7DvW6?4j@?2XQNo2D9DoSrVmbP z`wx;ey2@|#*og^VUzj6WwO5I0;xuSv>GW;s(_+q} z!aev_K26U}f1>)R^Gu&N==b~@{j7kJe(PJWqOvy@;C2jz?Ghz0p$%=ow#9KpdA5zg znPBA1@2;h7jlIKlDAWjTs@*%0j7fciV3XKpVQ(w>%TYv_TMI#nvR!W0ls?)y0odmS zV%j+mNg0OeC^ReaaTrnGR=K2IWcDNL!0<$orvinu&$1h1Y73k4iH}{vIp(3@)0l5W zYOvDf)Ui@)z}Snt(`|%V5KkpJaB7Rxb3P-fJX|FE!0@lDt_-(r4hameGT#gq$M@hR zB*Hv8ppP%L_2)lD+S={X+0Cx$FN|Kdw#<6xx@vWV>T9LnktcbVs6qCZs5sQROx@?)Rj*+y#ik!g7KtJjgix0&Bb;cm7IlwGBsqFhI>PqU@8#)O|^O zj&_S;v_kV{C?kI1J|+C@pi;`M6!LJareMYFG)So}CmUGO=;%UpUTM*_eZE;l_z(W- zbJJuaPuix7T8Ba&PiArf;HVW+BTc{&j z$eS_zJsC@Zc&RO1skxSQ)|qQqeP217M3+MCnIf;f;ps7pa84O`kiIh~HDEE)3~3l` z6SKEz*HfeQz)W6_k4@Y{gbYOkdk z-R?fGo@9iH`xy$#fLrw&*Ym6Swqj5Wx8yG+umOcd{WQq>+$DGM+e z@(YL_Ypuftv8`ykQ?e`0;*&9JAMk4wW#ES&oAq>i{5c%jS4fz_BrZEYrAI>gQmszz z0A&eTgPW^JmGqvUl<%9TVS!}aW$~l1Ld%3x-D`Oa?TX;ICfLF^-{gQX zu2gHL*G$kf%2$z;q#6RNN&jCXw4}~V5zicVAmO6zCg{1sUJ&e;$Yw?cDyk*4y*N|5 zR4f~6W*F(NA4$iVU}>&=aH=NVAIH6a$>SG##<7jRBhn+>xpvxQ^K!^x&)+&5=G_8nva&+RrP9Qs%H%CJ#?Pl&Abt}P$k7iasX^xT6%0{~TN8KFc(MH#+ zp6}0|$>qN0pYHyV@=V0-L5S`m;&<6Mr!+mM5ipqLP*hRuwg4Ec!qIY$&N{=Eu#_d) z*P^2tadL|)`g&00R=hTzb3xBr}VV8JVo55(EX$z$U*qb(iO~a7)1l!mGl1_bO zwzJN+;)?P;tZ!JZ>KBw=ExF)?junhwFUia;C-&$l7Wx;KPUKC40taRcph=21dbHp^ zDj^x_nHmvjbf(1yvC!4Aa-?Lrwf20xLtO--@_t1oYbctTG zT`!ofh^}H#tCQPaKVM(23tiXE2@vh)8?KgGy!5yK$*1$A-z$69vTVTn>6Lz}yNEGc zgoRB7AP}KZ46*Qf+81Zi?=#C$IUxLiA~x}S+Y&f5Xge_aD#=WcH&r~|BP-&8{A%&! zv@hhc4)u~`Oe(b$5K~AhCiIPw&hPw}l;Q(iKpf{DMcj-&{yT0jh>`R@ASA&Tj9FTF zuohc_JG8m~iE7HEEa3g7@4+6vqPj?=3prWCD-Ov`?_!>*I20by9vLqm39V}i$)G74 zPhDA>4b?8?TD(o|=u$;x@pCOfeI4$2BW_#16`R7Qe!9aGh?rmZHXcZgI(T7O{QCZR ziBWJpAIylwE5cM+#j1YL0EjM5p&T(>9WK4qQ`=TJxlnGgfV7K~=DBS`fW?zN?th09 zIj|`4Cx{+d@u!gA4)y(~N8@l^61I=kWFO{vjkc2kG?iM@Xvm(eFNHknXE@QhbFI*3 z^gEm)0Ls=Ky&fV#$;MX$0hH2xZZ^Nz{FQ}*eU6AnlLzwbx9V(kXgqf;^0bMyA5k+? z@$vG^@e}gg`o>vfpyTPnU0GAfGChvc2-v93DzK()6)V!aO=})+XDuLB*yY{5&$#h2 z^MFj$9_EFwcM#ESa zfrzV&=@hlweC^8$b}CH+>3BI(jhfFeQ&L!ia2;z!Y4O7J%(s9ZJK46fqXw~+alLpW zK2gyE#^(>o$3^|pU&wuYZbMqs3VS~Lth1%nCOot0l&n>Z=dH~cv1;m$H*cdU&@@Cu z(ZyyNiZ%}1zr1X~4pft}>6X7i9POpUEVGp=eCtuyIC+SzPta_tR z0N5GjaB4ZHPo7L@2?TvN)D&Q)4nw^-X8*y$Ie|bPOJxL*YFO&*>H5st`i1$vbB`G` z4_bB<)nU)}OZ*kb9`Ms9`!l^f--}yCiH9`OZ zx;M`Ic+l;|U&Yy=1a$c$Ip7Wk1)^vl0(+Tq3)@Yg$rReumjQa_0r$yUrE9``z;Mfc zS9sbDLLo&vsA>V#7`vg=xkmYzY>AHzVDq!W3B{AAURwY zpPHoo)423Kphxd>H1RvV+sS<0FcGoF8zUAjPEYgw&einSqMITqH-f}Ex7);9H(a`Q zEsE+q56Vd|b{<%y3JU`vKX@x>no7K`2oTGqpLnUjnWU<~ ziyJq)WS?;n6TagKsFzlSjE&_)M%~{YUoVz3$PpW#{6`BA(q zp@>_$QLC$=m7S)Q4v$y&49}XKopVLEz+6oXN7nSuPn(arJ$4)HRlRZE1c3 zJsr+7QFGgRAHCWxWp=J2zwxtp!^>xqt=ab7qmS;jMMX=~GwFyU3egWlM=Qgv~lrZ zEjq>=w$R&G0sq|YwOdiqJFjh=SCf4wCFrNXn;Ijd`?cR~vv{4v*D^@4Xx+OpTmAworTTziGGl5~U%d{HC;+2}9ma)2?sb;zmuXA3Ty{sYh4D zP5U5EkGrBsszlBAYMT?Y)+`4_8P}bV^14!ar3MJS?m{s}^mTdT7 zIsXobq^4d!kJp^Aw)a|FTt5JTeUASA)1N;-N1QZ$@Sf0N{BOkH;2JlRjBFHs*x#colFLv!b7r#Nd_|(F0T3GbC8)7zVnTP^XV5e6V*Shi8rb1 z>Q;w`k!ekza|P-a;{`6iZx{OQ*h@>zQl-9hdAHiBxZwDzlnd_}AO8csRh>$aG;2xP zq+<}!NCeU#Ik zO%L>JKe-%;|MvTHy|6_Ntt)U!&5vcVOwSZ6sAbB&-P(r<2d{BJbfNdV2tX1DV)GL* z=;>%pp6*YfnU=GrkmH_^rr~l&uJ`HMe>40toCqMnbU*D`cWV&h5Q0ue4oAkf3I(#u znOxffQLOZLyMaX61$S?fyZ4c9vL8-CE#7BVJDm(mH+n+PUj49@nOm%JkZ9EG_Oxzw z|M&BM>$>FFDGp+yI|R#PR<)^Bzbsfe+$t<7F}KK1PByIMdJ74qig+LE`H5@SjBT|7 zgC7+Mj%|c?Ss9EQxb3K5yWzx*annaozUCG%Pyz1EDNtEs1G}=!&n0H zD}{s`24oeuFrN67p_m&7gZ-UuVok%L8H9B4AQeH>LAOd_X(K&0F%#EPZ4cWT5?@cQ z+H9yC)x{YV7}w^Km?H^pVtF~oRD=i!C&)kBYeB_t->|f>sOzF*q2f~dQshzgYgU^& z0yV-!Nt}q`H|*Pb{N#a<6sMQ?)2NvB??ah0XYPKr9z~^d)6||Rt@>7%aYn&2Vk-5* zfT>kp;fUkZ_wI&#sL`w&%gx8kKYn*HocpH^p}&9=mXdm_q?Wr-fUi%a)gG_&!9QQK zox~ocd$-{8uYd^Jnq9UTu9BbQxld{Hg|%!%c|Z4-wfjvOnG}~7%P7jac|S3T^)=Fo zn*`qEM*$rERm$hyPn%KQxZ5?oy=9=pA%7ny3pPv>6iFzCCm^{}C<3e}=ukVjRBIgZ z3}y^Rd8Qaf)HKM2A^r?tlv_B`_o?>qpbfK2A}b7;ucoyxL@jFr0KTt&xiiFDAn|sq z+LtZY6N*pB&f<8bT6%Qms};h4kiSkiq@w%*iy~G>!$TgM{7L!2BWJX@0Mg~{_A$zc;6JgLr0-^ zx@om;Yu;HL3!KVY7IR!yzs81_3-^A#9ZNIdhv}pleM(5&AZ*lg^sYTMKzS(pT7eKw=m>bJj_JGA4TU7qRoWZ`*NQ}CJ$n0AY-W5zfSY#2!@7UI(EGkO@~rFn>OvK}txd(L@_MO0i%IxRrJx&jA7$fmkLox2w8VI-N1HN5 zdpk4i&-^#eJlFI0^6PDq(y-*2c{$1l%IxDcJglpXViukO=^fsTy3lq- z>heL4?)`C3nQ)S=vBbh((+Y59C}~Y#ajil3Vql|rLfwI~K#np-s)i_VN= zCAP%&0&~aDtc3za)H1DFw4qIG+c8n}Oqj&GY(u|rtf8o|LaUOxP|f%dnqpIz3P8k1 zA_lZ{xtv;c61^rXEf_r9;`95LmkUo$ps7?GXUiDs@V$Z5-XGuVi4)@;`rg0;>!iPV zQPyGKt>}l!zHfC}ebGjLHUpIVxnB}mwman+?c$C)KR?qEp!?Fo|A2~hrM*>brICB~ znVz)mE3XfqKk4T(Mh*@9ptoRljffFYQMY2Ne4I-H3zY7Hi6s7hno7+sxwg?5=yuMK}B;$TYJ7f7sUN~{{?fVw#x596v<^XHS zSLu=l}MKEOrNmY@wW7LYM3K=okwgZG6e~btBG!@!39Oq_bH~dBINQ zmiJj-rjk7|i4VU>%h?G^T$=P;ODxmED}uSKoKksrh`2XMXf;^8<50X44oKv<@HtLhW6EAmvzMD^Em z)$VgwK>ir?i;?_FeLUO zlqH7i0-LP>WQ}=dD6%Ndg39(~8VQuzfvys8!36}~YIhp++=t4F_Z#Z%F}R7KAV|{a zs@|KALEH8KT9>uW&#vL)g+X#MVOU-Vt$7>SI3)H0WWYoFzmir?N=ktDVCCJ+ya`Ba z9xKP9(9%K}|b$X6iNhmLyl<4%U{t)#B-+30Ax*=Pr{uBFOIwQ7w_vY*DQG7Sgp zNxeff1L2L4>a?5Ev^V$ERaMO%Fs6?FB$J+YU?Yj2ETVg5XuV?tREU| zlK;`-VMMQuPV$r4NV5`rOAJ*M#LC7d5fr#M&MXDhgW--qsVf>Eutac;OeiF!&f35# zg)C7|?BHc{J15MXqh`BJOIU&|$Hcjazsej!1*6;C46p($rQNJs>0CPss3&5x3E>Z+ z6wNNI4}l9A9gUMb8s^!pA=^c{`>GCD{`m8E06hVZXY8Rxe*4${_+HM)Q!{E6Y@B`f z`QC!6&tk(1)S;Mar6EC4#!>}mGY89^_a}q2CP(GJ-ulsRcE_y0beNT6ee3t}_0$VK zI&#C8H*CoX$pu@UEPoKtO_%cud0@b8%Z*Q7PS5)g^45S@ERxlMJxSHCHUaih7|2CQ z3`qj=YlYkHu*+q!tNF&nOn3F5Ki=qa=AexwJf40NgQLJeoIh-~#$b=e5P<*wlMG;IU+bxv}dE^aUbqgVf zALgpco<^j`3O8j1F!`ovgBVZ{CHuO=4g{-lC#NxJ5;f9TZ`4m3nsAn#qvxI;+sp5q zO<|hs?Tc%fTPHhyr^vMEeR#dbDx{~^MJjsuYN5Tc3q%z3X5Sx7;HAj;>Cv=%2=bi=76D$P`~F9J#1Zvl^y)pNh8 z08@%U!KMLsJI_|m&Gc;aBLLzxJXJ~A+IBASK;Qu?XrCzfPz?Cxh#57HEe6YGuCEzXIIyPWq-OeBJu{E>-Qw_jR!!MIMh`;CEwV6 zJ`-_??P0Szk8;(Xv3Qu@);sIYddKU%UmJ;LvGiKgG6A0=J8#<%kDo|!yGV--^A3`9=GU^1=I2T=xLK!o1D04hm!yLuKl|>?N#H zmO_Vbe&w>wsu1OA7)SB)N*+<>}(sp-7yWss%#l)XlR5|^4 z>A$q_Je;O>EAIN>8&8p&WUdbpUWwiaY@xi6J_ry=P3n|;?T;2F(S#82=%nPBLxe>c zHc9Pa)qXbjve{*oWqiE7$G_BBeD8YW$5sR?m$2yl?O3YgA<$V5$YcH31OH_O5tJka zs9V6mw1C4?OSBKi2cI*OL)G|G0>KD=-_fEu`>=L6Ef=D73b&pB%hHak^gLmXN!SmR z^-ecN4Jo8~U?o2pKw!6kBS^FvgdQ;*4IR%a$u|rMp3t6(H3EGrD=2p&m_RZiB>w~k z{j*sZee>+;ci0=1AA2sLN9$hxp_a{C$5c@2V*-Ihi6C@5@hTEPcz! zLqV9`OE^`=$R&p-(V#X_SWBaWOe>8fAvsoo{Km00>_(ASjQ@0?!X0__STMDUnI&{B z%|J%4UAB7vO(}n`dtk*lr}#Z^Ds(i!*f4a{l*Lcviss^EX&Ed^X5xc@TJ<>*<{Y6l zZ6zN+QOsa#%$~sFU&>Nxz5(e@yGWAQ_;fMG5& z=M(>Mi7PyLZd%}#V=o()(uFE9Sr^^r&B)H)Ur?Qw8B_W7MTRr_KuS4O1+!hD4O}ue zBl|?Eb-Cw4tO ztQe_E8nW~xcpP55PWZWQQdg5(`_9duDO0nKk^xVid^+iq|F;*QR&F*zz4^cwJver9 zZ`t4L=W=|&wxGiI$+>N}zunIMU-KVuiI1qQHw(e#{o8tP_?QHSUrl!78^e01JU6`sgf@IaGs-q5hMgWnNbUzHv4ckmdM7V zd;$xJBHhIwbpDCt+8U^bqwk2dRMrXq!Hh86q+-V&WN{qDZu88jpZC=GWfjnaiuqKi z2^hd>G=lS7>RuW_UO}X7Y4qo#ujgvoE649}dZ|3D)GYS8udw=Ejf=$ZA#I8J-daii z$A&u~iHRnS+te_gEGKwyP^O!Rrpp|Xpuh>?rO^~j3bWOzZu)5SPDW;DcL(5^N--US zF^?^*z_sdT&;Q05vU3eHK@or=V~kCIjLMOjufmDg#hU=`J>pW03hj144BCXDNGJ#J z!7}x+D^@?EIH5QiWmKe(@{O%T3r)s#ONeES!;Bn1@Vlt5FN7{kKVqP6R}t`s0D&+omV$$N2T3f<$W?GYiRtWFgnXk z8<)FfOhZD$%T1yssKw1o^~2*He+Cea)`II7C;3#@9k&8 zK@QInRLnaaB+w<|#EyogG|Y&AN`SuSX8E8^m3@l(fKmp=VB{mvkoP}D66RNDqLiX2 zAA*wrhIl;MN!Q~fwn2EGx1n-EIqD5ifZSgq5RthVd3NN-6|?6=t^T9-&&B!vuL#RV zhbb=yJGYuR639P>w#f#%h6azSJ#FXm#ftRhRjUnoHJ{=0z1EP>x?5Xk0LjT=;+HS4yP7@*-%Zni zGbV6%wJCdUp5QSuTIb&G9v)n(j^nK*9jr6zaj0?bRK9w5;3#{?#WXkc= zC1o5z*Ph+JrBiIvs6jwb`Ak!1f&cD(t8c^TblMw?v-b0<)ES#f;Yt%m#%te~ddrQC z?_UY#(PR3a{Wi!`G%tZOQo{cR8r}o?aKqx!8La9-=$R*WIYF5|1}~b3k<54C%?Vjn zhz}4MjRJHCE_Tt853W2M@psjai1hL70LlE8dv6VyvQ3cs&Hhfupug z-5Ls=Aom>{3j`7-%Y%M=_V3T`bJ1HTw;`E3FcTo!^`5ikxbbYpUJ!ZeZ*x2c@_CoA&?9p$g*nsN+!urvyt^5{2-sw0xa$4T*XKxLP z-qs1{u;_>v_J426jVF?R;2M`?^y;puRX&M151n2q9&bDi5e)%(y6_h?flmdE$M6>m zckeaxrIXXqkp1^dty8fL`~Plsc6OrFb)n+|W)(oozl5a)BCV&F1hI*NxzpkoCxpJk z7R&CfYX1G@&Vr#%wL;Tks_PkS06@`3(x0Pqz5-(#88wEv-Fh_{$q|O}N{8qsUSPSP zxBV@9I8wqHuBj-m*!N486~qURF~mI?f>_6bUCGlU+w}cF)y?$cK>qfs-QluPbzs-$ z^x}uBExOQaolG-YE5(9aZzqpEFFlSvu4waF+Gz7RZ)!eV|NY~_V|V9Y5E;3z_|TkO z;9VtI*S9+ViC31NY9__a@bx>A1EKN!IdydpNFl8F!sN-daWuhnQa_p!NjzRR;hF0m zlom2y6rY@2Q7<(-`ek@vRnj%09@m$olNlUJc1Ix%q#e)ejSxq%_ob#=NmiVmBk6xm ztHD4r*?N-tuu_#VTJG}p*|t9g!$>Dj3yYOwFDP1%u{7>tZ}f~Xx(5T*Kuh5R<&R(n zanLmv&0!HIjpDu)9)WleMrLVwSd^; zi}QxZ-A6tWegA&XhfjFKhuV8ueP2b1r9sZoxTN+=9asIRvA@J!gG4vn&{a0_k*;x;o51A-} zdQp)5m=b__>e2V~k})>bIGk$~#7>uwCN8II>*+$z>Y%i0X+MD*UMiId+l6*L0P=y` zYEKdf{q-sVFDKMPi_wSDiDQhuf|mqM!^9#TUF3H0#V1N<+uT&a`%df! zPbhvehkD_Wmg1dCZMw_=A%J%^caY(uN&Qij|8n{B7d|BwQOgZ zMDbqV-ol;C#GciwMv7nUQmdb0W1W`DRITLvlxZ#adG8+wA$Y-}OP*Yh1 zk~P{{F<^q=tcR)&dNQF*0vWbOX~FFW_#q*-W{-U1cA;EUwr_b6Tz#XF-A2q}fgEbE zRH&X=MkvKOO583{7Tz&WkK01=Tn0ta)B`uv$0Cq@M6*(DpQAYGLj1092=9=zowP}- z&oCJPg0h3-{3(S40Vn0#sS4;R5jd&cA>Xf_DR&QdYTvbir4Qq7h{r6hRGiA$v_3RahoH~`v3Org(lfdjN%zUDo!EB+z6n56f*3SC6PCpsGKOe@JTrri!xQ`vrOiq6W(U?s>s9<{DS9!OY)Ryc$ zb`r*J)9RB|JbW_=;GP73GQ-{HWFq*(-)wC-Ozf8=nr&o%1uQl={ImkcG0vP`^m_eG zR{GkMSUzS<@;9ae#fAN@7?pky;3A_215_SBGejH)9QcF?@~bU?f!CS5>HFfiCm}dq zA{p+Cn+}HQc@uwN$}jBllXks4>Mj+EABG+j^nZd7vTxy=rw;J8~7P#I}EPf2le0->fzD zlMRp11KdpS>9`2j%hIB`^4ZM{soPdTRK~a9_^v)6-Lg>ZXCNbpsHnB#SL)lvN)N$)8Y%vwT~>lUeRa0D_|p z*V?zY0W6q*wY_|=T7kY{h!RPlYG~M#u%S2d79_EtqZ&F|+mNJ7Xy_yWlD*AeFd96H zVW8d?v`$Z_wXC^Yt-u0P?8z!e>jNkGHhs9nXlYR>Y6(exj*>cPf1u0VU``AA%{HuW z14{jCQTZP_cRRUa;1s#Jpc(b+h3n(r)`|ZSncur!6e+#=_2|(AnFdh;1q})G zLEmE0tKu$iAg+md@BXVUmYI#~n##eH;ke`}yDm7=%Sj>zKE$j|Kp>fZ4{N`k8tku} zRPG|{FnIT~o^{|K-m+l|g135;A&F6&_yM82!oH_ZQ}IK)~DhqMfv3 zy93C5r&S3jo!O(SEDFu8%GO~4jvVi^7A*bmL+j4Mo)Vjc>|cN~z)@AsrEPwmJ=BTI zVN@Fjgl^d!#s#POsfYd?<5%ECT!bZn_*Z$)E@w_<$3nGa#dtKGXM4&hhHCLAi^ zo)eCecjOkCXs12#KX3=viD$mkb$_PV5ZSwx`RhXc+xf*7r`%!h&^MNmHFx#JN-g23 zXlLSVl;WhKd|No{HQAO1mr^`&&~mnY;mr{raF`Wc{cP&}Wa4`*)TueIn1S6WJ{DK0x``IUY1@o^YYO`T|?^|DLzuk2Ly9&oRr?XOC!Rs9Uyh;&@ekEUDIOGz3Lw!`vE>#37M#+;q5|t1A-B$E%<;s6hz_+8vrESM6G^!$t?uC$ z6aL<7F`(NTxe}kj=-Z`52L4W{BC&K)m21wffioBOSe z8`*aF|H{pd8ph#}M~WVGM!W}&``@ixdaoWg+x8u$E;onYTR+=8Zx~e&Fy#TqPU0(o zz=B)A7;&HER0;+?ozqUNvS)pb4rXE=;Ipd@0Lu(PtglV=A9j55SS%=eN7#Cj>o58b_Tutear089;Zuc z`pM))nco^XB==&l3!6OCp4sMS>(^c1+4#NgsA0V4JIuxh7~}=fZ^tQGf2TiIgLzzE z7B#5Mi0dd1{tRv)H!9_OoknARGIef7r;!eDovB_q&)Zt*WoxGM;Yt zhwgz(;o%)c?p5F{BqCSB-M(rs&1>E6Q2F>qHf((yVXG)F!QiOsH{<1W)G6IL{=95) z5>8~@|2ZlE|Ic{>d7i;rzeH_qkA(&Ks8HXisG>z1cAsqH99_ctAEBe@r&sIG&baQH zeH(R^W`0vg`QO#DZbZ%yQh$hQe>$;KlBU8yqhX2%$G5ld`P^E3 zS;g!+hlE$^Wk|{2fDJ>SqVjB2J`)cMODX?8AUwQn>lSkUv=8PMZWBRfG(?m!gWZkI z>KCZ@@2@&`o+wteF)oE<`&PjWZ;Y_c=Tan0u#a6C>v*q({O{|_@gaE93k$!3Tv|Jo zizw9O6cjEia&vPxlC?A&+MF|J*&PC_buzoMf~t0(GF`k6D@~&y`|ra2Q0EC(p4CpD zrC#1A0r&1*sBUcZ?uLei^@Top;_Zhpr>l}JfN*MsS3iGwXEgYCSy4X ztE=ks8}mO$zePDj&CoZtiMoMgI3Zc<{EsDX+ODh+=1~fdg}(gn0XUZZGx9*6vBFgO zet=T`zq>V=Y@F7ePkxWRTZw?b9L+#HWYYz5tqXds=b2;(< z`y++BzDh;UMuba^zRLS9;{t7M4ev>laFWvvyotED*iw^HL})qF`W*5GxQ@{2BGMk* zSPJ3NBOb^caXvY&e(?ED{sifNKh&?~SfG`Ih?weNb*`ScM%Q?cj=E@OS*0!^1Bpi~O_Wb{E1|Km|U+?x-sIU2O?f%MH)#hfI zt*x@s{lkNUsO#nB<=Z39A1w^T;BuDihL6~9R}w`*_dKlo8;+m;V}7{tZa0NHHyrz#zN744Pva^1e=pv3ms|gMKRfJZ@D@4~)Lc95 z8Y9QXcwAgWdF%hbyL~X}D`dEml3C1|4M#m*Y_Jcq_3_#F`2L+u4M0|{4}5yLk1UzN zV;n{+OpknX#qO_2MoDI2q{d$AG5+sI@b;~5MKKs{(!aI0wExp<)%PQRiJFo+x~2m0 zn?5@`Q*o3Ty5p1s89TW`EUFb6@L!h!b#3u&1|_;thFu%i^#9GuxM<-6a8j)x1^k1o znYnM|VZB30&^@cAxAq&V0maiMhWsC1G7VUOv!$fYc+|1tZKHM=rD>OU@7_H$@Ok9> zzd?Esq)lBC{^|A<@23*|H(X8Qu4?NG^A})tqk3gzWWYT@J8hlpNSmM6`U@*NN!s2$N#-cJV*PPJf{C!d+hG=ShLyZ??2u7&T?<<-{pOMw)=9^ z%+~0+PRv;9qFHKkc=a5^Ww%S`oL;-tUU1^PmoN9|h(fJzI4r>P{@(8Q?V+Jpr$6WZ zsc*J*OHENlNl9%;SIpY4g8UmFS@cZ_Vm>xwb@0gt`6m^2__}m$Is(ys!^cXMoi9+t z^!=TEZ=UFeIQLxmTGYiT`IXlduiaYKGXKZAppW)mfgy8d%<4+v zgcK1Qo+h~P$(ZHd6%u#tTB@Po(RFLwWl1df+Xg;jh(&8Y1qy4T*{t!!=ZV`Gll?`11v&h;k?X7_3=op*89rfXMz z?5p4TysBPwVpSy{G(3$CPTZVxwCmibKU#CM_HWjjY-w&iqxSuS10OV7Z%RgMP5s6m z?)F+P`|a7jt6ryboumY5kz^yVFM8vTWPbrU3$0Yuf4gk_IG+)TigHrR+InD>!?!U>0Y=; z1!{2gyB*#P4DNUT-`d!$#K0iH;nernIljHt859d0|3Io75QM+3s-||@O1TaS?83{1OVv)A(j9D literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/favicon-16x16.png b/apps/block_scout_web/assets/static/images/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..4d11480368a5414e2cfd33f0f8809652ce880ffb GIT binary patch literal 728 zcmV;}0w?{6P)Px%l1W5CR5(vnQ%z_SK^T4C?4}ixrb&CSQlw%jA~jX?;7Jc213lD(h=><$txzgh zkY+c>{GBQ)YNZIC1-*JG9`vM0EhMdYP>U^ksBU6gN|VjbjLhtAQip|aX1@8}do%k! z!heH;l>|Uoo{P>(5YEi(l++F+0im8e51q31eJC_jqN3PDA+VoYX6rnX8N zpfy4uzpTAaKzTlqA!!r>bO=o$t<ob#wFBtrvVmWh!2jUd+4fu zr0Agw*!l4an;-8$@P;lHp48E6aB$Dd($c*y+cb=v(aFF%82ZkZ0J(?Qz!)OYepJgP zRKGlc9sTE?1H6g##WWh#&k!Eh z3b=}{%$}UC<&}d#BvGDET8xZ*_s|&Xe?CAoHqm$f8M+RqQLnCG{dF3`twV4?aABt= zor3mX%aQ=f3%wSj_HXQn^`ZON4ej3A*OzG2Rw1~wQxi_`8&!2+qrhC+NCZ}3OhT$n z^$BGpZ76Ga+$o0M(M=F4X!(%{@DK`dLF16LH)MTyLq72;cYmln5e9MM5Z8sCa zU?MXzwOvwasqParT-b(wWA5vz@H(I7x@HyN>6x99kKYy_lKlfNR50!L9aL!m0000< KMNUMnLSTZW1XAPx literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/favicon-32x32.png b/apps/block_scout_web/assets/static/images/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..a97445a2f3062c65d2eeef5bf718624c7f6c2c36 GIT binary patch literal 1618 zcmV-Y2CeytP)Px*3`s;mR9HuyS6gfpRT%#MGh2u)UAqNJ#R8fTq9COaf(nU76r;j}Px6NG5!~-!wQ3G0u!Gud`ix?B}g0x)fwv<~-ch1rOoHMhtyR(Hj z$z*nBXU_lq|My?ML&$d!0G0P&{l~GEXfaWFrVIN@>SH#%68R8-wrPjyL|Y3@+ybB0y!uKHx>y z&XSE#Y@A5D#!D~~20>I?xpu&U2%sN>PUArq-C^(ZvlAaqtZ{&5KylpDU-3`LD=FoI z>Z;ZzTltmHz-K)Ur63GY&#uxM#m0s9)v#^9HUVRzbwm_F)y-L*Y|W)3b7j68BL}>f z3El0nTH#QWX4~>c9uD7%GWHD^C0$iNcTKV-G$-sD{JsTXjCp8+8E|TMX)Q=JX@{-L zByyu`?P{MCugqzwWc{2~c|ch1$E*TvT!QP2^ARr=L80S-fFT(5GGZYG9?q0>js+gHlpqwCH0D^Q8)tR5QHGWAvoirnECu^kcc4J z(}rYE6UZ5lvWGrE@$}^yxGsRvE9m=S6|VIjgyLc}hRx|JNLQC?eBQc&CNC0x6?ATk z#z|0vG0t8K4S>Bbnzj@bOZI3l&mCHUk%3O!wfGan}H&$pPSaTXCGoX%G=W17PD$I{cFtY(nYmb+|e78~P5fMERnRFuvq| z-}p$f3w?)QK)(K6DqI)Rm5`&{%TkH?8?H3c@z&xvIkcJGWuxX~5XO7X zf$7hFr4z6J#3oExv;zf&6*lOE>%HIN{E=6oQr8XV9NP>4X+YYzM}?G1x$4bu0K((| z18~L{W9EuqaO1*{=s)=mraiTv3*K+TLuWq2rK5GW0kNBDgJ5IcJ^z*rfKLW#0i-F2 zG>C`-IRcIk04S<>0@I%ST!;JYfu}KH#&d|>w z6rXx*B_3o7yis^^+nsTQB;n)*Z(-ux^-yvYXZFt4^*{ZY?@&<0G?*iG{XG6Uumo~s z&{PB)wKRAM$e}Rd=j60WnAQWPGP}0N>LK8lAyY78*?tt3Ex_=lV>tWGa!pI&)TNmE z=%*R2aOjV{xOn7s-!MnQ&wjrQHAQjm1-7T=PD2JE3b+wDy5ZgZ_{R{Dak%>T*I>R) zCnS_F-i-;i2Y7v;uj6H0J^Q6jdA}0UlqfkW6P2$S%QO{t_m7NEC zV<%(Dtd~%>@IAj1@-joe??dnRtISB|&~r`(hf%N(=LnK4AY+bWEoTT$LDXcq<$-5` z9KC9)fnm6^{sFBCa`dwAduRkaiE?UpEK`sK6oe0Z9y$#LcnyJDJ_+Bkt4}Mm`+zgm>LhJYEOG<4U`q7_75Y`Sb}Lu z?CU`S0-`i+Hjm;r-)5i-g9@N?XK9T?s@W5#!4HkVrXOU@PF_B3!()y7`J%* QnE(I)07*qoM6N<$f{iEsy#N3J literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/favicon.ico b/apps/block_scout_web/assets/static/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9e95458e99862573530d9a8ad592be72d2396f5a GIT binary patch literal 15406 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/gc_logo_2.svg b/apps/block_scout_web/assets/static/images/gc_logo_2.svg new file mode 100644 index 0000000..d7bbdd8 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/gc_logo_2.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/apps/block_scout_web/assets/static/images/gc_logo_old.svg b/apps/block_scout_web/assets/static/images/gc_logo_old.svg new file mode 100644 index 0000000..ffc8501 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/gc_logo_old.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/gc_logo_rebrand.svg b/apps/block_scout_web/assets/static/images/gc_logo_rebrand.svg new file mode 100644 index 0000000..8010c1c --- /dev/null +++ b/apps/block_scout_web/assets/static/images/gc_logo_rebrand.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/assets/static/images/gochain_logo.png b/apps/block_scout_web/assets/static/images/gochain_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d60ce4ef5c602d3bfa74ea749d662fd544ff0af8 GIT binary patch literal 13703 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 \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/blockchair.png b/apps/block_scout_web/assets/static/images/icons/blockchair.png new file mode 100644 index 0000000000000000000000000000000000000000..f62ffa6c69d5a6606c007b35b956d244aad71f84 GIT binary patch literal 562 zcmV-20?qx2P)}<=s9Cq9RvVKtbGk78p_Tu1T95{PiF3a z(A#te-=OnKxW>#RFjP+O+`T2Mhj z@hPDt7&Q%O04cu#QUZ`hfK+0QID?Du!7U=ZmvnV@Mu|y_ACL4182B@H+83UyrC@5g zdW{sLoPuh60Kv^O1VVglASp>nlk8j!kBXui>(5sq zEKz>t&yaZgO2BHYr;R{PiV?wqNj7}*)7?qtnzV$; zc4lU~{=4aUe`{(hyj9g@{!&#`KN{qp5%4kZ1ubSjI+?bbcK`qY07*qoM6N<$g3KiM AWdHyG literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/icons/blockchair@2x.png b/apps/block_scout_web/assets/static/images/icons/blockchair@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ad11e733e26084437b339e1bd52f1d728a8e615e GIT binary patch literal 1404 zcmV-?1%vvDP)-3ZM%)l+O}=mwvE&N-hXEM>W1fh$Me~__lD*5mz0pG zYtgh>TKzh8R;Q<>*;6dH{&^!gDaoyA<0f$`bF(i3#$aw{0dJj0MT+It9~Y)ipYGD4 zd5ajOiOEM(f*duT(1b=n>*NG~FYnS6%dPJRX{o8IjvYHDD@}|)855*XI6^yh7SxYl zL)Ewyv;u};{{TJ3a*JKPa#h}_e*GkC3o9^!Tx{*39Wx2)+s~i^_|%naLMzNC*tS)x zwH&09h@VvJHE@IS<_oxU&@1ZZ2dG{UShY5^B0>JZIyGw@Z2=y@2vMfmETuCJh^H=fK1J_Bx~ik=9pV{T0*^ z80hP#r&uoK0$!uIIRt_An{lhmfq{hsuh9HG17XzNH&8_sg$-lq+pBj?isjay;3L3| z`>Mz9pqmm6g+>V`6M!jAp$hYP*Rn~o)JfweXejo#zJnB70XSLS{{T&NGUWbV;I7Ne zg%~cajA;7?@UU|RGiESTNztZR%b8<_k5p3Z=Xg*o5@E-dE#9NkGP_v@1oDuTIYSp& z8C)^y4Y0}7bi5S~#caFUaFNQv=)Q@Q&F6mId&VtVuuw@cF4nAZldVP~F@pR9-bo0u zp*pZ2oMD{^nM#eP22lg3QT(-c!XcP!R~-&=d;W{vom{>)s8e_1$rC5!ta_EI#~C4F zs%X_R$5zf+?1M6Ehk}g-SXZhaHQ0di4Wvd;v#5y}>`)eBxg|KGtX_YStz5pMhC(L0 z#a;0A@O&{~%&?XeL&k9td}A}fxFL7`1`Ya#Po~CTt3_+nu#Er{xgZ5(7}U8NSFW|?B=I^(QU@?x)i}D-QXXy$$*aJhrG+Y9m**J39Bt*qVHJ7#>-G88olPy64ghn}G?NUn~;(Nys z;xC1xEbu;~%|A;?N-pB1)x8yd`(lM_(QEP10T;eQpA7r2FB!sv@&K1ES{mBDTbFE8 z8GA3KRTK$~kea&A0T|Sk$6ipc@&P8q#@Y4g+#_0KY51yIx=tvmslgv-q%O7rh7ZIu z9?KgX(7S&-Z*R?Wet1$jhWjnT{t%$5x&sd;#?N=AFC2mwd4SWWO!X~artHT5SGT%L zcPtEfg*}9=2?9#0YX2Q12ryJ4%7Ipyz;R>7ju8t*5HN=FwIg#@PCnlLU

    veQ&Ynx6Oj{_a1(@6i{#R + + diff --git a/apps/block_scout_web/assets/static/images/icons/copy.svg b/apps/block_scout_web/assets/static/images/icons/copy.svg new file mode 100644 index 0000000..89c13cb --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/copy.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/dots.svg b/apps/block_scout_web/assets/static/images/icons/dots.svg new file mode 100644 index 0000000..1f7dccd --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/dots.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/assets/static/images/icons/etherchain.png b/apps/block_scout_web/assets/static/images/icons/etherchain.png new file mode 100755 index 0000000000000000000000000000000000000000..b864c2001a7636eaefb0f32ba87aea0a4264dae1 GIT binary patch literal 305 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 XUqb{Mr2w{600000NkvXXu0mjf8-aHM literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/icons/fontawesome/bar-chart.svg b/apps/block_scout_web/assets/static/images/icons/fontawesome/bar-chart.svg new file mode 100644 index 0000000..26f56f5 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/fontawesome/bar-chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/fontawesome/github.svg b/apps/block_scout_web/assets/static/images/icons/fontawesome/github.svg new file mode 100644 index 0000000..9f3f18d --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/fontawesome/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/fontawesome/info-circle.svg b/apps/block_scout_web/assets/static/images/icons/fontawesome/info-circle.svg new file mode 100644 index 0000000..5ae697f --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/fontawesome/info-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/fontawesome/tag.svg b/apps/block_scout_web/assets/static/images/icons/fontawesome/tag.svg new file mode 100644 index 0000000..d18e312 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/fontawesome/tag.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/fontawesome/telegram.svg b/apps/block_scout_web/assets/static/images/icons/fontawesome/telegram.svg new file mode 100644 index 0000000..faa8d3e --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/fontawesome/telegram.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/fontawesome/twitter.svg b/apps/block_scout_web/assets/static/images/icons/fontawesome/twitter.svg new file mode 100644 index 0000000..e189ab8 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/fontawesome/twitter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/link.svg b/apps/block_scout_web/assets/static/images/icons/link.svg new file mode 100644 index 0000000..097e437 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/link.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/assets/static/images/icons/metamask-fox.svg b/apps/block_scout_web/assets/static/images/icons/metamask-fox.svg new file mode 100644 index 0000000..6cb41ba --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/metamask-fox.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/block_scout_web/assets/static/images/icons/pic-empty.svg b/apps/block_scout_web/assets/static/images/icons/pic-empty.svg new file mode 100644 index 0000000..97a4514 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/pic-empty.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/plus.svg b/apps/block_scout_web/assets/static/images/icons/plus.svg new file mode 100644 index 0000000..838e2a4 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/remove.svg b/apps/block_scout_web/assets/static/images/icons/remove.svg new file mode 100644 index 0000000..8298bf9 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/swap/1inch.svg b/apps/block_scout_web/assets/static/images/icons/swap/1inch.svg new file mode 100644 index 0000000..88cd538 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/swap/1inch.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/apps/block_scout_web/assets/static/images/icons/swap/component.png b/apps/block_scout_web/assets/static/images/icons/swap/component.png new file mode 100644 index 0000000000000000000000000000000000000000..50a1ed5240ca0e955c564ab19de6460ca40e11ac GIT binary patch literal 25775 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ℑ^ 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 U_lV`7r!oPOqH-eDLIy$q2aC|K`v3p{ literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/icons/swap/curve.svg b/apps/block_scout_web/assets/static/images/icons/swap/curve.svg new file mode 100644 index 0000000..71fbd20 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/swap/curve.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/swap/honeyswap.png b/apps/block_scout_web/assets/static/images/icons/swap/honeyswap.png new file mode 100644 index 0000000000000000000000000000000000000000..723593bc3b1d6fd27fbd2139bed71d40970292b3 GIT binary patch literal 3773 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 Vw4eWlqy2+}%uK9|%Z>iL^B+E7Y-a!f literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/icons/swap/sushi.svg b/apps/block_scout_web/assets/static/images/icons/swap/sushi.svg new file mode 100644 index 0000000..8d1ebf4 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/swap/sushi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/swap/swapr.svg b/apps/block_scout_web/assets/static/images/icons/swap/swapr.svg new file mode 100644 index 0000000..fc63da2 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/swap/swapr.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/icons/withdraw.svg b/apps/block_scout_web/assets/static/images/icons/withdraw.svg new file mode 100644 index 0000000..575e7a5 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/icons/withdraw.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/assets/static/images/kaly_footer.png b/apps/block_scout_web/assets/static/images/kaly_footer.png new file mode 100644 index 0000000000000000000000000000000000000000..a97445a2f3062c65d2eeef5bf718624c7f6c2c36 GIT binary patch literal 1618 zcmV-Y2CeytP)Px*3`s;mR9HuyS6gfpRT%#MGh2u)UAqNJ#R8fTq9COaf(nU76r;j}Px6NG5!~-!wQ3G0u!Gud`ix?B}g0x)fwv<~-ch1rOoHMhtyR(Hj z$z*nBXU_lq|My?ML&$d!0G0P&{l~GEXfaWFrVIN@>SH#%68R8-wrPjyL|Y3@+ybB0y!uKHx>y z&XSE#Y@A5D#!D~~20>I?xpu&U2%sN>PUArq-C^(ZvlAaqtZ{&5KylpDU-3`LD=FoI z>Z;ZzTltmHz-K)Ur63GY&#uxM#m0s9)v#^9HUVRzbwm_F)y-L*Y|W)3b7j68BL}>f z3El0nTH#QWX4~>c9uD7%GWHD^C0$iNcTKV-G$-sD{JsTXjCp8+8E|TMX)Q=JX@{-L zByyu`?P{MCugqzwWc{2~c|ch1$E*TvT!QP2^ARr=L80S-fFT(5GGZYG9?q0>js+gHlpqwCH0D^Q8)tR5QHGWAvoirnECu^kcc4J z(}rYE6UZ5lvWGrE@$}^yxGsRvE9m=S6|VIjgyLc}hRx|JNLQC?eBQc&CNC0x6?ATk z#z|0vG0t8K4S>Bbnzj@bOZI3l&mCHUk%3O!wfGan}H&$pPSaTXCGoX%G=W17PD$I{cFtY(nYmb+|e78~P5fMERnRFuvq| z-}p$f3w?)QK)(K6DqI)Rm5`&{%TkH?8?H3c@z&xvIkcJGWuxX~5XO7X zf$7hFr4z6J#3oExv;zf&6*lOE>%HIN{E=6oQr8XV9NP>4X+YYzM}?G1x$4bu0K((| z18~L{W9EuqaO1*{=s)=mraiTv3*K+TLuWq2rK5GW0kNBDgJ5IcJ^z*rfKLW#0i-F2 zG>C`-IRcIk04S<>0@I%ST!;JYfu}KH#&d|>w z6rXx*B_3o7yis^^+nsTQB;n)*Z(-ux^-yvYXZFt4^*{ZY?@&<0G?*iG{XG6Uumo~s z&{PB)wKRAM$e}Rd=j60WnAQWPGP}0N>LK8lAyY78*?tt3Ex_=lV>tWGa!pI&)TNmE z=%*R2aOjV{xOn7s-!MnQ&wjrQHAQjm1-7T=PD2JE3b+wDy5ZgZ_{R{Dak%>T*I>R) zCnS_F-i-;i2Y7v;uj6H0J^Q6jdA}0UlqfkW6P2$S%QO{t_m7NEC zV<%(Dtd~%>@IAj1@-joe??dnRtISB|&~r`(hf%N(=LnK4AY+bWEoTT$LDXcq<$-5` z9KC9)fnm6^{sFBCa`dwAduRkaiE?UpEK`sK6oe0Z9y$#LcnyJDJ_+Bkt4}Mm`+zgm>LhJYEOG<4U`q7_75Y`Sb}Lu z?CU`S0-`i+Hjm;r-)5i-g9@N?XK9T?s@W5#!4HkVrXOU@PF_B3!()y7`J%* QnE(I)07*qoM6N<$f{iEsy#N3J literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/kalycoin-logo.png b/apps/block_scout_web/assets/static/images/kalycoin-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8591385b55d3c66dc892f55859b67df50a6f62af GIT binary patch literal 16631 zcmeIYWmH_v)-~F=yA#}<#@*fBNkRjS(=_hx!Ciwx@IY`9+yev%5E9(oonV(d&pGEk zXWZ}4JMR7do$j%__pVxNR;^jHYVX=(MQf@nV4#ws0ssIEB}G~7mw(GY2QuQzFLXUk z7XYA&@XJrxzwdK*Y{O_R38lnP)zcSAbi9%3BWIo%`O~Cuv`&)dLM8A1C}v z*}S(ud<(DNFx~DT+`K8c@not=|0qLze|MMntP1!1${%g(&HV+9#4|ZsLklia=#5CL z*&RjiJwWQ&7f+Z|uyaB4(X};_n0rNx$S*GpD#pGJXyzIwn%TM`F2p|oTu35mYnaOav|qYQ%)Ob zU0xFYtw{Dvop(xyWc3@PxH^qJKn?D)rG_WA9hcPk$yBUgNJAa@yMfO z2o#Ro%VK>;hoiRo238fPMO_p<4ToIImAdWF;jB||NETKntWTe^h*Gw@bdv~c&jLB? zr>4?kw$8iHPFEwYKcQqmTHNU0Ed6ES>UHL-n;Ow+Wc$n|nc80~O0tdjg*G#FzO`8f zn{yV*>S&LYrWzaX@y!_Oj5o~~n;ea|0Bp+AO>CQ*eu&&0nzp%m>|k_YN%Th1W+f?* zxHnEQ?(uA>@=S0aI5co}^}aRdjltqpkFE~CQH{VmwsskDG+|HRl{JYI=>>)a&?+{h)Z2I6fYi}(iZ1fgJ)FYqBUEVRp^hAmX!7#7}F;zHrT}HSrFAr z62;2GZ`i`;q9Ja%*lSJrN%AbKn{*#IFrIqL4teT|%hlR_;&$m8Jo3~sDJt})z1A&c zm_@8inDad9^hjFblo-!{b4r^>oN;7b!qYrT%V_6ACSfP;!?dti9#m)_Mwi*-YZbv5 zmgYB7j~hPK3pga4<)2sb$Jr#fwiD_9abZlWK3-C#ySDh_Xm8wbBPj33NAAAiYwWP? z4N=HgUd51XoO)E1Wv0^t{x|e0o{XhqRElO2`;!IinGQ!vY(~)N>^G9$N|EaNXdY(~ z$rHAorZGKh*U@_^qtjZ%%l33X7I3CK_j}W<(D7FebnlA2BU(ibbwUl@6b|~zL4^53 z@JwFy8C%NAaTR1WGhlv_6oC~+AtOIezG6mY%st7)fZN`z_o~|-7`ywWgdD%?kl zzp00Oq=>7FLA9lm)QIolQuDYYH=&3xp+Xz+7ZLHEn=8?VNKUuH#FeKMu(@p~Qp*Ud zamC33+-&)=6=*(RBL^J9y!+86=E)-GJjogF+n#xgC4V{lz}5G3{^2e=nBMBAYl_gu zOy@NXLrvsjrbijwj54BpCa;rnR7v!ZtF}{W6H59+jH7{zV`Ac&!_cii47JM%9RbmW z?m34m=GQjuq&=^~Q5wen$v8>kUHY+Zx4td>(3Q;#BS!6e+Oer~P?2IN-X}*(%gvkx zx{)%oL*oM3!n_Y8wEAjZ)DwvSQ|36G--p*@dj5;SqASgpX^g+>EKoKbwntjm-Kt!l zoqc$Gx$%n@9x^xS+@lC%eYF+4`xZAAaDsvt@~x4-7|HxJkpXhe)r9>ZCUjrtMcg6% zg>I-#zmR{__nz;wr@@=wy^V6X9l@(;`f4?q7K{xe$6>_;i#EST-dT4S`f){ zk^AnFpc)O=)DDxXApG+(ZrVZEQ^xBQY*Np|(mUa~?~4f`$*^y&PwDu>jSoU0qcy6v zvGu0nsw}s5cH5f>m2ilnjgiQojs~=%B^^Kjj%rb9#vZLPN|a^tWY$_>h#tdlh@IcL z>G?y~b5=XEt`U2okC1GsS{8e2fQ9w1hA0$AQFR~_VQMfD@mbMc=Dya zugnkkZ2X9rjgj>y_L!03b@OY-rBJ#nF;o7`bo1{H9G2EiG{bO##lfc{nQ{J}HpqF$-k55oOIdb3j0NlTVOUMrf0rgRLO z@BoZjp4}S70$i3UrHE51X~u?oXKN@bK87|7J4-nTCtAoeSL-n4yRLWS>mP6{)>>g! zW6;dU+3%kix8^aubh9lmk1m2NvAxM~xGJh~I|PLXj`bWw@w;xtK6q=ksBp3B9KC%E zlc<#3!mnIO0YaHuOA0rFi7jPHUK1INkVg89a3FvYVaHh$Xt7WaY9kJ1GGIupQd)7& zreQ8H<}S0R_AGEZzqx0-*ro7@_C&y-)3tQEcCKG}# z;>_@6%_0X&M?$p{bKGY+rt6qyL3YwXBl0LESf_AQUF)bttgYDvD_&0Qgz&?w&7AK< z)fZG%rpSSN<fI{d{NCf!ShrkJtZia9dAiO1 zo1?4`dkl(T!~0axulkAuM|uXW4T99~*4b zGemof$b>Aqse~~ylRk}|qi?q`y;D)JvpC4Z>4Jaa%5g;9B@NS41Nux?S3^R(KQ_jn z+J`p63CXfS73zc{vW>QlKrV?NaKh3yen*Nv@x_fxY_Y!sBpOEMBw>nb#OOX{Wzzj@ zq*$X*3oB1M+ z9bl$SWCnAqsfxzU=!l1xMh-Kwqy#AKg9%g8TClE#H%fLcc}KL(jhL6o#?bRUtW8k^ z>|kD$bAB_jbDunUThz2?`6Dlre-Q3=I4Y4t(YhRoWn&*jLzcc6M)O&Zm&R=Sz}Qzl z{_%YJGAbzBoPVW)?QPbfaGKYGR=2v{VZm0`fx6rwg*@&I#!TfVj&wIQw z3(OlPt?MI8G}ESwxr3uB>7A!r3t|b#$OfJq{%h%gAV;joiPgG z<$Srf-ooN7Yco(ExSRr$xoJHxab>)AW2Ah!aQnA(Oz+TKssB zY-*Xm-k|7*(59VAmvjC?WtoV|-0qc(%0Pt<9IWN8>(F5W9_T98%yXFEDub$3RPH{w z#4|kA=XIq!4Qyxu6pzD_ho+z=_^(QT>S>OZP&ZT)dr=1{3YZD6=jwsZ==13Z3*pz) zqbT1mFs$>uob zK7#tS1w*FN4E?TbeJ9LbZqp>6$Gg6{wpn)LV;ir+NN=XJRG_%J>3DhB5)y$@%L2w& zt$UsL>TIRZfJarfpvF6XEegCele_8l+}GzgP3A6#X^rx}8_Yw*%fAM8TM)eu!_=uk zx6H@y6n0PqY`$pGM!qo5L{f`5u>Ri--2HikdFPAvaMuGj?# z;XL-0F$!8M+YOB_fo2=k9?-p7jrlI{?IX)hCh^b(cAE@2TTygt#wPf?n~)@;NT)^K zl&0-xWNsG<7Bcw?phrQ};(4a?XhR%v9dB$g1S7gcAxG}hCxtTDN-`^+3S3;t*GL|s zo~7$onYW|UXE@?ye{-rCog>|L8i0;+Y}+ zYgj*ySZUv3%asW09}Fd^qH9Egxfm_5CtSkiy@N3&flf+>4%5nfiLho&Pth~c#H1tI zXe~gz*7YbmvOE;#GS%=bloC^BC)`uBfHX;B+1A-3_ushZG>M6KtvK3d>war&z0o33 zsMucyyVt6M4MLB0#5)U?Y4+cEj@6_X6W4L9!UlNxB$6Mqf271npyXR6I4_74FKvB3 zR3H(~^kblh4-B?_AQ3%QO!~F|+dB$Zw?*3RA;zE~kGo1rjU@B&YV+q{h@I34vN#2$ zsG^1CPNNM=s5R_ciIYXW@>XKBs`kNXm?8Pi|eQ3o(+Up7PBo)jm%d z61xBygfc2hluLaHUX78BtK9)v(0I;6yvS zr1Su3lG^gu##3%P)lp~>Df50r(hce|1W4{Ds^>&LeH$`6PX0_Bfr&Mg5!{^-=c=O*5g#0_ zr91sUD}?}i(LyBGuCY*+OB5Gd4QY9V5VaR}zTiF&RG}Fkr+vag91WfzFIAN&f1;)< z`exBz@*d|)$R?E=%Yo$Ynx-_;(hywARVb7rgieUqfgD)PaQL&IUR9T!Pp1Mwo6smt z$_J|#X>HA>_G4wh$fsgdE#@ei7*J6`{Vp{O(Ir4CY$HQgX^`QB-d4 z4qWsUTk`%|_?=P{rVQPUX;xatMGnnW{2PaTV*vy((qrJ(M4O8^5;HNM^t z-${~b_?x!aMDG~(PA9?*-N|63A{XFDEuHyM$SdW%l!pyofQ>a#`GO5jYvRM}-o5a) z=qQ=1jQJVV*QEMUN^$5=-0JmVd6lN>j55KABt?p@e-2ro9%TU@o zrrnM*W9lQQPMK3t{Zy3KnWTHL65x_6^QY))d3{g7xikKGO1lCwzFSFqs1Z3qyp)PT ziu@wsJEeNsrFi)LciSa#WH3KJtJc$-qRI9vwlgpaHN+RX&bq^XrRf^Wt!J=nD=%l^ z8LTFGqLA<;$SVt(PZM8c5Y*cSUh4D}tD z@eDX9zKq9~FMpO%4PgkeovY7Wm%X#pp^KXATJ)3?lB`5Zh%RFC4dmtdeq%ML>y%Lq z%d);Z8iD^VsloYHkH|xoUkE4rvj&n^p)bLBScNy{Y5>#XK{HEmAt9D`w`Emu;xc0I zgFjrMl%U$1Si?{Q+M;|-*iO$gc;otolUsJ zKu1d1Fxu#@53&j2pjb&XwM!3|R~42k;0U@eK5Lcu(r6@_(BJVPGWC&Jlgl%)_<|uF zK&V4V1a2@TAJ>DThah_%RZ|NGB1$1ky!5Y@$Z6j}Cd0`P9N3EKLzf2Hb=H|jreVdP zx?u4%yidQA>Eefpr3?ie8um#`SfTHb^oNSRmXXGwGT1ZBpycY16Zc~SzJ6)ee;2MJ zxy544OI@})BV`F2x36U2$+^d5JZylDCPYi`#gNBZ)zVQgH_wVH5Q+=3b@sH;E73`l zYo5|z#vlJ&ia6%*snaIz#|g`X^DtK9csDtGK9#J?>+Ehq7uaaNQ}oVuvVJi=G79EU zh$d$r><$ppbHA#OxfxvEOJozI;hH(xD-#e#^ECX%-UOR09p`8WYz)*nuv~miz7PLs zT^o!__eM973iHT`3rwNu+o43%eh6F2L=ow&a4UWmToOQ7AYFb)ASzym+A5#b2hNlnLwo@quv8EbM3IjwVnJq}a zMW(@#YJ9WmKcaber~LUG`HSS(w4iZavE+Bn6C!G~+valVxR5GK$2>YOf~?viuADf} z9aMEkb!BG|Uao^UQRBFeboM~tDP5qGyE;)psO~#$@fOmx_RVqF!XiRI{+77ml5IW=n>4Br`;AtJ zojcr3RQ^peSFi?lB}yj!rChvSJbO^WuLSdWQYw3|sbFv8OV;)^2nA7_lWT&q>4R^M zZ^ZO;nkf_TP<;cGVW1*D=Q)B^UR>13-8(#q(4~NP(HlJ}yP8~Y_X93`nQKMu;KF4; zyGph3!wfENhoR?O5IB6;_)CM5A!jO!w?z6S9|B3cy3Oj4(5g_{a?B4WO#0_jrCAr6 zvT|ND)1X2UjEX@4d87>_!If_3Uy|fy9Ebv2amzjrW))|l`5ITeY4UEH4{vT z)PluLx@=4t8B%CeccWcW780x0k)icWA13gzZOO{;`GEBK^+!z^VxZpb3D!?gr3J7&T&C1_u zk|WyACJ|3|I%ay>&t$Nm_(ro1E!p8nF}&-0N+PdCU`~X36H<;o($&jy8xQ+h8uxsR zOE&g%BlRYuc#n7-3a37vWcm@VlgVu$dy{Zp>3~4BJ^#s$?_s{jIO=nwxFP+I(`o&S zgA*SE+KQ|s)|t)rbW+6xO}Mg&;^}ySiArw70DCYWP@PI{p2(>{ZYze8+jmAT)zQw5 ztYf=Em*fLInp}-i;HoCTPJ4Hi)0rx+T>;Gjr^@ej?Y`}*dsBOiWN(%o%GvM^fE-!7 zxReqZ=Sq0qv4!5DBQyc=QmE-ZczX3VmQ*t6y=D**@q1wmiX0_O01xuD1C9?++S&`)aYLEMPh~TdVgbHXB{lgHik{QMQm*?=GYdZFsK>* zJg&Pz)J^!wnX#saijd9JzLX8DRG!p{K3f`+8}?29ICHb0aSzA*vP3^E{7|z&I$)tn z?ue4zF;NI-7rtsl25;-@Y)1BcgQEGep8dKlih;Zq7oaZ7o8d_%Jn%*-Jd}Q*%d@ne z_$QE|EaJQASnMJ6)qZps^FrmPu1@So^-@N2czuh|{+{yn(sS0=IUmtimlF9`4cfTn zMt?f@h2ktLbQziQ>DJGU)EQ0Q<_oWlNZu5u7shi7#n#9D^G?EOs()# z1#XJVAxh*C%$c|e8naU+SoJnUu)E4y4Sw)6o8@iofb&z(ARW8i)#*M^14VoK3%XKQ znwpTz1b3N`RD1LX9fziff7R{G<m|pfrzt?|dhACvT zLsX=25N-F04_!Y=W@o06gFpBQ$1`?HkURsKKPwbq#8U}^wroSEkaE@=zKlB9%jBH6 z+}=WKG>$ksXs*VR_F4~XK7Z!Lo6p`K>5HkSHs|cHN%@4tiYYk9t`iGc{i;*eh4wTf z@ZmhRHj))nRYjN5WVA=7rgfpu;y7Vixcg`g;fU?~Cx!-$+N7HKu#(yCf;5X@k-Wkb zB}FaWygCo@VHCm8!;rBLNsOBhyI)#9d&l2Pk8k39@`D@OjPAX1V09+q4foYA@b)xT z-SrqPwHv2l#;YTL?Thm@)+~PyTV~)ya4?9wvq-5*d0uIAaSf&*jkgqjevtJw>Lgt` zy9`OS<~NRx)lYCAmHkJ+%9{FN;$D6(!r?b~U;A`bCb{yH6^JL)G4GOQ*<_xFY1XK? zPE}ic1b@!gmZ|k5w2&10U_j0sq9*XWVX?_6&~J(0hhG!Db z1viETzp9nRTs|bNX8@m%NQ=^yY(~TRC8PadU@o?Wzj(wD4xSrpjI~h8LPTV;`bb}+ zysWwvDU!lh8ByV$ah85Jzvd#YThP7sZuNMu4(t}90 zJOusPO)lO?$rL%d>X0!%=Q@dssj=gS0*oV*p`~<*{OP2;h}0zRTW2ZG@K&aFeg*X) z3He~2Gqv}+u<(=a(W-OH+Nrdn%GANaIwm5XO)4TJDf<4!WGl3_f;NW@D4LH}kr#e9 zL25rGO*%mP^cko1Fx62bR1Pc%&cV`c>au11A%~DGPtK1Z^2XD^p;pdUJ*Ii82rZj`(^<*h z61*UZ>fzWNVvy~FZrW0S-I|*o^FCef3}fB~-olxc6Qz)XsdvQIL`R^flr1=Bqi=ua z^S4MsuQ5K|yE|6|_JHHU*$a0z%tZl5vtmTLH?ecpoRdzih&1r1<|$2xd|R}QJ^FU! z+pP0F$fx!=!c4TQRk8cmue5qHF_ghno{h0bWk0Cda%<@&IHG0tjMNq0faut5!qQIj zld5lECN>ERlQsIlw5-9?dy^tDBFk}%n{TGykSthEuk8<2Y7c2mW&U;xAyhbh*fbrz z1?qfLt`Y#0F+7MF<58HQ??2lw(7LFWtCrd&5y=#=%xQnH!5;~xAfempC(|kTZdE7d zB_hbo$)3q0`B9ySCS_0A1t?+xs`lXT`(mtQ$p9nY=$2Vl6{A6=<*T!GL}!#v`cb6@ zoqJm{o|B4VKXE91?gu8^7fK=P6gd1QYRvMf+RnbF{YSH&q5D$Nb%?P&?EAL2W7iP< zUk}MK9{07dxOFb3#Q*7$+{C0PUxFvD4^ zUGpooF~Mzc$+6HdKFL&}R|E+I<5JH}uEqr&IRbqXoyBYfu6w-J(?S7=ynQ!LcD2Cm zDtJ-(p4`x&RXIg@yeNL=jWH=5gEPhq!l&2wMQWdD-m1h)=ZF^;(_;&lrCZFr*axsCwks?yIJBcFQ*SmON?4lN={-7RR%9Ou_-p z_a@Bwz(r)zDSOT&`RQ7{v4ao`E}wW(&%~7YY$CiJ?5tzFBsC}^mtxxP6+D?} ziKW*B?;29ir#ipx?bSN>oYo5*9u1~0#BtdTiRSbL@LJB#96uG*oSq!lmAGq*6dTPg8mXXq-=;#v#=n3rmyKCMb5SdRZ>+$gLq3O; zcRPIDxcfofu(Shy4PD!Tn4hp^`igs{M&e1)w_>mRhO&zf{J|A`fqQsljxl;WB(EcW zPyS$LLV}SxQuFsH^H(`Q+1I9Qh&2ybU}ic>j{h-rvA{AJNv6n2o>UiH2B~yC9f3@X zKVd3E`4F#uk>k|p6e>0ecCWDJp~21cj1KE8PxzU-Mx+7sRQ)E5M_a1gFS(+!pDp1( z&LqKnUtYQ$m?p_;M}JEt*Y3Z4Nu+Z#FnuNAEqu{8Gcvx!782)c_-aKh)9$lTiJq=M z1JJmF5Jvvtcz;Y{p*^t6Xzl@~_T&wVW9@SdyYAJaCEEDM&eIV7WZeTef$5{qUB{oL z?)N8B6BR2QVwxkDA~Gcr!!u=CeIpuM9!oBa`|^H_8RC^~7Pnj-pGD0FmarpWuxq@- z&BJQ*Z}BXDHLe=yqkRK8X}!mr*$!a0%_3MiF^-;JF2stn^+j6WeqDUA|5T}hfANs~ zOJL4=L?S-uhZPJWR^AeGtM3;{Nk_Er$8|ZT*Gn7W_?e!e_|vPi+U5_G0X?F$#Gde! z?JNEQ=*EsS^Qq1C2OHR)-=N9l!Z~XGvfF)M2vQq`r)o_KzU7dL?Lnlwz!X;?LESL9 zy*#HSovi6{40c3ub{7@NX4<(z>LGXPT_%Z0j#*>p9%2H2!Wfr66m!Dm)NgA9#C`oh z=j4qp$-NVaafyz#J7klQ3C28AscacuxVL-D2_kv|km)XI1-b(96Wt~?`A9^aFg3T5 zn?aQF4d<51>iaV`|4D}-+3&W$fG%0fCqoV04l$fEBodSpz4T!3hzNKB$ssohgh;ac zH4<~#KyC0mf47X|QOPU${1Db0vVy2YrXZ4nrA96M$nC4PmsPww#k30>f>=!Z?sDLf zDsus`mE$d8M43KM(-TjnIL{UiT(RweADXT>gYv>6e13f?exri)=bjnMo?$wF3hZKI ze^|yfY}n}RirF7FU5bnEdT03~$JBbzyaKMW--O#!MCW|$e=LG>)|zKE%^$4jxj1!s zob`MUIg#+TsoLXD=xq8Obh3iF=oDr5gs4ruQ7j&LCvnJ!^jjW(eF^}83AdGz(NvO= z`DaGLOCCb5f0Bq|pC*39bD6TgF4-Z~dSaol5e|WKDhpwcv@L7nzM0E35wESHwka9H zTu5lbKxJr5LoH@)2-PjX+08}UEeUr*4VB$o6xv4lEcQInu>&jJF{ayK0jtS3syJ9= z&hVK9@y%2W($(?A;XxTqdKR=m=WEC%ZClg}W|EixSC;gju zzF_~mWNANjQL9OWXv#K@&kuubt99KxGV;Pg8e}wtXpU?xT&$!lXS~jaT6|xLxH(vJ zTLi1NSnQWmx`3I}`r4)+_+p9Eu@zuxEOoV{NeF2g?MpHhzkAR0No?NYZ6=TC!O{-F zvS?BZ1)NIUika|8@a-`m6ie>JI0nW7f?pSCH5{E-EI@JrPu8o-_IIrAX4ff~#wuu( zFpmq}1MVs*H$kK1W~WLS=p#uFjbVNiUJ}JlUt!@|Bqy>(TDFDMDT8>&?d}i-1>xn< zQag^%j%%;m9v7x_SFl8uT#^=Y1L$5-M{aCiQbY{ZRD~^^963OiPUc_^FGuK03JCxp zD&YkMS=fW!sLa9Ewh%Fzlh0i=RJN94GzNTXKsBfg*v3}T#|5n8qpoY=V{aj3Nh2YS zD(WTt0^kUC15tT7IzU{7y~Jq#!WDiw|5MCKL-m)5o4pu~p_(R@jFStPikE|z1IRAt zW$VFBBaTWX>SAdntSu}54~Um5F&Y~;H>fZtr>CbUhbIq*lZ!PcmynPUCy<+yo16W` zg5A{{;s)|!hq%)If%qFk7VK)_VheS%b%Id+!33E*xx0zc(7e=B{j-0LP&KuG!9!gC z!NLn4oL(R(Cl?2h)6tRh-#uL25qA5AJKscHVp;|~SawvN!hyk5xu4@);&tN$VE zKl=8k=C5%6y(2H~|HAzb>wm`nm+_00nwqezlZE@A?kUNN(fny&*wV?u)>8PdQ;?83 zAD;jhH@lEI5X{cY&Bek*pdI9IKeZc`+ z@LO7f`9bV_Jc2^(ykLGVc5{9n0d{jB(46-L93;pM`Zov-7uy$Af*k(6SAU=^U!eGS z1%)hM9N2+8yyon@AWK1ZA=3&_FqH}ao>5q=5hg<8-brM>|C*VAhM)Ri&RDV4G5;&FU-@~G4>-u8h{YUfvP`wV=`R}X0CxL_QUsY68e+gF@Wbt<+ zt{@Mv^K4 zgDnMk`31Qx1VJF~|3r6nvU2kTxqzjtUm|^p=0!q(MMK5(4>Fnm)85kt{D&#v%lziz zVh0N9a&ZfD@d*QkXgL4xhi3ug<>%(%ehEs@@=kqMZM);r`p;-`2PnTK+D3Svy~rV$Of9#sA>z zk3#-0e*Ph||BEfWK>xRq{}sRgrR%?R{jV7KUm5>bUH_%)f5pK6%J{$P`u~kC)c@Jd zfgvvsc%CnNH7JeR+{-=>(OgAA7V!M%ThQ?#<)s7#s%Yd20HERhIbZtaBI+Y_008VaO0rVAUh_xqZK1kzX+zH+h}h#i+USGNk_9~oX;G+i%_cH+ zP17nf&sCF^F}Ui-A&oGDtCe(Vmbu`swG}}`Vf^Tb=13_sV;M>;_p8hk{VB0hxV_G% z@f(6%xVSb%&Bh(??FI4&8J{;tq{Y`=H!^8I#_x3%1f1|=Y~z9y=cXy`I~ zoTZ9kokkG!EjSH;?T{=W`bNR@MPhoZP&E=}6+Vb;tCT88?drZv9UMG%G4<#+^=O8H zUS3!|FXGLDX8C010DzhTWYD&z#zK>wC4KUkmjS2FK`yEs7_dK0Tddbh*%I4A&K&Bq zz)^*w$wHdkh>9mY>mLDvW#QDU;s=EeMs2?9mV}D!W{v572-V$CZF70-Fm_hYxco6# zr8B`fV{UsAO9sSOB&yim>GN`!U@bTy&+^S+V)XM$6_xZ@>B}$~K=aHo*)xbR>NT-r zDHcFbK4`AB(K1Z>l2^7nJv+Ug(kj?c^!L0nCH$!<6DF;`s*9%a4`am#YIrkywb7q3 zMpRb7c>-c8PDM>g7in2!Cd5+j%K*nVSXAePwHe3LbYh z>j`j(;v16zcbT+Y_~m-3%nM)Atb5)5)Oqz3PWJXPttfUU$@0tLBGa)7lI*@`Z|Bqx zMd7OJXm{R3nj=e``}|b^RuDIVM2SkV!0$t;Ua3A?NHGE)Y@NScU$0~#Lb%lFSIJCi zDpqW=LHsv@OpO2_KQmC2l-SBS^pGgKrk%}Se+(Vii<*jO!2TCpFch8&%Lt}Wqk6b7 zdefVbd5o}f1yu9kk( zU2z6agR?%f5?;EaRMTT3czF%PCvA{m(ZPfc)4FSw2p}fQKo^1dJ)owkU3Qs!?>tH- z-XUOf5Y!MO^s*^`lWI6Nf<6OGpI+Wg+zPb946V12>l?0*g-Z@9bkQLtCat21Ucl|x z1&(U-9(=8V(dzv6x?a40w;FDBm_Qcp&LtR3tf4~QY8zs2@uhaxaTIhLkYs@uHM(|>W-y(0*9QifdV$&J z-)Ja7tXNO8Lz+f6;KT*A#L*vtykxzrCkesDeP`$105|4O9B4wT506i5S>lIBob7=^GK3k*Hu}B`aa8<~?ffgmjc)or)T!`!i%=GB$1P4yZ+ie0 zpK5F@ZZ@u1frxDQ?%Ss$fID^%p6ebvSmn9by#&IR=`-jXzM$CLQ? z5hm19G5v|*Jr6DVcG-T}6fGL!rk}zu4{YJ}BlH^gUDGtpJJ^Ak>JT?G+Y1W3VmpxuyVU>%FtH3`047pjJape>Q7v7 z4X>*8N(wjVs`hKkC1#&50!J4;1^I_g#x^Y%Use`EB$03g23C}C zbtHH%fL+3#9cFN@J?j#T8DT_$6LU0LlB}kD=QDrby+80W&>TA&y3yxerM}v}&lxft z@XFKYmfZCANmQTKs6oLAsu8piPEe1Q+sdCfGRtBC!r}Z;SRM zGSR@LWAQ^|85hs_C>?gmL)&6ndFzD&Q`;4VLLqd&qk&lP5iAR94Idru{7Y^wN!UBf z{}QmP>6Rf*wPS1nM24y?MUJJ?*RoDL3$$&*-uJ9^p?39cJrY;`ctQ>rm|4II z8YA|>N_j1)_Nd0%!(F8J@h zb*(UxuVm9f710cMW1<17SRLtZSWagWtG|7PlT1D>1~3pj0Epf$C5*Im#c$?gjk6>- z$%jO9ESfdmP_WrT;Y)Um2++S<^AncY5u=+hPQ(qfzJ_2+z#=o9(Q;VU2J+NCkKL@j z)`q;2-+jbEf0&yCicQ*a$`8K*2A3o-Rt5@mJHCU|!0maLwQ5!|D|xd+r(KDqsMVic3r;Kxb(pV&o#AW_yyem9Q50{c z<>COCd!R%|Aw?FQfLTV28jH&~O2#7jHHAFM+4rB<&NqKWID-qY;5eNmEr086NG%BC zaj^!vkkv1duOm*D?HXg2+%t=zE5EJLem@H9A_xqBGU$xii>aaV()?6JPEf@kV#+Uq zpUL%fZHxYRo!`CYIlT}mYkLnX5G3>NTS{=QnC4GQ2ubQE#mzSni^;E&A0N^{m-zwc zJQ)dax?$(DoJ9}Ufw7Nv){%0&A#0sF#CyMxPYHI!e47{32s#qSv@zfNSJo-P$p%lc zOq6NA=PN#rdyV9&-uZ3fZFl&ygRIRtBh`e9?$Z);>f)y#*ZxI?fQW!VY&<71#5J2X z1d@?4E27pO8$IG~SF1oNJmQ*qbhBFF80(K0d+S4EUJ{7vPO`k03;?3aWAR}6wCljN z4Mk+Q=f?@;_*_-D>)=gOL)YYQv{SDte0d{XtO9!;IL*@Eo@u>Dy9l98ITNnkKWVN) znsdl_E)x8y#ph8IaHx!g{23s77}zGZmGl|ayLA3GNyjkw`~C@C9+u~c0IOCy4M6<7v5}cY3@wRRHxUmNeb%@BYL&CoeA#P03h-cQ*MLO6JXHN+ z4oM~~1l+ov$z0?;>u;v}D=EF^+;H(k5+rrrZTAQf75K1wM#3Br{3{#>AVTnd)MUSk zLQe){e`A4zbd)RZa-U@25h@CfgU89p;`UbH%ZoknFUmdrYaFEn`Z+#R~Zmh=WUmspnf7HQm`wqr%k z%cmD7wFqk^?xAvf#vEax5nwMI4X~+L;F$uqx#9r!DYABzB|%H$KSNk&KUbC7qpJvs zJv4K-n_XW_tsW2`i|i>D%W~WvyjnXC&@E;CC96TFCIhVAU?^B$85;440n8Nb5;g|` zy1y>5Fnw3!95h<2*xxrDll@jr5a|zS3DJ#`Xj~1uY=^1xCU_|BVX!)%;H)zJ@Vv)gm*0+{l)AF$}7l7Ipa zYjjlNwf=7LA4Z7%6_HMR?+1_f&mA7jUSq@t&h@`au(0~Ff`gav5&r4-2?}W}Tet<8 zMY2WQcB#=)7!2?1F`0-%Q+4z2Pj)7Qn5@UFY@Q}Qlw+rQ0b{4W62$vSWd+e0#k8%9 zOULDouz+xZ>cQlkPOD=(L*T3$!QmDcL$npKq^o00$5c$S2$8+HfQ>$cN&RYOr`CKk zt3RQ8^`q=XUy2)Us~dXCN$38gJI;f9&g$1LnXXmUqIKdvF6U`u@-P@kt!=$T>WuS^ z&0B?EeU_j)zP5LoP1gvHuVmflRyXBhbC-yp6QSL~zpV`nzA-{KI-U_V0-}puG+yrx ztg_zF&v%d#`y2p?9Wb_kCF~)Z@J#A%Kl8uoh8UJK}jlU|7Vj z1cS)z31pLvD!vjTwsMul%wP(9hq^tuwSU+XUjg9!2wu%jP|BZL6)~HuG1R_886rR^mZJ4F)r@Dr^Zz(&&yMH1Kfv-QB zVmb9#m+G^8eUP0ls+#776R42{NM?OpN`$_Z_ZQV>Vgcr&GaxF!;FI)~&drmxs4WVK z>n>lZ0mN+)OOvEFgr_AG#Ev9qG*h=2ll?!Im7x{XUDIF?s~=Y(n+nIDNp!(NXQsL) zo^du)mopB_(AOhAnNH`j)g%eUOzbLG#W5p5cl=E+SYBO8REWS9QOm-1L?w6`?GglY zdwMgN#F%|*%{ekQ&Qn4d*oL(lQP!}P3LwmVQ-zlKD}=_)z$K=xL%DjI{%IR$@C%Gq z!7xz|@27W%G?^Dza#rl;*ygTkZHL_T44Ug`%xLTu@r2q|Sv{tGXv|dwbm^o>il3W< zW{;S)jqP&Be*|@Tq?2lda3{o^sjzPwzm1iEDb;aguKV&D!6<4!Jz~|WrAkqp%xQO> zX_PzmRg8JY`+@o`Ernk)1wWs3T;|eVa_|gS@JArtbdj)RdMB@K{B?k#H3khiAwm+r z=SL~%dM^~``6uoMlYeVP3M*27gXCBvf;;)sH?bvu#nH1%B{_8L(_M^1Iik=Vfu9yUKft{!#W-e98i7FbYm09RV8atPuoYi6p=qCq1Y%=ha6`34 z?U~cx<(=xF#zv=PQCSZHV1n|jV*~(#;%Ebx$S)hp O03|tf*;;9{kpBn0XK@$+ literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/kovan_logo.svg b/apps/block_scout_web/assets/static/images/kovan_logo.svg new file mode 100644 index 0000000..d57a709 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/kovan_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/last_block.svg b/apps/block_scout_web/assets/static/images/last_block.svg new file mode 100644 index 0000000..28c3f04 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/last_block.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/logo.svg b/apps/block_scout_web/assets/static/images/logo.svg new file mode 100644 index 0000000..a8df6bf --- /dev/null +++ b/apps/block_scout_web/assets/static/images/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/lukso_dashboard_image.png b/apps/block_scout_web/assets/static/images/lukso_dashboard_image.png new file mode 100755 index 0000000000000000000000000000000000000000..94874619da7e0dee1a4358dcfd94217483257811 GIT binary patch literal 22431 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/lukso_logo.png b/apps/block_scout_web/assets/static/images/lukso_logo.png new file mode 100755 index 0000000000000000000000000000000000000000..266804f19db039e45bf8706a81ee9024c22dc5ce GIT binary patch literal 2326 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 \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/callisto-mainnet.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/callisto-mainnet.svg new file mode 100644 index 0000000..bc6220a --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/callisto-mainnet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/circle-xusdt.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/circle-xusdt.svg new file mode 100644 index 0000000..ba01ae4 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/circle-xusdt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/ethereum-classic.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/ethereum-classic.svg new file mode 100644 index 0000000..852943c --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/ethereum-classic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/ethereum-mainnet.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/ethereum-mainnet.svg new file mode 100644 index 0000000..7de7571 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/ethereum-mainnet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/goerli-testnet.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/goerli-testnet.svg new file mode 100644 index 0000000..e2293d0 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/goerli-testnet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/kovan-testnet.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/kovan-testnet.svg new file mode 100644 index 0000000..2baa304 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/kovan-testnet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/lukso-l14-testnet.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/lukso-l14-testnet.svg new file mode 100644 index 0000000..f2b3017 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/lukso-l14-testnet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/poa-core.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/poa-core.svg new file mode 100644 index 0000000..826aecc --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/poa-core.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/poa-sokol.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/poa-sokol.svg new file mode 100644 index 0000000..70b0d1e --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/poa-sokol.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/rinkeby-testnet.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/rinkeby-testnet.svg new file mode 100644 index 0000000..e13556c --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/rinkeby-testnet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/ropsten-testnet.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/ropsten-testnet.svg new file mode 100644 index 0000000..5f44f21 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/ropsten-testnet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/rsk-mainnet.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/rsk-mainnet.svg new file mode 100644 index 0000000..b22027a --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/rsk-mainnet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/network-selector-icons/xdai-chain.svg b/apps/block_scout_web/assets/static/images/network-selector-icons/xdai-chain.svg new file mode 100644 index 0000000..3667ade --- /dev/null +++ b/apps/block_scout_web/assets/static/images/network-selector-icons/xdai-chain.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/pirl_logo.svg b/apps/block_scout_web/assets/static/images/pirl_logo.svg new file mode 100644 index 0000000..d769dc4 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/pirl_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/poa_logo.svg b/apps/block_scout_web/assets/static/images/poa_logo.svg new file mode 100644 index 0000000..3210331 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/poa_logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/assets/static/images/purple-block.svg b/apps/block_scout_web/assets/static/images/purple-block.svg new file mode 100644 index 0000000..c198cbe --- /dev/null +++ b/apps/block_scout_web/assets/static/images/purple-block.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/rinkeby_logo.svg b/apps/block_scout_web/assets/static/images/rinkeby_logo.svg new file mode 100644 index 0000000..4659ef9 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/rinkeby_logo.svg @@ -0,0 +1 @@ + diff --git a/apps/block_scout_web/assets/static/images/ropsten_logo.svg b/apps/block_scout_web/assets/static/images/ropsten_logo.svg new file mode 100644 index 0000000..3f1e7e5 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/ropsten_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/rsk_logo.svg b/apps/block_scout_web/assets/static/images/rsk_logo.svg new file mode 100644 index 0000000..3f469de --- /dev/null +++ b/apps/block_scout_web/assets/static/images/rsk_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/smart_contract.svg b/apps/block_scout_web/assets/static/images/smart_contract.svg new file mode 100644 index 0000000..cd9b5c1 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/smart_contract.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/social_logo.svg b/apps/block_scout_web/assets/static/images/social_logo.svg new file mode 100644 index 0000000..aab45fa --- /dev/null +++ b/apps/block_scout_web/assets/static/images/social_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/sokol_logo.svg b/apps/block_scout_web/assets/static/images/sokol_logo.svg new file mode 100644 index 0000000..3017562 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/sokol_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/spinner.svg b/apps/block_scout_web/assets/static/images/spinner.svg new file mode 100644 index 0000000..dd29cb7 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/spinner.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/tobalaba_logo.svg b/apps/block_scout_web/assets/static/images/tobalaba_logo.svg new file mode 100644 index 0000000..d26774f --- /dev/null +++ b/apps/block_scout_web/assets/static/images/tobalaba_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/token.svg b/apps/block_scout_web/assets/static/images/token.svg new file mode 100644 index 0000000..8390f97 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/token.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/tomochain_logo.svg b/apps/block_scout_web/assets/static/images/tomochain_logo.svg new file mode 100644 index 0000000..71cfd2b --- /dev/null +++ b/apps/block_scout_web/assets/static/images/tomochain_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/transaction.svg b/apps/block_scout_web/assets/static/images/transaction.svg new file mode 100644 index 0000000..824b4cd --- /dev/null +++ b/apps/block_scout_web/assets/static/images/transaction.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/assets/static/images/transactions.svg b/apps/block_scout_web/assets/static/images/transactions.svg new file mode 100644 index 0000000..7fad652 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/transactions.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/assets/static/images/wanchain_logo.png b/apps/block_scout_web/assets/static/images/wanchain_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..788cccc8b3a65003c2f1617bd315c03a83e7f60c GIT binary patch literal 2416 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/xdai_alternative.svg b/apps/block_scout_web/assets/static/images/xdai_alternative.svg new file mode 100644 index 0000000..ac9749d --- /dev/null +++ b/apps/block_scout_web/assets/static/images/xdai_alternative.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/xdai_logo.svg b/apps/block_scout_web/assets/static/images/xdai_logo.svg new file mode 100644 index 0000000..3026b95 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/xdai_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/xusdt-logo-footer.svg b/apps/block_scout_web/assets/static/images/xusdt-logo-footer.svg new file mode 100644 index 0000000..67bae45 --- /dev/null +++ b/apps/block_scout_web/assets/static/images/xusdt-logo-footer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/images/xusdt-logo-top.svg b/apps/block_scout_web/assets/static/images/xusdt-logo-top.svg new file mode 100644 index 0000000..c52ec4e --- /dev/null +++ b/apps/block_scout_web/assets/static/images/xusdt-logo-top.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/assets/static/manifest.webmanifest b/apps/block_scout_web/assets/static/manifest.webmanifest new file mode 100644 index 0000000..b20abb7 --- /dev/null +++ b/apps/block_scout_web/assets/static/manifest.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/apps/block_scout_web/assets/static/mstile-150x150.png b/apps/block_scout_web/assets/static/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..07bb1b3afd66677e924bff4ae2f2faf0fe5ec83b GIT binary patch literal 7418 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 ITleM}yKa1`vTDzvbfE;rX) z2J~6IeOZGb5~4+4CcO?^c4Bh)QiE=}5Vt4(L$hz|k9t=%Gqc5j&}CVmc0i#U8`QJ! z!0`8>86XcY0JdS5yD4?qVYKDty8`tw&3}oOiAttq*_d1HDI3df(PYPJBUOs5 z?|r0EjjoB&+pI0C*M-S1YP-V-v+_IqNc`2boNW#-jC3`nC_Q*n^T^rtrV z%d&9+5k0kwm_^F1M$v56qoAW-_Vt(mnr@VxuRpQ-G8J%0k?2nX&6cV-%{J@_U(l6Z z|LPui>*5VxXpnW5EO^e+rt;C^n*t$f@>Ydmm1S_>l7Aiggg0ogy=ED?x(x`y^3zQX z?^j$^GSW_aPAB$$p||h$kI0|2h%|c;#AbQa*C^;Q`xPOhU=e-!K^vZ zRse2e0V+M=N(r0G=Kk8ss|9f-aeR4V3kkU~{s3w?p$3 zTY+wT_j83VT4MlaWb~}*OHrOk;?%bvO4vgNdCcQ06g(ZuAjK95GiO5+K&ECu*99J< z!WZ$;44|+~|KDn~DG1jSO!%iN%r_xG*&W6NFQ&d*DsCJDf#v7qSc9Ea{tMQn0W87 zdjUknF8rL$@IFoa399`(>RrC>JW%e6(sjJ3MD;uGvHWzE4AuLy`7`foB`NP zvB+!QlD69PJoZzD3*Go;hrmGAv%#K@w_SS?zaK3du1xLn{P>lBjKbY1s3*u6$nEc(P5d&_Vxqa)Wv}= z31U7b7DC!>d0sX{Zcn_kyPW+7{>`Gr(RRi2bS21iLH@E8iEc7DX(`fCx#HLFNt0ux zz8^P!i!gZh0r6VVREK-VaA#|B?)AfXvUjgc2-fU|S}M*D3yi78LOeI~P1Cp|)Z-eV z_Ybuk50k1uMIG8(BSfOH|AQ4#0&ZSOr3k%5=4d3OcKvrt}sWRkoIjEi0;Xi(_=vGV9Z={6efh{QAsS{<0{vN~tK zium=KqMy=#Qr16L9!phFUUjed=-2IL2U-Oda6i|?UaAZ?J2jXP zR@5AL86Wn=Gb214wTp>AyWF;)_(-=P6kxVm)#VhJ=NiZS;Kt~dhn~H8ib2f3W?GSu zv0uek0OMJM3Bqm**_#C;Hk#YBIZ3j8;Ed;zW?u@zsmmz|FZOd2>%)5e+(!<@xNO6Z;C_M|4sN0(*FnSTTK5O?0&4g XG|Dv;S8ws314}(EqkC{o=Y; + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/apps/block_scout_web/assets/webpack.config.js b/apps/block_scout_web/assets/webpack.config.js new file mode 100644 index 0000000..0a1ec27 --- /dev/null +++ b/apps/block_scout_web/assets/webpack.config.js @@ -0,0 +1,173 @@ +const path = require('path') +const TerserJSPlugin = require('terser-webpack-plugin') +const MiniCssExtractPlugin = require('mini-css-extract-plugin') +const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const { ContextReplacementPlugin } = require('webpack') +const glob = require('glob') +const webpack = require('webpack') + +function transpileViewScript(file) { + return { + entry: file, + output: { + filename: file.replace('./js/view_specific/', ''), + path: path.resolve(__dirname, '../priv/static/js') + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader' + } + } + ] + } + } +}; + +const jsOptimizationParams = { + parallel: true +} + +const appJs = + { + entry: { + 'app': './js/app.js', + 'chart-loader': './js/chart-loader.js', + 'balance-chart-loader': './js/balance-chart-loader.js', + 'chain': './js/pages/chain.js', + 'blocks': './js/pages/blocks.js', + 'address': './js/pages/address.js', + 'address-transactions': './js/pages/address/transactions.js', + 'address-token-transfers': './js/pages/address/token_transfers.js', + 'address-coin-balances': './js/pages/address/coin_balances.js', + 'address-internal-transactions': './js/pages/address/internal_transactions.js', + 'address-logs': './js/pages/address/logs.js', + 'address-validations': './js/pages/address/validations.js', + 'validated-transactions': './js/pages/transactions.js', + 'verified-contracts': './js/pages/verified_contracts.js', + 'pending-transactions': './js/pages/pending_transactions.js', + 'transaction': './js/pages/transaction.js', + 'verification-form': './js/pages/verification_form.js', + 'token-counters': './js/pages/token_counters.js', + 'token-transfers': './js/pages/token/token_transfers.js', + 'admin-tasks': './js/pages/admin/tasks.js', + 'token-contract': './js/pages/token_contract.js', + 'smart-contract-helpers': './js/lib/smart_contract/index.js', + 'token-transfers-toggle': './js/lib/token_transfers_toggle.js', + 'try-api': './js/lib/try_api.js', + 'try-eth-api': './js/lib/try_eth_api.js', + 'async-listing-load': './js/lib/async_listing_load', + 'non-critical': './css/non-critical.scss', + 'main-page': './css/main-page.scss', + 'tokens': './js/pages/token/search.js', + 'text-ad': './js/lib/text_ad.js', + 'banner': './js/lib/banner.js', + 'autocomplete': './js/lib/autocomplete.js', + 'search-results': './js/pages/search-results/search.js', + 'token-overview': './js/pages/token/overview.js', + 'export-csv': './css/export-csv.scss', + 'csv-download': './js/lib/csv_download.js', + 'dropzone': './js/lib/dropzone.js', + 'delete-item-handler': './js/pages/account/delete_item_handler.js', + 'public-tags-request-form': './js/lib/public_tags_request_form.js' + }, + output: { + filename: '[name].js', + path: path.resolve(__dirname, '../priv/static/js') + }, + optimization: { + minimizer: [new TerserJSPlugin(jsOptimizationParams), new CssMinimizerPlugin()], + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader' + } + }, + { + test: /\.scss$/, + use: [ + { + loader: MiniCssExtractPlugin.loader, + options: { + esModule: false, + }, + }, { + loader: 'css-loader' + }, { + loader: 'postcss-loader' + }, { + loader: 'sass-loader', + options: { + sassOptions: { + precision: 8, + includePaths: [ + 'node_modules/bootstrap/scss', + 'node_modules/@fortawesome/fontawesome-free/scss' + ] + } + } + } + ] + }, { + test: /\.(svg|ttf|eot|woff|woff2)$/, + use: { + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: '../fonts/', + publicPath: '../fonts/' + } + } + }, { + test: /\.(png)$/, + use: { + loader: 'file-loader' + } + } + ] + }, + resolve: { + fallback: { + "os": require.resolve("os-browserify/browser"), + "https": require.resolve("https-browserify"), + "http": require.resolve("stream-http"), + "crypto": require.resolve("crypto-browserify"), + "util": require.resolve("util/"), + "stream": require.resolve("stream-browserify"), + "assert": require.resolve("assert/"), + } + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: '../css/[name].css' + }), + new CopyWebpackPlugin( + { + patterns: [ + { from: 'static/', to: '../' } + ] + } + ), + new ContextReplacementPlugin(/moment[\/\\]locale$/, /en/), + new webpack.DefinePlugin({ + 'process.env.SOCKET_ROOT': JSON.stringify(process.env.SOCKET_ROOT), + 'process.env.NETWORK_PATH': JSON.stringify(process.env.NETWORK_PATH) + }), + new webpack.ProvidePlugin({ + process: 'process/browser', + Buffer: ['buffer', 'Buffer'], + }), + ] + } + +const viewScripts = glob.sync('./js/view_specific/**/*.js').map(transpileViewScript) + +module.exports = viewScripts.concat(appJs) diff --git a/apps/block_scout_web/config/config.exs b/apps/block_scout_web/config/config.exs new file mode 100644 index 0000000..c3dd7b0 --- /dev/null +++ b/apps/block_scout_web/config/config.exs @@ -0,0 +1,115 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Config module. +# +# This configuration file is loaded before any dependency and +# is restricted to this project. +import Config + +network_path = + "NETWORK_PATH" + |> System.get_env("/") + |> (&(if !String.ends_with?(&1, "/") do + &1 <> "/" + else + &1 + end)).() + +api_path = + "API_PATH" + |> System.get_env("/") + |> (&(if !String.ends_with?(&1, "/") do + &1 <> "/" + else + &1 + end)).() + +# General application configuration +config :block_scout_web, + namespace: BlockScoutWeb, + ecto_repos: [Explorer.Repo, Explorer.Repo.Account] + +config :block_scout_web, + admin_panel_enabled: System.get_env("ADMIN_PANEL_ENABLED", "") == "true" + +config :block_scout_web, BlockScoutWeb.Counters.BlocksIndexedCounter, enabled: true + +config :block_scout_web, BlockScoutWeb.Counters.InternalTransactionsIndexedCounter, enabled: true + +# Configures the endpoint +config :block_scout_web, BlockScoutWeb.Endpoint, + url: [ + path: network_path, + api_path: api_path + ], + render_errors: [view: BlockScoutWeb.ErrorView, accepts: ~w(html json)], + pubsub_server: BlockScoutWeb.PubSub + +config :block_scout_web, BlockScoutWeb.Tracer, + service: :block_scout_web, + adapter: SpandexDatadog.Adapter, + trace_key: :blockscout + +# Configures gettext +config :block_scout_web, BlockScoutWeb.Gettext, locales: ~w(en), default_locale: "en" + +config :block_scout_web, BlockScoutWeb.SocialMedia, + twitter: "PoaNetwork", + telegram: "poa_network", + facebook: "PoaNetwork", + instagram: "PoaNetwork" + +config :block_scout_web, BlockScoutWeb.Chain.TransactionHistoryChartController, + # days + history_size: 30 + +config :ex_cldr, + default_locale: "en", + default_backend: BlockScoutWeb.Cldr + +config :logger, :block_scout_web, + # keep synced with `config/config.exs` + format: "$dateT$time $metadata[$level] $message\n", + metadata: + ~w(application fetcher request_id first_block_number last_block_number missing_block_range_count missing_block_count + block_number step count error_count shrunk import_id transaction_id)a, + metadata_filter: [application: :block_scout_web] + +config :prometheus, BlockScoutWeb.Prometheus.Instrumenter, + # override default for Phoenix 1.4 compatibility + # * `:transport_name` to `:transport` + # * remove `:vsn` + channel_join_labels: [:channel, :topic, :transport], + # override default for Phoenix 1.4 compatibility + # * `:transport_name` to `:transport` + # * remove `:vsn` + channel_receive_labels: [:channel, :topic, :transport, :event] + +config :spandex_phoenix, tracer: BlockScoutWeb.Tracer + +config :wobserver, + # return only the local node + discovery: :none, + mode: :plug + +config :block_scout_web, BlockScoutWeb.ApiRouter, + writing_enabled: System.get_env("DISABLE_WRITE_API") != "true", + reading_enabled: System.get_env("DISABLE_READ_API") != "true", + wobserver_enabled: System.get_env("WOBSERVER_ENABLED") == "true" + +config :block_scout_web, BlockScoutWeb.WebRouter, enabled: System.get_env("DISABLE_WEBAPP") != "true" + +# Configures Ueberauth local settings +config :ueberauth, Ueberauth, + providers: [ + auth0: { + Ueberauth.Strategy.Auth0, + [callback_path: "/auth/auth0/callback"] + } + ] + +config :hammer, + backend: {Hammer.Backend.ETS, [expiry_ms: 60_000 * 60 * 4, cleanup_interval_ms: 60_000 * 10]} + +# Import environment specific config. This must remain at the bottom +# of this file so it overrides the configuration defined above. +import_config "#{config_env()}.exs" diff --git a/apps/block_scout_web/config/dev.exs b/apps/block_scout_web/config/dev.exs new file mode 100644 index 0000000..d26e1b2 --- /dev/null +++ b/apps/block_scout_web/config/dev.exs @@ -0,0 +1,66 @@ +import Config + +# For development, we disable any cache and enable +# debugging and code reloading. +# +# The watchers configuration can be used to run external +# watchers to your application. For example, we use it +# with webpack to recompile .js and .css sources. + +config :block_scout_web, BlockScoutWeb.Endpoint, + debug_errors: true, + code_reloader: true, + check_origin: false, + watchers: [ + node: [ + "node_modules/webpack/bin/webpack.js", + "--mode", + "development", + "--watch", + cd: Path.expand("../assets", __DIR__) + ] + ] + +# ## SSL Support +# +# In order to use HTTPS in development, a self-signed +# certificate can be generated by running the following +# command from your terminal: +# +# openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout priv/server.key -out priv/server.pem +# +# The `http:` config above can be replaced with: +# +# https: [port: 4000, keyfile: "priv/server.key", certfile: "priv/server.pem"], +# +# If desired, both `http:` and `https:` keys can be +# configured to run both http and https servers on +# different ports. + +# Watch static and templates for browser reloading. +config :block_scout_web, BlockScoutWeb.Endpoint, + live_reload: [ + patterns: [ + ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$}, + ~r{priv/gettext/.*(po)$}, + ~r{lib/block_scout_web/views/.*(ex)$}, + ~r{lib/block_scout_web/templates/.*(eex)$} + ] + ] + +config :block_scout_web, BlockScoutWeb.Tracer, env: "dev", disabled?: true + +config :logger, :block_scout_web, + level: :debug, + path: Path.absname("logs/dev/block_scout_web.log") + +config :logger, :api, + level: :debug, + path: Path.absname("logs/dev/api.log"), + metadata_filter: [fetcher: :api] + +# Set a higher stacktrace during development. Avoid configuring such +# in production as building large stacktraces may be expensive. +config :phoenix, :stacktrace_depth, 20 + +config :block_scout_web, :captcha_helper, BlockScoutWeb.CaptchaHelper diff --git a/apps/block_scout_web/config/prod.exs b/apps/block_scout_web/config/prod.exs new file mode 100644 index 0000000..366b886 --- /dev/null +++ b/apps/block_scout_web/config/prod.exs @@ -0,0 +1,33 @@ +import Config + +# For production, we often load configuration from external +# sources, such as your system environment. For this reason, +# you won't find the :http configuration below, but set inside +# BlockScoutWeb.Endpoint.init/2 when load_from_system_env is +# true. Any dynamic configuration should be done there. +# +# Don't forget to configure the url host to something meaningful, +# Phoenix uses this information when generating URLs. +# +# Finally, we also include the path to a cache manifest +# containing the digested version of static files. This +# manifest is generated by the mix phx.digest task +# which you typically run after static files are built. +config :block_scout_web, BlockScoutWeb.Endpoint, + cache_static_manifest: "priv/static/cache_manifest.json", + force_ssl: false + +config :block_scout_web, BlockScoutWeb.Tracer, env: "production", disabled?: true + +config :logger, :block_scout_web, + level: :info, + path: Path.absname("logs/prod/block_scout_web.log"), + rotate: %{max_bytes: 52_428_800, keep: 19} + +config :logger, :api, + level: :debug, + path: Path.absname("logs/prod/api.log"), + metadata_filter: [fetcher: :api], + rotate: %{max_bytes: 52_428_800, keep: 19} + +config :block_scout_web, :captcha_helper, BlockScoutWeb.CaptchaHelper diff --git a/apps/block_scout_web/config/runtime/test.exs b/apps/block_scout_web/config/runtime/test.exs new file mode 100644 index 0000000..f465b51 --- /dev/null +++ b/apps/block_scout_web/config/runtime/test.exs @@ -0,0 +1,21 @@ +import Config + +alias EthereumJSONRPC.Variant + +config :explorer, Explorer.ExchangeRates, enabled: false, store: :none + +config :explorer, Explorer.KnownTokens, enabled: false, store: :none + +config :ueberauth, Ueberauth.Strategy.Auth0.OAuth, + domain: "example.com", + client_id: "clien_id", + client_secret: "secrets" + +config :ueberauth, Ueberauth, + logout_url: "example.com/logout", + logout_return_to_url: "example.com/return" + +variant = Variant.get() + +Code.require_file("#{variant}.exs", "#{__DIR__}/../../../explorer/config/test") +Code.require_file("#{variant}.exs", "#{__DIR__}/../../../indexer/config/test") diff --git a/apps/block_scout_web/config/test.exs b/apps/block_scout_web/config/test.exs new file mode 100644 index 0000000..9c3414f --- /dev/null +++ b/apps/block_scout_web/config/test.exs @@ -0,0 +1,35 @@ +import Config + +config :block_scout_web, :sql_sandbox, true + +# We don't run a server during test. If one is required, +# you can enable the server option below. +config :block_scout_web, BlockScoutWeb.Endpoint, + http: [port: 4002], + secret_key_base: "27Swe6KtEtmN37WyEYRjKWyxYULNtrxlkCEKur4qoV+Lwtk8lafsR16ifz1XBBYj", + server: true, + pubsub_server: BlockScoutWeb.PubSub, + checksum_address_hashes: true + +config :block_scout_web, BlockScoutWeb.Tracer, disabled?: false + +config :logger, :block_scout_web, + level: :warn, + path: Path.absname("logs/test/block_scout_web.log") + +# Configure wallaby +config :wallaby, screenshot_on_failure: true, driver: Wallaby.Chrome, js_errors: false + +config :block_scout_web, BlockScoutWeb.Counters.BlocksIndexedCounter, enabled: false + +config :block_scout_web, BlockScoutWeb.Counters.InternalTransactionsIndexedCounter, enabled: false + +config :block_scout_web, :captcha_helper, BlockScoutWeb.TestCaptchaHelper + +config :ueberauth, Ueberauth, + providers: [ + auth0: { + Ueberauth.Strategy.Auth0, + [callback_url: "example.com/callback"] + } + ] diff --git a/apps/block_scout_web/lib/block_scout_web.ex b/apps/block_scout_web/lib/block_scout_web.ex new file mode 100644 index 0000000..fddfe7c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web.ex @@ -0,0 +1,86 @@ +defmodule BlockScoutWeb do + @moduledoc """ + The entrypoint for defining your web interface, such + as controllers, views, channels and so on. + + This can be used in your application as: + + use BlockScoutWeb, :controller + use BlockScoutWeb, :view + + The definitions below will be executed for every view, + controller, etc, so keep them short and clean, focused + on imports, uses and aliases. + + Do NOT define functions inside the quoted expressions + below. Instead, define any helper function in modules + and import those modules here. + """ + def version(), do: Application.get_env(:block_scout_web, :version) + + def controller do + quote do + use Phoenix.Controller, namespace: BlockScoutWeb + + import BlockScoutWeb.Controller + import BlockScoutWeb.Router.Helpers + import BlockScoutWeb.WebRouter.Helpers, except: [static_path: 2] + import BlockScoutWeb.Gettext + import BlockScoutWeb.ErrorHelpers + import Plug.Conn + + alias BlockScoutWeb.AdminRouter.Helpers, as: AdminRoutes + end + end + + def view do + quote do + use Phoenix.View, + root: "lib/block_scout_web/templates", + namespace: BlockScoutWeb + + # Import convenience functions from controllers + import Phoenix.Controller, only: [get_flash: 2, view_module: 1] + + # Use all HTML functionality (forms, tags, etc) + use Phoenix.HTML + + import BlockScoutWeb.{ + CurrencyHelpers, + ErrorHelpers, + Gettext, + Router.Helpers, + TabHelpers, + Tokens.Helpers, + Views.ScriptHelpers, + WeiHelpers + } + + import BlockScoutWeb.WebRouter.Helpers, except: [static_path: 2] + end + end + + def router do + quote do + use Phoenix.Router + + import Plug.Conn + import Phoenix.Controller + end + end + + def channel do + quote do + use Phoenix.Channel + + import BlockScoutWeb.Gettext + end + end + + @doc """ + When used, dispatch to the appropriate controller/view/etc. + """ + defmacro __using__(which) when is_atom(which) do + apply(__MODULE__, which, []) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/admin_router.ex b/apps/block_scout_web/lib/block_scout_web/admin_router.ex new file mode 100644 index 0000000..7a1c328 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/admin_router.ex @@ -0,0 +1,51 @@ +defmodule BlockScoutWeb.AdminRouter do + @moduledoc """ + Router for admin pages. + """ + + use BlockScoutWeb, :router + + alias BlockScoutWeb.Plug.FetchUserFromSession + alias BlockScoutWeb.Plug.Admin.{CheckOwnerRegistered, RequireAdminRole} + + pipeline :browser do + plug(:accepts, ["html"]) + plug(:fetch_session) + plug(:fetch_flash) + plug(:protect_from_forgery) + plug(BlockScoutWeb.CSPHeader) + end + + pipeline :check_configured do + plug(CheckOwnerRegistered) + end + + pipeline :ensure_admin do + plug(FetchUserFromSession) + plug(RequireAdminRole) + end + + scope "/setup", BlockScoutWeb.Admin do + pipe_through([:browser]) + + get("/", SetupController, :configure) + post("/", SetupController, :configure_admin) + end + + scope "/login", BlockScoutWeb.Admin do + pipe_through([:browser, :check_configured]) + + get("/", SessionController, :new) + post("/", SessionController, :create) + end + + scope "/", BlockScoutWeb.Admin do + pipe_through([:browser, :check_configured, :ensure_admin]) + + get("/", DashboardController, :index) + + scope "/tasks" do + get("/create_contract_methods", TaskController, :create_contract_methods, as: :create_contract_methods) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/api_router.ex b/apps/block_scout_web/lib/block_scout_web/api_router.ex new file mode 100644 index 0000000..d850b61 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/api_router.ex @@ -0,0 +1,199 @@ +defmodule RPCTranslatorForwarder do + @moduledoc """ + Phoenix router limits forwarding, + so this module is to forward old paths for backward compatibility + """ + alias BlockScoutWeb.API.RPC.RPCTranslator + defdelegate init(opts), to: RPCTranslator + defdelegate call(conn, opts), to: RPCTranslator +end + +defmodule BlockScoutWeb.ApiRouter do + @moduledoc """ + Router for API + """ + use BlockScoutWeb, :router + alias BlockScoutWeb.Plug.{CheckAccountAPI, CheckApiV2} + + pipeline :api do + plug(:accepts, ["json"]) + end + + pipeline :account_api do + plug(:fetch_session) + plug(:protect_from_forgery) + plug(CheckAccountAPI) + end + + pipeline :api_v2 do + plug(CheckApiV2) + plug(:fetch_session) + plug(:protect_from_forgery) + end + + alias BlockScoutWeb.Account.Api.V1.{TagsController, UserController} + + scope "/account/v1", as: :account_v1 do + pipe_through(:api) + pipe_through(:account_api) + + get("/get_csrf", UserController, :get_csrf) + + scope "/user" do + get("/info", UserController, :info) + + get("/watchlist", UserController, :watchlist) + delete("/watchlist/:id", UserController, :delete_watchlist) + post("/watchlist", UserController, :create_watchlist) + put("/watchlist/:id", UserController, :update_watchlist) + + get("/api_keys", UserController, :api_keys) + delete("/api_keys/:api_key", UserController, :delete_api_key) + post("/api_keys", UserController, :create_api_key) + put("/api_keys/:api_key", UserController, :update_api_key) + + get("/custom_abis", UserController, :custom_abis) + delete("/custom_abis/:id", UserController, :delete_custom_abi) + post("/custom_abis", UserController, :create_custom_abi) + put("/custom_abis/:id", UserController, :update_custom_abi) + + get("/public_tags", UserController, :public_tags_requests) + delete("/public_tags/:id", UserController, :delete_public_tags_request) + post("/public_tags", UserController, :create_public_tags_request) + put("/public_tags/:id", UserController, :update_public_tags_request) + + scope "/tags" do + get("/address/", UserController, :tags_address) + get("/address/:id", UserController, :tags_address) + delete("/address/:id", UserController, :delete_tag_address) + post("/address/", UserController, :create_tag_address) + put("/address/:id", UserController, :update_tag_address) + + get("/transaction/", UserController, :tags_transaction) + get("/transaction/:id", UserController, :tags_transaction) + delete("/transaction/:id", UserController, :delete_tag_transaction) + post("/transaction/", UserController, :create_tag_transaction) + put("/transaction/:id", UserController, :update_tag_transaction) + end + end + end + + scope "/account/v1" do + pipe_through(:api) + pipe_through(:account_api) + + scope "/tags" do + get("/address/:address_hash", TagsController, :tags_address) + + get("/transaction/:transaction_hash", TagsController, :tags_transaction) + end + end + + scope "/v2", as: :api_v2 do + pipe_through(:api) + pipe_through(:api_v2) + + alias BlockScoutWeb.API.V2 + + get("/search", V2.SearchController, :search) + + scope "/config" do + get("/json-rpc-url", V2.ConfigController, :json_rpc_url) + end + + scope "/transactions" do + get("/", V2.TransactionController, :transactions) + get("/:transaction_hash", V2.TransactionController, :transaction) + get("/:transaction_hash/token-transfers", V2.TransactionController, :token_transfers) + get("/:transaction_hash/internal-transactions", V2.TransactionController, :internal_transactions) + get("/:transaction_hash/logs", V2.TransactionController, :logs) + get("/:transaction_hash/raw-trace", V2.TransactionController, :raw_trace) + end + + scope "/blocks" do + get("/", V2.BlockController, :blocks) + get("/:block_hash_or_number", V2.BlockController, :block) + get("/:block_hash_or_number/transactions", V2.BlockController, :transactions) + end + + scope "/addresses" do + get("/:address_hash", V2.AddressController, :address) + get("/:address_hash/token-balances", V2.AddressController, :token_balances) + get("/:address_hash/transactions", V2.AddressController, :transactions) + get("/:address_hash/token-transfers", V2.AddressController, :token_transfers) + get("/:address_hash/internal-transactions", V2.AddressController, :internal_transactions) + get("/:address_hash/logs", V2.AddressController, :logs) + get("/:address_hash/blocks-validated", V2.AddressController, :blocks_validated) + get("/:address_hash/coin-balance-history", V2.AddressController, :coin_balance_history) + get("/:address_hash/coin-balance-history-by-day", V2.AddressController, :coin_balance_history_by_day) + end + + scope "/main-page" do + get("/blocks", V2.MainPageController, :blocks) + get("/transactions", V2.MainPageController, :transactions) + end + + scope "/stats" do + get("/", V2.StatsController, :stats) + + scope "/charts" do + get("/transactions", V2.StatsController, :transactions_chart) + get("/market", V2.StatsController, :market_chart) + end + end + end + + scope "/v1", as: :api_v1 do + pipe_through(:api) + alias BlockScoutWeb.API.{EthRPC, RPC, V1} + alias BlockScoutWeb.API.V1.HealthController + alias BlockScoutWeb.API.V2.SearchController + + # leave the same endpoint in v1 in order to keep backward compatibility + get("/search", SearchController, :search) + get("/health", HealthController, :health) + get("/gas-price-oracle", V1.GasPriceOracleController, :gas_price_oracle) + + if Application.compile_env(:block_scout_web, __MODULE__)[:reading_enabled] do + get("/supply", V1.SupplyController, :supply) + post("/eth-rpc", EthRPC.EthController, :eth_request) + end + + if Application.compile_env(:block_scout_web, __MODULE__)[:writing_enabled] do + post("/decompiled_smart_contract", V1.DecompiledSmartContractController, :create) + post("/verified_smart_contracts", V1.VerifiedSmartContractController, :create) + end + + if Application.compile_env(:block_scout_web, __MODULE__)[:reading_enabled] do + forward("/", RPC.RPCTranslator, %{ + "block" => {RPC.BlockController, []}, + "account" => {RPC.AddressController, []}, + "logs" => {RPC.LogsController, []}, + "token" => {RPC.TokenController, []}, + "stats" => {RPC.StatsController, []}, + "contract" => {RPC.ContractController, [:verify]}, + "transaction" => {RPC.TransactionController, []} + }) + end + end + + # For backward compatibility. Should be removed + scope "/" do + pipe_through(:api) + alias BlockScoutWeb.API.{EthRPC, RPC} + + if Application.compile_env(:block_scout_web, __MODULE__)[:reading_enabled] do + post("/eth-rpc", EthRPC.EthController, :eth_request) + + forward("/", RPCTranslatorForwarder, %{ + "block" => {RPC.BlockController, []}, + "account" => {RPC.AddressController, []}, + "logs" => {RPC.LogsController, []}, + "token" => {RPC.TokenController, []}, + "stats" => {RPC.StatsController, []}, + "contract" => {RPC.ContractController, [:verify]}, + "transaction" => {RPC.TransactionController, []} + }) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/application.ex b/apps/block_scout_web/lib/block_scout_web/application.ex new file mode 100644 index 0000000..2d84cc8 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/application.ex @@ -0,0 +1,52 @@ +defmodule BlockScoutWeb.Application do + @moduledoc """ + Supervises `BlockScoutWeb.Endpoint` in order to serve Web UI. + """ + + use Application + + alias BlockScoutWeb.API.APILogger + alias BlockScoutWeb.Counters.{BlocksIndexedCounter, InternalTransactionsIndexedCounter} + alias BlockScoutWeb.{Endpoint, Prometheus} + alias BlockScoutWeb.RealtimeEventHandler + + def start(_type, _args) do + import Supervisor + + Prometheus.Instrumenter.setup() + Prometheus.Exporter.setup() + + APILogger.message( + "Current global API rate limit #{inspect(Application.get_env(:block_scout_web, :api_rate_limit)[:global_limit])} reqs/sec" + ) + + APILogger.message( + "Current API rate limit by key #{inspect(Application.get_env(:block_scout_web, :api_rate_limit)[:limit_by_key])} reqs/sec" + ) + + APILogger.message( + "Current API rate limit by IP #{inspect(Application.get_env(:block_scout_web, :api_rate_limit)[:limit_by_ip])} reqs/sec" + ) + + # Define workers and child supervisors to be supervised + children = [ + # Start the endpoint when the application starts + {Phoenix.PubSub, name: BlockScoutWeb.PubSub}, + child_spec(Endpoint, []), + {Absinthe.Subscription, Endpoint}, + {RealtimeEventHandler, name: RealtimeEventHandler}, + {BlocksIndexedCounter, name: BlocksIndexedCounter}, + {InternalTransactionsIndexedCounter, name: InternalTransactionsIndexedCounter} + ] + + opts = [strategy: :one_for_one, name: BlockScoutWeb.Supervisor, max_restarts: 1_000] + Supervisor.start_link(children, opts) + end + + # Tell Phoenix to update the endpoint configuration + # whenever the application is updated. + def config_change(changed, _new, removed) do + Endpoint.config_change(changed, removed) + :ok + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/captcha_helper.ex b/apps/block_scout_web/lib/block_scout_web/captcha_helper.ex new file mode 100644 index 0000000..abeb3d1 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/captcha_helper.ex @@ -0,0 +1,27 @@ +defmodule BlockScoutWeb.CaptchaHelper do + @moduledoc """ + A helper for CAPTCHA + """ + + @callback recaptcha_passed?(String.t() | nil) :: bool + @spec recaptcha_passed?(String.t() | nil) :: bool + def recaptcha_passed?(nil), do: false + + def recaptcha_passed?(recaptcha_response) do + re_captcha_secret_key = Application.get_env(:block_scout_web, :re_captcha_secret_key) + body = "secret=#{re_captcha_secret_key}&response=#{recaptcha_response}" + + headers = [{"Content-type", "application/x-www-form-urlencoded"}] + + case HTTPoison.post("https://www.google.com/recaptcha/api/siteverify", body, headers, []) do + {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> + case Jason.decode!(body) do + %{"success" => true} -> true + _ -> false + end + + _ -> + false + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/chain.ex b/apps/block_scout_web/lib/block_scout_web/chain.ex new file mode 100644 index 0000000..3d710fe --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/chain.ex @@ -0,0 +1,413 @@ +defmodule BlockScoutWeb.Chain do + @moduledoc """ + Converts the `param` to the corresponding resource that uses that format of param. + """ + + import Explorer.Chain, + only: [ + find_or_insert_address_from_hash: 1, + hash_to_block: 1, + hash_to_transaction: 1, + number_to_block: 1, + string_to_address_hash: 1, + string_to_block_hash: 1, + string_to_transaction_hash: 1, + token_contract_address_from_token_name: 1 + ] + + alias Explorer.Chain.Block.Reward + + alias Explorer.Chain.{ + Address, + Address.CoinBalance, + Address.CurrentTokenBalance, + Block, + InternalTransaction, + Log, + SmartContract, + Token, + TokenTransfer, + Transaction, + Wei + } + + alias Explorer.PagingOptions + + defimpl Poison.Encoder, for: Decimal do + def encode(value, _opts) do + # silence the xref warning + decimal = Decimal + + [?\", decimal.to_string(value), ?\"] + end + end + + @page_size 50 + @default_paging_options %PagingOptions{page_size: @page_size + 1} + @address_hash_len 40 + @tx_block_hash_len 64 + + def default_paging_options do + @default_paging_options + end + + def current_filter(%{paging_options: paging_options} = params) do + params + |> Map.get("filter") + |> case do + "to" -> [direction: :to, paging_options: paging_options] + "from" -> [direction: :from, paging_options: paging_options] + _ -> [paging_options: paging_options] + end + end + + def current_filter(params) do + params + |> Map.get("filter") + |> case do + "to" -> [direction: :to] + "from" -> [direction: :from] + _ -> [] + end + end + + @spec from_param(String.t()) :: {:ok, Address.t() | Block.t() | Transaction.t()} | {:error, :not_found} + def from_param(param) + + def from_param("0x" <> number_string = param) when byte_size(number_string) == @address_hash_len, + do: address_from_param(param) + + def from_param("0x" <> number_string = param) when byte_size(number_string) == @tx_block_hash_len, + do: block_or_transaction_from_param(param) + + def from_param(param) when byte_size(param) == @address_hash_len, + do: address_from_param("0x" <> param) + + def from_param(param) when byte_size(param) == @tx_block_hash_len, + do: block_or_transaction_from_param("0x" <> param) + + def from_param(string) when is_binary(string) do + case param_to_block_number(string) do + {:ok, number} -> number_to_block(number) + _ -> token_address_from_name(string) + end + end + + def next_page_params([], _list, _params), do: nil + + def next_page_params(_, list, params) do + next_page_params = Map.merge(params, paging_params(List.last(list))) + current_items_count_str = Map.get(next_page_params, "items_count") + + items_count = + if current_items_count_str do + {current_items_count, _} = Integer.parse(current_items_count_str) + current_items_count + Enum.count(list) + else + Enum.count(list) + end + + Map.put(next_page_params, "items_count", items_count) + end + + def paging_options(%{"hash" => hash, "fetched_coin_balance" => fetched_coin_balance}) do + with {coin_balance, ""} <- Integer.parse(fetched_coin_balance), + {:ok, address_hash} <- string_to_address_hash(hash) do + [paging_options: %{@default_paging_options | key: {%Wei{value: Decimal.new(coin_balance)}, address_hash}}] + else + _ -> + [paging_options: @default_paging_options] + end + end + + def paging_options(%{ + "address_hash" => address_hash, + "tx_hash" => tx_hash, + "block_hash" => block_hash, + "holder_count" => holder_count, + "name" => name, + "inserted_at" => inserted_at, + "item_type" => item_type + }) do + [ + paging_options: %{ + @default_paging_options + | key: {address_hash, tx_hash, block_hash, holder_count, name, inserted_at, item_type} + } + ] + end + + def paging_options(%{"holder_count" => holder_count, "name" => token_name}) do + case Integer.parse(holder_count) do + {holder_count, ""} -> + [paging_options: %{@default_paging_options | key: {holder_count, token_name}}] + + _ -> + [paging_options: @default_paging_options] + end + end + + def paging_options(%{ + "block_number" => block_number_string, + "transaction_index" => transaction_index_string, + "index" => index_string + }) do + with {block_number, ""} <- Integer.parse(block_number_string), + {transaction_index, ""} <- Integer.parse(transaction_index_string), + {index, ""} <- Integer.parse(index_string) do + [paging_options: %{@default_paging_options | key: {block_number, transaction_index, index}}] + else + _ -> + [paging_options: @default_paging_options] + end + end + + def paging_options(%{"block_number" => block_number_string, "index" => index_string}) do + with {block_number, ""} <- Integer.parse(block_number_string), + {index, ""} <- Integer.parse(index_string) do + [paging_options: %{@default_paging_options | key: {block_number, index}}] + else + _ -> + [paging_options: @default_paging_options] + end + end + + def paging_options(%{"block_number" => block_number_string}) do + case Integer.parse(block_number_string) do + {block_number, ""} -> + [paging_options: %{@default_paging_options | key: {block_number}}] + + _ -> + [paging_options: @default_paging_options] + end + end + + def paging_options(%{"index" => index_string}) when is_binary(index_string) do + case Integer.parse(index_string) do + {index, ""} -> + [paging_options: %{@default_paging_options | key: {index}}] + + _ -> + [paging_options: @default_paging_options] + end + end + + def paging_options(%{"index" => index}) when is_integer(index) do + [paging_options: %{@default_paging_options | key: {index}}] + end + + def paging_options(%{"inserted_at" => inserted_at_string, "hash" => hash_string}) do + with {:ok, inserted_at, _} <- DateTime.from_iso8601(inserted_at_string), + {:ok, hash} <- string_to_transaction_hash(hash_string) do + [paging_options: %{@default_paging_options | key: {inserted_at, hash}, is_pending_tx: true}] + else + _ -> + [paging_options: @default_paging_options] + end + end + + def paging_options(%{"token_name" => name, "token_type" => type, "token_inserted_at" => inserted_at}), + do: [paging_options: %{@default_paging_options | key: {name, type, inserted_at}}] + + def paging_options(%{"value" => value, "address_hash" => address_hash}) do + [paging_options: %{@default_paging_options | key: {value, address_hash}}] + end + + def paging_options(%{"token_name" => name, "token_type" => type, "value" => value}) do + [paging_options: %{@default_paging_options | key: {name, type, value}}] + end + + def paging_options(%{"smart_contract_id" => id}) do + [paging_options: %{@default_paging_options | key: {id}}] + end + + def paging_options(_params), do: [paging_options: @default_paging_options] + + def put_key_value_to_paging_options([paging_options: paging_options], key, value) do + [paging_options: Map.put(paging_options, key, value)] + end + + def fetch_page_number(%{"page_number" => page_number_string}) do + case Integer.parse(page_number_string) do + {number, ""} -> + number + + _ -> + 1 + end + end + + def fetch_page_number(%{"items_count" => item_count_str}) do + {items_count, _} = Integer.parse(item_count_str) + div(items_count, @page_size) + 1 + end + + def fetch_page_number(_), do: 1 + + def update_page_parameters(new_page_number, new_page_size, %PagingOptions{} = options) do + %PagingOptions{options | page_number: new_page_number, page_size: new_page_size} + end + + def param_to_block_number(formatted_number) when is_binary(formatted_number) do + case Integer.parse(formatted_number) do + {number, ""} -> {:ok, number} + _ -> {:error, :invalid} + end + end + + def param_to_block_timestamp(timestamp_string) when is_binary(timestamp_string) do + case Integer.parse(timestamp_string) do + {temstamp_int, ""} -> + timestamp = + temstamp_int + |> DateTime.from_unix!(:second) + + {:ok, timestamp} + + _ -> + {:error, :invalid_timestamp} + end + end + + def param_to_block_closest(closest) when is_binary(closest) do + case closest do + "before" -> {:ok, :before} + "after" -> {:ok, :after} + _ -> {:error, :invalid_closest} + end + end + + def split_list_by_page(list_plus_one), do: Enum.split(list_plus_one, @page_size) + + defp address_from_param(param) do + case string_to_address_hash(param) do + {:ok, hash} -> + find_or_insert_address_from_hash(hash) + + :error -> + {:error, :not_found} + end + end + + defp token_address_from_name(name) do + case token_contract_address_from_token_name(name) do + {:ok, hash} -> find_or_insert_address_from_hash(hash) + _ -> {:error, :not_found} + end + end + + defp paging_params({%Address{hash: hash, fetched_coin_balance: fetched_coin_balance}, _}) do + %{"hash" => hash, "fetched_coin_balance" => Decimal.to_string(fetched_coin_balance.value)} + end + + defp paging_params(%Token{holder_count: holder_count, name: token_name}) do + %{"holder_count" => holder_count, "name" => token_name} + end + + defp paging_params([%Token{holder_count: holder_count, name: token_name}, _]) do + %{"holder_count" => holder_count, "name" => token_name} + end + + defp paging_params({%Reward{block: %{number: number}}, _}) do + %{"block_number" => number, "index" => 0} + end + + defp paging_params(%Block{number: number}) do + %{"block_number" => number} + end + + defp paging_params(%InternalTransaction{index: index, transaction_hash: transaction_hash}) do + {:ok, %Transaction{block_number: block_number, index: transaction_index}} = hash_to_transaction(transaction_hash) + %{"block_number" => block_number, "transaction_index" => transaction_index, "index" => index} + end + + defp paging_params(%Log{index: index} = log) do + if Ecto.assoc_loaded?(log.transaction) do + %{"block_number" => log.transaction.block_number, "transaction_index" => log.transaction.index, "index" => index} + else + %{"index" => index} + end + end + + defp paging_params(%Transaction{block_number: nil, inserted_at: inserted_at, hash: hash}) do + %{"inserted_at" => DateTime.to_iso8601(inserted_at), "hash" => hash} + end + + defp paging_params(%Transaction{block_number: block_number, index: index}) do + %{"block_number" => block_number, "index" => index} + end + + defp paging_params(%TokenTransfer{block_number: block_number, log_index: index}) do + %{"block_number" => block_number, "index" => index} + end + + defp paging_params(%Address.Token{name: name, type: type, inserted_at: inserted_at}) do + inserted_at_datetime = DateTime.to_iso8601(inserted_at) + + %{"token_name" => name, "token_type" => type, "token_inserted_at" => inserted_at_datetime} + end + + defp paging_params(%CurrentTokenBalance{address_hash: address_hash, value: value}) do + %{"address_hash" => to_string(address_hash), "value" => Decimal.to_integer(value)} + end + + defp paging_params({%CurrentTokenBalance{value: value}, %Token{name: name, type: type}}) do + %{"token_name" => name, "token_type" => type, "value" => Decimal.to_integer(value)} + end + + defp paging_params(%CoinBalance{block_number: block_number}) do + %{"block_number" => block_number} + end + + defp paging_params(%SmartContract{} = smart_contract) do + %{"smart_contract_id" => smart_contract.id} + end + + defp paging_params(%{ + address_hash: address_hash, + tx_hash: tx_hash, + block_hash: block_hash, + holder_count: holder_count, + name: name, + inserted_at: inserted_at, + type: type + }) do + inserted_at_datetime = DateTime.to_iso8601(inserted_at) + + %{ + "address_hash" => address_hash, + "tx_hash" => tx_hash, + "block_hash" => block_hash, + "holder_count" => holder_count, + "name" => name, + "inserted_at" => inserted_at_datetime, + "item_type" => type + } + end + + defp block_or_transaction_from_param(param) do + with {:error, :not_found} <- transaction_from_param(param) do + hash_string_to_block(param) + end + end + + defp transaction_from_param(param) do + case string_to_transaction_hash(param) do + {:ok, hash} -> + hash_to_transaction(hash) + + :error -> + {:error, :not_found} + end + end + + defp hash_string_to_block(hash_string) do + case string_to_block_hash(hash_string) do + {:ok, hash} -> + hash_to_block(hash) + + :error -> + {:error, :not_found} + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex new file mode 100644 index 0000000..e73a54d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/channels/address_channel.ex @@ -0,0 +1,335 @@ +defmodule BlockScoutWeb.AddressChannel do + @moduledoc """ + Establishes pub/sub channel for address page live updates. + """ + use BlockScoutWeb, :channel + + alias BlockScoutWeb.API.V2.AddressView, as: AddressViewAPI + + alias BlockScoutWeb.{ + AddressCoinBalanceView, + AddressView, + InternalTransactionView, + TransactionView + } + + alias Explorer.{Chain, Market, Repo} + alias Explorer.Chain.{Hash, Transaction, Wei} + alias Explorer.Chain.Hash.Address, as: AddressHash + alias Explorer.ExchangeRates.Token + alias Phoenix.View + + intercept([ + "balance_update", + "coin_balance", + "count", + "internal_transaction", + "transaction", + "verification_result", + "token_transfer", + "pending_transaction" + ]) + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + def join("addresses:" <> address_hash, _params, socket) do + {:ok, %{}, assign(socket, :address_hash, address_hash)} + end + + def handle_in("get_balance", _, socket) do + with {:ok, casted_address_hash} <- AddressHash.cast(socket.assigns.address_hash), + {:ok, address = %{fetched_coin_balance: balance}} when not is_nil(balance) <- + Chain.hash_to_address(casted_address_hash), + exchange_rate <- Market.get_exchange_rate(Explorer.coin()) || Token.null(), + {:ok, rendered} <- render_balance_card(address, exchange_rate, socket) do + reply = + {:ok, + %{ + balance_card: rendered, + balance: address.fetched_coin_balance.value, + fetched_coin_balance_block_number: address.fetched_coin_balance_block_number + }} + + {:reply, reply, socket} + else + _ -> + {:noreply, socket} + end + end + + def handle_out( + "balance_update", + %{address: address, exchange_rate: exchange_rate}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket + ) do + push(socket, "balance", %{ + balance: address.fetched_coin_balance.value, + block_number: address.fetched_coin_balance_block_number, + exchange_rate: exchange_rate.usd_value + }) + + {:noreply, socket} + end + + def handle_out( + "balance_update", + %{address: address, exchange_rate: exchange_rate}, + socket + ) do + case render_balance_card(address, exchange_rate, socket) do + {:ok, rendered} -> + push(socket, "balance", %{ + balance_card: rendered, + balance: address.fetched_coin_balance.value, + fetched_coin_balance_block_number: address.fetched_coin_balance_block_number + }) + + {:noreply, socket} + + _ -> + {:noreply, socket} + end + end + + def handle_out("verification_result", result, socket) do + case result[:result] do + {:ok, _contract} -> + push(socket, "verification", %{verification_result: :ok}) + {:noreply, socket} + + {:error, result} -> + push(socket, "verification", %{verification_result: result}) + {:noreply, socket} + end + end + + def handle_out("count", %{count: count}, %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket) do + push(socket, "count", %{count: to_string(count)}) + + {:noreply, socket} + end + + def handle_out("count", %{count: count}, socket) do + Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) + + push(socket, "count", %{count: BlockScoutWeb.Cldr.Number.to_string!(count, format: "#,###")}) + + {:noreply, socket} + end + + def handle_out( + "internal_transaction", + %{address: _address, internal_transaction: _internal_transaction}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket + ) do + push(socket, "internal_transaction", %{internal_transaction: 1}) + + {:noreply, socket} + end + + def handle_out("internal_transaction", %{address: address, internal_transaction: internal_transaction}, socket) do + Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) + + rendered_internal_transaction = + View.render_to_string( + InternalTransactionView, + "_tile.html", + current_address: address, + internal_transaction: internal_transaction + ) + + push(socket, "internal_transaction", %{ + to_address_hash: to_string(internal_transaction.to_address_hash), + from_address_hash: to_string(internal_transaction.from_address_hash), + internal_transaction_html: rendered_internal_transaction + }) + + {:noreply, socket} + end + + def handle_out("transaction", data, socket), do: handle_transaction(data, socket, "transaction") + + def handle_out("token_transfer", data, socket), do: handle_token_transfer(data, socket, "token_transfer") + + def handle_out( + "coin_balance", + %{block_number: block_number}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket + ) do + coin_balance = Chain.get_coin_balance(socket.assigns.address_hash, block_number) + + rendered_coin_balance = AddressViewAPI.render("coin_balance.json", %{coin_balance: coin_balance}) + + push(socket, "coin_balance", %{coin_balance: rendered_coin_balance}) + + push_current_coin_balance(socket, block_number, coin_balance) + + {:noreply, socket} + end + + def handle_out("coin_balance", %{block_number: block_number}, socket) do + coin_balance = Chain.get_coin_balance(socket.assigns.address_hash, block_number) + + Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) + + rendered_coin_balance = + View.render_to_string( + AddressCoinBalanceView, + "_coin_balances.html", + conn: socket, + coin_balance: coin_balance + ) + + push(socket, "coin_balance", %{ + coin_balance_html: rendered_coin_balance + }) + + push_current_coin_balance(socket, block_number, coin_balance) + + {:noreply, socket} + end + + def handle_out("pending_transaction", data, %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket), + do: handle_transaction(data, socket, "pending_transaction") + + def handle_out("pending_transaction", data, socket), do: handle_transaction(data, socket, "transaction") + + def push_current_coin_balance( + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket, + block_number, + coin_balance + ) do + push(socket, "current_coin_balance", %{ + coin_balance: (coin_balance && coin_balance.value) || %Wei{value: Decimal.new(0)}, + exchange_rate: (Market.get_exchange_rate(Explorer.coin()) || Token.null()).usd_value, + block_number: block_number + }) + end + + def push_current_coin_balance(socket, block_number, coin_balance) do + {:ok, hash} = Chain.string_to_address_hash(socket.assigns.address_hash) + + rendered_current_coin_balance = + View.render_to_string( + AddressView, + "_current_coin_balance.html", + conn: socket, + address: Chain.hash_to_address(hash), + coin_balance: (coin_balance && coin_balance.value) || %Wei{value: Decimal.new(0)}, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + ) + + rendered_link = + View.render_to_string( + AddressView, + "_block_link.html", + conn: socket, + block_number: block_number + ) + + push(socket, "current_coin_balance", %{ + current_coin_balance_html: rendered_current_coin_balance, + current_coin_balance_block_number_html: rendered_link, + current_coin_balance_block_number: coin_balance.block_number + }) + end + + def handle_transaction( + %{address: _address, transaction: _transaction}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket, + event + ) do + push(socket, event, %{transaction: 1}) + + {:noreply, socket} + end + + def handle_transaction(%{address: address, transaction: transaction}, socket, event) do + Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) + + rendered = + View.render_to_string( + TransactionView, + "_tile.html", + conn: socket, + current_address: address, + transaction: transaction, + burn_address_hash: @burn_address_hash + ) + + push(socket, event, %{ + to_address_hash: to_string(transaction.to_address_hash), + from_address_hash: to_string(transaction.from_address_hash), + transaction_hash: Hash.to_string(transaction.hash), + transaction_html: rendered + }) + + {:noreply, socket} + end + + def handle_token_transfer( + %{address: _address, token_transfer: _token_transfer}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket, + event + ) do + push(socket, event, %{token_transfer: 1}) + + {:noreply, socket} + end + + def handle_token_transfer(%{address: address, token_transfer: token_transfer}, socket, event) do + Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) + + transaction = + Transaction + |> Repo.get_by(hash: token_transfer.transaction_hash) + |> Repo.preload([ + :from_address, + :to_address, + :block, + :created_contract_address, + token_transfers: [:from_address, :to_address, :token] + ]) + + rendered = + View.render_to_string( + TransactionView, + "_tile.html", + current_address: address, + transaction: transaction, + burn_address_hash: @burn_address_hash, + conn: socket + ) + + push(socket, event, %{ + to_address_hash: to_string(token_transfer.to_address_hash), + from_address_hash: to_string(token_transfer.from_address_hash), + token_transfer_hash: Hash.to_string(token_transfer.transaction_hash), + token_transfer_html: rendered + }) + + {:noreply, socket} + end + + defp render_balance_card(address, exchange_rate, socket) do + Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) + + try do + rendered = + View.render_to_string( + AddressView, + "_balance_dropdown.html", + conn: socket, + address: address, + coin_balance_status: :current, + exchange_rate: exchange_rate + ) + + {:ok, rendered} + rescue + _ -> + :error + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex new file mode 100644 index 0000000..560a1d4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex @@ -0,0 +1,65 @@ +defmodule BlockScoutWeb.BlockChannel do + @moduledoc """ + Establishes pub/sub channel for live updates of block events. + """ + use BlockScoutWeb, :channel + + alias BlockScoutWeb.API.V2.BlockView, as: BlockViewAPI + alias BlockScoutWeb.{BlockView, ChainView} + alias Phoenix.View + alias Timex.Duration + + intercept(["new_block"]) + + def join("blocks:new_block", _params, socket) do + {:ok, %{}, socket} + end + + def join("blocks:" <> _miner_address, _params, socket) do + {:ok, %{}, socket} + end + + def handle_out( + "new_block", + %{block: block, average_block_time: average_block_time}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket + ) do + rendered_block = BlockViewAPI.render("block.json", %{block: block, socket: nil}) + + push(socket, "new_block", %{ + average_block_time: to_string(Duration.to_milliseconds(average_block_time)), + block: rendered_block + }) + + {:noreply, socket} + end + + def handle_out("new_block", %{block: block, average_block_time: average_block_time}, socket) do + Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) + + rendered_block = + View.render_to_string( + BlockView, + "_tile.html", + block: block, + block_type: BlockView.block_type(block) + ) + + rendered_chain_block = + View.render_to_string( + ChainView, + "_block.html", + block: block + ) + + push(socket, "new_block", %{ + average_block_time: Timex.format_duration(average_block_time, Explorer.Counters.AverageBlockTimeDurationFormat), + chain_block_html: rendered_chain_block, + block_html: rendered_block, + block_number: block.number, + block_miner_hash: to_string(block.miner_hash) + }) + + {:noreply, socket} + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/exchange_rate_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/exchange_rate_channel.ex new file mode 100644 index 0000000..ef5d520 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/channels/exchange_rate_channel.ex @@ -0,0 +1,35 @@ +defmodule BlockScoutWeb.ExchangeRateChannel do + @moduledoc """ + Establishes pub/sub channel for address page live updates. + """ + use BlockScoutWeb, :channel + + intercept(["new_rate"]) + + def join("exchange_rate:new_rate", _params, socket) do + {:ok, %{}, socket} + end + + def handle_out( + "new_rate", + %{exchange_rate: exchange_rate, market_history_data: market_history_data}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket + ) do + push(socket, "new_rate", %{ + exchange_rate: exchange_rate.usd_value, + available_supply: exchange_rate.available_supply, + chart_data: market_history_data + }) + + {:noreply, socket} + end + + def handle_out("new_rate", %{exchange_rate: exchange_rate, market_history_data: market_history_data}, socket) do + push(socket, "new_rate", %{ + exchange_rate: exchange_rate, + market_history_data: market_history_data + }) + + {:noreply, socket} + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/reward_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/reward_channel.ex new file mode 100644 index 0000000..e53ac61 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/channels/reward_channel.ex @@ -0,0 +1,46 @@ +defmodule BlockScoutWeb.RewardChannel do + @moduledoc """ + Establishes pub/sub channel for live updates of block reward events. + """ + use BlockScoutWeb, :channel + + alias BlockScoutWeb.TransactionView + alias Explorer.Chain + alias Phoenix.View + + intercept(["new_reward"]) + + def join("rewards:" <> address_hash, _params, socket) do + with {:ok, hash} <- Chain.string_to_address_hash(address_hash), + {:ok, address} <- Chain.hash_to_address(hash) do + {:ok, %{}, assign(socket, :current_address, address)} + end + end + + def handle_out( + "new_reward", + %{emission_funds: _emission_funds, validator: _validator}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket + ) do + push(socket, "new_reward", %{reward: 1}) + + {:noreply, socket} + end + + def handle_out("new_reward", %{emission_funds: emission_funds, validator: validator}, socket) do + Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) + + rendered_reward = + View.render_to_string( + TransactionView, + "_emission_reward_tile.html", + current_address: socket.assigns.current_address, + emission_funds: emission_funds, + validator: validator + ) + + push(socket, "new_reward", %{reward_html: rendered_reward}) + + {:noreply, socket} + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/token_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/token_channel.ex new file mode 100644 index 0000000..56b22c0 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/channels/token_channel.ex @@ -0,0 +1,51 @@ +defmodule BlockScoutWeb.TokenChannel do + @moduledoc """ + Establishes pub/sub channel for live updates of token transfer events. + """ + use BlockScoutWeb, :channel + + alias BlockScoutWeb.Tokens.TransferView + alias Explorer.Chain + alias Explorer.Chain.Hash + alias Phoenix.View + + intercept(["token_transfer"]) + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + def join("tokens:" <> _transaction_hash, _params, socket) do + {:ok, %{}, socket} + end + + def handle_out( + "token_transfer", + %{token_transfer: _token_transfer}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket + ) do + push(socket, "token_transfer", %{token_transfer: 1}) + + {:noreply, socket} + end + + def handle_out("token_transfer", %{token_transfer: token_transfer}, socket) do + Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) + + rendered_token_transfer = + View.render_to_string( + TransferView, + "_token_transfer.html", + conn: socket, + token: token_transfer.token, + token_transfer: token_transfer, + burn_address_hash: @burn_address_hash + ) + + push(socket, "token_transfer", %{ + token_transfer_hash: Hash.to_string(token_transfer.transaction_hash), + token_transfer_html: rendered_token_transfer + }) + + {:noreply, socket} + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex b/apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex new file mode 100644 index 0000000..669991e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/channels/transaction_channel.ex @@ -0,0 +1,92 @@ +defmodule BlockScoutWeb.TransactionChannel do + @moduledoc """ + Establishes pub/sub channel for live updates of transaction events. + """ + use BlockScoutWeb, :channel + + alias BlockScoutWeb.TransactionView + alias Explorer.Chain + alias Explorer.Chain.Hash + alias Phoenix.View + + intercept(["pending_transaction", "transaction"]) + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + def join("transactions:new_transaction", _params, socket) do + {:ok, %{}, socket} + end + + def join("transactions:new_pending_transaction", _params, socket) do + {:ok, %{}, socket} + end + + def join("transactions:stats", _params, socket) do + {:ok, %{}, socket} + end + + def join("transactions:" <> _transaction_hash, _params, socket) do + {:ok, %{}, socket} + end + + def handle_out( + "pending_transaction", + %{transaction: _transaction}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket + ) do + push(socket, "pending_transaction", %{pending_transaction: 1}) + + {:noreply, socket} + end + + def handle_out("pending_transaction", %{transaction: transaction}, socket) do + Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) + + rendered_transaction = + View.render_to_string( + TransactionView, + "_tile.html", + transaction: transaction, + burn_address_hash: @burn_address_hash, + conn: socket + ) + + push(socket, "pending_transaction", %{ + transaction_hash: Hash.to_string(transaction.hash), + transaction_html: rendered_transaction + }) + + {:noreply, socket} + end + + def handle_out( + "transaction", + %{transaction: _transaction}, + %Phoenix.Socket{handler: BlockScoutWeb.UserSocketV2} = socket + ) do + push(socket, "transaction", %{transaction: 1}) + + {:noreply, socket} + end + + def handle_out("transaction", %{transaction: transaction}, socket) do + Gettext.put_locale(BlockScoutWeb.Gettext, socket.assigns.locale) + + rendered_transaction = + View.render_to_string( + TransactionView, + "_tile.html", + transaction: transaction, + burn_address_hash: @burn_address_hash, + conn: socket + ) + + push(socket, "transaction", %{ + transaction_hash: Hash.to_string(transaction.hash), + transaction_html: rendered_transaction + }) + + {:noreply, socket} + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/user_socket.ex b/apps/block_scout_web/lib/block_scout_web/channels/user_socket.ex new file mode 100644 index 0000000..6060428 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/channels/user_socket.ex @@ -0,0 +1,21 @@ +defmodule BlockScoutWeb.UserSocket do + use Phoenix.Socket + use Absinthe.Phoenix.Socket, schema: BlockScoutWeb.Schema + + channel("addresses:*", BlockScoutWeb.AddressChannel) + channel("blocks:*", BlockScoutWeb.BlockChannel) + channel("exchange_rate:*", BlockScoutWeb.ExchangeRateChannel) + channel("rewards:*", BlockScoutWeb.RewardChannel) + channel("transactions:*", BlockScoutWeb.TransactionChannel) + channel("tokens:*", BlockScoutWeb.TokenChannel) + + def connect(%{"locale" => locale}, socket) do + {:ok, assign(socket, :locale, locale)} + end + + def connect(_params, socket) do + {:ok, socket} + end + + def id(_socket), do: nil +end diff --git a/apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex b/apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex new file mode 100644 index 0000000..b012b8d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/channels/user_socket_v2.ex @@ -0,0 +1,19 @@ +defmodule BlockScoutWeb.UserSocketV2 do + @moduledoc """ + Module to distinct new and old UI websocket connections + """ + use Phoenix.Socket + + channel("addresses:*", BlockScoutWeb.AddressChannel) + channel("blocks:*", BlockScoutWeb.BlockChannel) + channel("exchange_rate:*", BlockScoutWeb.ExchangeRateChannel) + channel("rewards:*", BlockScoutWeb.RewardChannel) + channel("transactions:*", BlockScoutWeb.TransactionChannel) + channel("tokens:*", BlockScoutWeb.TokenChannel) + + def connect(_params, socket) do + {:ok, socket} + end + + def id(_socket), do: nil +end diff --git a/apps/block_scout_web/lib/block_scout_web/checksum_address.ex b/apps/block_scout_web/lib/block_scout_web/checksum_address.ex new file mode 100644 index 0000000..660db06 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/checksum_address.ex @@ -0,0 +1,58 @@ +defmodule BlockScoutWeb.ChecksumAddress do + @moduledoc """ + Adds checksummed version of address hashes. + """ + + import Plug.Conn + + alias BlockScoutWeb.Controller, as: BlockScoutWebController + alias Explorer.Chain + alias Explorer.Chain.Address + alias Phoenix.Controller + alias Plug.Conn + + def init(opts), do: opts + + def call(%Conn{params: %{"id" => id}} = conn, _opts) do + check_checksum(conn, id, "id") + end + + def call(%Conn{params: %{"address_id" => id}} = conn, _opts) do + check_checksum(conn, id, "address_id") + end + + def call(conn, _), do: conn + + defp check_checksum(conn, id, param_name) do + if Application.get_env(:block_scout_web, :checksum_address_hashes) do + case Chain.string_to_address_hash(id) do + {:ok, address_hash} -> + checksummed_hash = Address.checksum(address_hash) + + if checksummed_hash != id do + conn = %{conn | params: Map.merge(conn.params, %{param_name => checksummed_hash})} + + path_with_checksummed_address = String.replace(conn.request_path, id, checksummed_hash) + + new_path = + if conn.query_string != "" do + path_with_checksummed_address <> "?" <> conn.query_string + else + path_with_checksummed_address + end + + conn + |> Controller.redirect(to: new_path |> BlockScoutWebController.full_path()) + |> halt + else + conn + end + + _ -> + conn + end + else + conn + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/cldr.ex b/apps/block_scout_web/lib/block_scout_web/cldr.ex new file mode 100644 index 0000000..722dbe6 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/cldr.ex @@ -0,0 +1,13 @@ +defmodule BlockScoutWeb.Cldr do + @moduledoc """ + Cldr global configuration. + """ + + use Cldr, + default_locale: "en", + locales: ["en"], + gettext: BlockScoutWeb.Gettext, + generate_docs: false, + precompile_number_formats: ["#,###", "#,##0.##################", "#.#%", "#,##0"], + providers: [Cldr.Number, Cldr.Unit] +end diff --git a/apps/block_scout_web/lib/block_scout_web/controller.ex b/apps/block_scout_web/lib/block_scout_web/controller.ex new file mode 100644 index 0000000..7776342 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controller.ex @@ -0,0 +1,65 @@ +defmodule BlockScoutWeb.Controller do + @moduledoc """ + Common controller error responses + """ + + import Phoenix.Controller + import Plug.Conn + + @doc """ + Renders HTML Not Found error + """ + def not_found(conn) do + conn + |> put_status(:not_found) + |> put_view(BlockScoutWeb.PageNotFoundView) + |> render(:index, token: nil) + |> halt() + end + + def unprocessable_entity(conn) do + conn + |> put_status(:unprocessable_entity) + |> put_view(BlockScoutWeb.Error422View) + |> render(:index) + |> halt() + end + + @doc """ + Checks if the request is AJAX or not. + """ + def ajax?(conn) do + case get_req_header(conn, "x-requested-with") do + [value] -> value in ["XMLHttpRequest", "xmlhttprequest"] + [] -> false + end + end + + def current_full_path(conn) do + current_path = current_path(conn) + + full_path(current_path) + end + + def full_path(path) do + url_params = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url] + network_path = url_params[:path] + + if network_path do + if path =~ network_path do + path + else + network_path = + if String.starts_with?(path, "/") do + String.trim_trailing(network_path, "/") + else + network_path + end + + network_path <> path + end + else + path + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/fallback_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/fallback_controller.ex new file mode 100644 index 0000000..56c3e44 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/fallback_controller.ex @@ -0,0 +1,83 @@ +defmodule BlockScoutWeb.Account.Api.V1.FallbackController do + use Phoenix.Controller + + alias BlockScoutWeb.Account.Api.V1.UserView + alias Ecto.Changeset + + def call(conn, {:identity, _}) do + conn + |> put_status(:not_found) + |> put_view(UserView) + |> render(:message, %{message: "User not found"}) + end + + def call(conn, {:watchlist, _}) do + conn + |> put_status(:not_found) + |> put_view(UserView) + |> render(:message, %{message: "Watchlist not found"}) + end + + def call(conn, {:error, %{reason: :item_not_found}}) do + conn + |> put_status(:not_found) + |> put_view(UserView) + |> render(:message, %{message: "Item not found"}) + end + + def call(conn, {:error, %Changeset{} = changeset}) do + conn + |> put_status(:unprocessable_entity) + |> put_view(UserView) + |> render(:changeset_errors, changeset: changeset) + end + + def call(conn, {:create_tag, {:error, message}}) do + conn + |> put_status(:unprocessable_entity) + |> put_view(UserView) + |> render(:message, %{message: message}) + end + + def call(conn, {:watchlist_delete, false}) do + conn + |> put_status(:not_found) + |> put_view(UserView) + |> render(:message, %{message: "Watchlist address not found"}) + end + + def call(conn, {:tag_delete, false}) do + conn + |> put_status(:not_found) + |> put_view(UserView) + |> render(:message, %{message: "Tag not found"}) + end + + def call(conn, {:api_key_delete, false}) do + conn + |> put_status(:not_found) + |> put_view(UserView) + |> render(:message, %{message: "Api key not found"}) + end + + def call(conn, {:custom_abi_delete, false}) do + conn + |> put_status(:not_found) + |> put_view(UserView) + |> render(:message, %{message: "Custom ABI not found"}) + end + + def call(conn, {:public_tag_delete, false}) do + conn + |> put_status(:not_found) + |> put_view(UserView) + |> render(:message, %{message: "Error"}) + end + + def call(conn, {:auth, _}) do + conn + |> put_status(:unauthorized) + |> put_view(UserView) + |> render(:message, %{message: "Unauthorized"}) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/tags_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/tags_controller.ex new file mode 100644 index 0000000..91f8ab5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/tags_controller.ex @@ -0,0 +1,87 @@ +defmodule BlockScoutWeb.Account.Api.V1.TagsController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + + alias BlockScoutWeb.Models.{GetAddressTags, GetTransactionTags, UserFromAuth} + alias Explorer.Account.Identity + alias Explorer.{Chain, Repo} + alias Explorer.Chain.Hash.{Address, Full} + + action_fallback(BlockScoutWeb.Account.Api.V1.FallbackController) + + def tags_address(conn, %{"address_hash" => address_hash}) do + personal_tags = + if is_nil(current_user(conn)) do + %{personal_tags: [], watchlist_names: []} + else + uid = current_user(conn).id + + with {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:watchlist, %{watchlists: [watchlist | _]}} <- + {:watchlist, Repo.account_repo().preload(identity, :watchlists)}, + {:address_hash, {:ok, address_hash}} <- {:address_hash, Address.cast(address_hash)} do + GetAddressTags.get_address_tags(address_hash, %{id: identity.id, watchlist_id: watchlist.id}) + else + _ -> + %{personal_tags: [], watchlist_names: []} + end + end + + public_tags = + case Address.cast(address_hash) do + {:ok, address_hash} -> + GetAddressTags.get_public_tags(address_hash) + + _ -> + %{common_tags: []} + end + + conn + |> put_status(200) + |> render(:address_tags, %{tags_map: Map.merge(personal_tags, public_tags)}) + end + + def tags_transaction(conn, %{"transaction_hash" => transaction_hash}) do + transaction = + with {:ok, transaction_hash} <- Full.cast(transaction_hash), + {:ok, transaction} <- Chain.hash_to_transaction(transaction_hash) do + transaction + else + _ -> + nil + end + + personal_tags = + if is_nil(current_user(conn)) do + %{personal_tags: [], watchlist_names: [], personal_tx_tag: nil} + else + uid = current_user(conn).id + + with {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:watchlist, %{watchlists: [watchlist | _]}} <- + {:watchlist, Repo.account_repo().preload(identity, :watchlists)}, + false <- is_nil(transaction) do + GetTransactionTags.get_transaction_with_addresses_tags(transaction, %{ + id: identity.id, + watchlist_id: watchlist.id + }) + else + _ -> + %{personal_tags: [], watchlist_names: [], personal_tx_tag: nil} + end + end + + public_tags_from = + if is_nil(transaction), do: [], else: GetAddressTags.get_public_tags(transaction.from_address_hash).common_tags + + public_tags_to = + if is_nil(transaction), do: [], else: GetAddressTags.get_public_tags(transaction.to_address_hash).common_tags + + public_tags = %{common_tags: public_tags_from ++ public_tags_to} + + conn + |> put_status(200) + |> render(:transaction_tags, %{tags_map: Map.merge(personal_tags, public_tags)}) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/user_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/user_controller.ex new file mode 100644 index 0000000..22ab4e2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api/v1/user_controller.ex @@ -0,0 +1,476 @@ +defmodule BlockScoutWeb.Account.Api.V1.UserController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import Ecto.Query, only: [from: 2] + + alias BlockScoutWeb.Models.UserFromAuth + alias Explorer.Account.Api.Key, as: ApiKey + alias Explorer.Account.CustomABI + alias Explorer.Account.{Identity, PublicTagsRequest, TagAddress, TagTransaction, WatchlistAddress} + alias Explorer.ExchangeRates.Token + alias Explorer.{Market, Repo} + alias Plug.CSRFProtection + + action_fallback(BlockScoutWeb.Account.Api.V1.FallbackController) + + @ok_message "OK" + + def info(conn, _params) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)} do + conn + |> put_status(200) + |> render(:user_info, %{identity: identity}) + end + end + + def watchlist(conn, _params) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:watchlist, %{watchlists: [watchlist | _]}} <- + {:watchlist, Repo.account_repo().preload(identity, :watchlists)}, + watchlist_with_addresses <- preload_watchlist_address_fetched_coin_balance(watchlist) do + conn + |> put_status(200) + |> render(:watchlist_addresses, %{ + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + watchlist_addresses: watchlist_with_addresses.watchlist_addresses + }) + end + end + + def delete_watchlist(conn, %{"id" => watchlist_address_id}) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:watchlist, %{watchlists: [watchlist | _]}} <- + {:watchlist, Repo.account_repo().preload(identity, :watchlists)}, + {count, _} <- WatchlistAddress.delete(watchlist_address_id, watchlist.id), + {:watchlist_delete, true} <- {:watchlist_delete, count > 0} do + conn + |> put_status(200) + |> render(:message, %{message: @ok_message}) + end + end + + def create_watchlist(conn, %{ + "address_hash" => address_hash, + "name" => name, + "notification_settings" => %{ + "native" => %{ + "incoming" => watch_coin_input, + "outcoming" => watch_coin_output + }, + "ERC-20" => %{ + "incoming" => watch_erc_20_input, + "outcoming" => watch_erc_20_output + }, + "ERC-721" => %{ + "incoming" => watch_erc_721_input, + "outcoming" => watch_erc_721_output + } + # , + # "ERC-1155" => %{ + # "incoming" => watch_erc_1155_input, + # "outcoming" => watch_erc_1155_output + # } + }, + "notification_methods" => %{ + "email" => notify_email + } + }) do + watchlist_params = %{ + name: name, + watch_coin_input: watch_coin_input, + watch_coin_output: watch_coin_output, + watch_erc_20_input: watch_erc_20_input, + watch_erc_20_output: watch_erc_20_output, + watch_erc_721_input: watch_erc_721_input, + watch_erc_721_output: watch_erc_721_output, + watch_erc_1155_input: watch_erc_721_input, + watch_erc_1155_output: watch_erc_721_output, + notify_email: notify_email, + address_hash: address_hash + } + + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:watchlist, %{watchlists: [watchlist | _]}} <- + {:watchlist, Repo.account_repo().preload(identity, :watchlists)}, + {:ok, watchlist_address} <- + WatchlistAddress.create(Map.put(watchlist_params, :watchlist_id, watchlist.id)), + watchlist_address_preloaded <- WatchlistAddress.preload_address_fetched_coin_balance(watchlist_address) do + conn + |> put_status(200) + |> render(:watchlist_address, %{ + watchlist_address: watchlist_address_preloaded, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + }) + end + end + + def update_watchlist(conn, %{ + "id" => watchlist_address_id, + "address_hash" => address_hash, + "name" => name, + "notification_settings" => %{ + "native" => %{ + "incoming" => watch_coin_input, + "outcoming" => watch_coin_output + }, + "ERC-20" => %{ + "incoming" => watch_erc_20_input, + "outcoming" => watch_erc_20_output + }, + "ERC-721" => %{ + "incoming" => watch_erc_721_input, + "outcoming" => watch_erc_721_output + } + # , + # "ERC-1155" => %{ + # "incoming" => watch_erc_1155_input, + # "outcoming" => watch_erc_1155_output + # } + }, + "notification_methods" => %{ + "email" => notify_email + } + }) do + watchlist_params = %{ + id: watchlist_address_id, + name: name, + watch_coin_input: watch_coin_input, + watch_coin_output: watch_coin_output, + watch_erc_20_input: watch_erc_20_input, + watch_erc_20_output: watch_erc_20_output, + watch_erc_721_input: watch_erc_721_input, + watch_erc_721_output: watch_erc_721_output, + watch_erc_1155_input: watch_erc_721_input, + watch_erc_1155_output: watch_erc_721_output, + notify_email: notify_email, + address_hash: address_hash + } + + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:watchlist, %{watchlists: [watchlist | _]}} <- + {:watchlist, Repo.account_repo().preload(identity, :watchlists)}, + {:ok, watchlist_address} <- + WatchlistAddress.update(Map.put(watchlist_params, :watchlist_id, watchlist.id)), + watchlist_address_preloaded <- WatchlistAddress.preload_address_fetched_coin_balance(watchlist_address) do + conn + |> put_status(200) + |> render(:watchlist_address, %{ + watchlist_address: watchlist_address_preloaded, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + }) + end + end + + def tags_address(conn, _params) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + address_tags <- TagAddress.get_tags_address_by_identity_id(identity.id) do + conn + |> put_status(200) + |> render(:address_tags, %{address_tags: address_tags}) + end + end + + def delete_tag_address(conn, %{"id" => tag_id}) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {count, _} <- TagAddress.delete(tag_id, identity.id), + {:tag_delete, true} <- {:tag_delete, count > 0} do + conn + |> put_status(200) + |> render(:message, %{message: @ok_message}) + end + end + + def create_tag_address(conn, %{"address_hash" => address_hash, "name" => name}) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:ok, address_tag} <- + TagAddress.create(%{ + name: name, + address_hash: address_hash, + identity_id: identity.id + }) do + conn + |> put_status(200) + |> render(:address_tag, %{address_tag: address_tag}) + end + end + + def update_tag_address(conn, %{"id" => tag_id} = attrs) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:ok, address_tag} <- + TagAddress.update( + reject_nil_map_values(%{ + id: tag_id, + name: attrs["name"], + address_hash: attrs["address_hash"], + identity_id: identity.id + }) + ) do + conn + |> put_status(200) + |> render(:address_tag, %{address_tag: address_tag}) + end + end + + def tags_transaction(conn, _params) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + transaction_tags <- TagTransaction.get_tags_transaction_by_identity_id(identity.id) do + conn + |> put_status(200) + |> render(:transaction_tags, %{transaction_tags: transaction_tags}) + end + end + + def delete_tag_transaction(conn, %{"id" => tag_id}) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {count, _} <- TagTransaction.delete(tag_id, identity.id), + {:tag_delete, true} <- {:tag_delete, count > 0} do + conn + |> put_status(200) + |> render(:message, %{message: @ok_message}) + end + end + + def create_tag_transaction(conn, %{"transaction_hash" => tx_hash, "name" => name}) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:ok, transaction_tag} <- + TagTransaction.create(%{ + name: name, + tx_hash: tx_hash, + identity_id: identity.id + }) do + conn + |> put_status(200) + |> render(:transaction_tag, %{transaction_tag: transaction_tag}) + end + end + + def update_tag_transaction(conn, %{"id" => tag_id} = attrs) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:ok, transaction_tag} <- + TagTransaction.update( + reject_nil_map_values(%{ + id: tag_id, + name: attrs["name"], + tx_hash: attrs["transaction_hash"], + identity_id: identity.id + }) + ) do + conn + |> put_status(200) + |> render(:transaction_tag, %{transaction_tag: transaction_tag}) + end + end + + def api_keys(conn, _params) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + api_keys <- ApiKey.get_api_keys_by_identity_id(identity.id) do + conn + |> put_status(200) + |> render(:api_keys, %{api_keys: api_keys}) + end + end + + def delete_api_key(conn, %{"api_key" => api_key_uuid}) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {count, _} <- ApiKey.delete(api_key_uuid, identity.id), + {:api_key_delete, true} <- {:api_key_delete, count > 0} do + conn + |> put_status(200) + |> render(:message, %{message: @ok_message}) + end + end + + def create_api_key(conn, %{"name" => api_key_name}) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:ok, api_key} <- + ApiKey.create(%{name: api_key_name, identity_id: identity.id}) do + conn + |> put_status(200) + |> render(:api_key, %{api_key: api_key}) + end + end + + def update_api_key(conn, %{"name" => api_key_name, "api_key" => api_key_value}) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:ok, api_key} <- + ApiKey.update(%{value: api_key_value, name: api_key_name, identity_id: identity.id}) do + conn + |> put_status(200) + |> render(:api_key, %{api_key: api_key}) + end + end + + def custom_abis(conn, _params) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + custom_abis <- CustomABI.get_custom_abis_by_identity_id(identity.id) do + conn + |> put_status(200) + |> render(:custom_abis, %{custom_abis: custom_abis}) + end + end + + def delete_custom_abi(conn, %{"id" => id}) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {count, _} <- CustomABI.delete(id, identity.id), + {:custom_abi_delete, true} <- {:custom_abi_delete, count > 0} do + conn + |> put_status(200) + |> render(:message, %{message: @ok_message}) + end + end + + def create_custom_abi(conn, %{"contract_address_hash" => contract_address_hash, "name" => name, "abi" => abi}) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:ok, custom_abi} <- + CustomABI.create(%{ + name: name, + address_hash: contract_address_hash, + abi: abi, + identity_id: identity.id + }) do + conn + |> put_status(200) + |> render(:custom_abi, %{custom_abi: custom_abi}) + end + end + + def update_custom_abi( + conn, + %{ + "id" => id + } = params + ) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:ok, custom_abi} <- + CustomABI.update( + reject_nil_map_values(%{ + id: id, + name: params["name"], + address_hash: params["contract_address_hash"], + abi: params["abi"], + identity_id: identity.id + }) + ) do + conn + |> put_status(200) + |> render(:custom_abi, %{custom_abi: custom_abi}) + end + end + + def public_tags_requests(conn, _params) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + public_tags_requests <- PublicTagsRequest.get_public_tags_requests_by_identity_id(identity.id) do + conn + |> put_status(200) + |> render(:public_tags_requests, %{public_tags_requests: public_tags_requests}) + end + end + + def delete_public_tags_request(conn, %{"id" => id, "remove_reason" => remove_reason}) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:public_tag_delete, true} <- + {:public_tag_delete, + PublicTagsRequest.mark_as_deleted_public_tags_request(%{ + id: id, + identity_id: identity.id, + remove_reason: remove_reason + })} do + conn + |> put_status(200) + |> render(:message, %{message: @ok_message}) + end + end + + def create_public_tags_request(conn, params) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:ok, public_tags_request} <- + PublicTagsRequest.create(%{ + full_name: params["full_name"], + email: params["email"], + tags: params["tags"], + website: params["website"], + additional_comment: params["additional_comment"], + addresses: params["addresses"], + company: params["company"], + is_owner: params["is_owner"], + identity_id: identity.id + }) do + conn + |> put_status(200) + |> render(:public_tags_request, %{public_tags_request: public_tags_request}) + end + end + + def update_public_tags_request( + conn, + %{ + "id" => id + } = params + ) do + with {:auth, %{id: uid}} <- {:auth, current_user(conn)}, + {:identity, [%Identity{} = identity]} <- {:identity, UserFromAuth.find_identity(uid)}, + {:ok, public_tags_request} <- + PublicTagsRequest.update( + reject_nil_map_values(%{ + id: id, + full_name: params["full_name"], + email: params["email"], + tags: params["tags"], + website: params["website"], + additional_comment: params["additional_comment"], + addresses: params["addresses"], + company: params["company"], + is_owner: params["is_owner"], + identity_id: identity.id + }) + ) do + conn + |> put_status(200) + |> render(:public_tags_request, %{public_tags_request: public_tags_request}) + end + end + + def get_csrf(conn, _) do + with {:auth, %{id: _}} <- {:auth, current_user(conn)} do + conn + |> put_resp_header("x-bs-account-csrf", CSRFProtection.get_csrf_token()) + |> put_status(200) + |> render(:message, %{message: "ok"}) + end + end + + defp reject_nil_map_values(map) when is_map(map) do + Map.reject(map, fn {_k, v} -> is_nil(v) end) + end + + defp preload_watchlist_address_fetched_coin_balance(watchlist) do + watchlist + |> Repo.account_repo().preload(watchlist_addresses: from(wa in WatchlistAddress, order_by: [desc: wa.id])) + |> WatchlistAddress.preload_address_fetched_coin_balance() + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/api_key_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/api_key_controller.ex new file mode 100644 index 0000000..55c0b05 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/api_key_controller.ex @@ -0,0 +1,65 @@ +defmodule BlockScoutWeb.Account.ApiKeyController do + use BlockScoutWeb, :controller + + alias Explorer.Account.Api.Key, as: ApiKey + + import BlockScoutWeb.Account.AuthController, only: [authenticate!: 1] + + def new(conn, _params) do + authenticate!(conn) + + render(conn, "form.html", method: :create, api_key: empty_api_key()) + end + + def create(conn, %{"key" => api_key}) do + current_user = authenticate!(conn) + + case ApiKey.create(%{name: api_key["name"], identity_id: current_user.id}) do + {:ok, _} -> + redirect(conn, to: api_key_path(conn, :index)) + + {:error, invalid_api_key} -> + render(conn, "form.html", method: :create, api_key: invalid_api_key) + end + end + + def create(conn, _) do + redirect(conn, to: api_key_path(conn, :index)) + end + + def index(conn, _params) do + current_user = authenticate!(conn) + + render(conn, "index.html", api_keys: ApiKey.get_api_keys_by_identity_id(current_user.id)) + end + + def edit(conn, %{"id" => api_key}) do + current_user = authenticate!(conn) + + case ApiKey.get_api_key_by_value_and_identity_id(api_key, current_user.id) do + nil -> + not_found(conn) + + %ApiKey{} = api_key -> + render(conn, "form.html", method: :update, api_key: ApiKey.changeset(api_key)) + end + end + + def update(conn, %{"id" => api_key, "key" => %{"value" => api_key, "name" => name}}) do + current_user = authenticate!(conn) + + ApiKey.update(%{value: api_key, identity_id: current_user.id, name: name}) + + redirect(conn, to: api_key_path(conn, :index)) + end + + def delete(conn, %{"id" => api_key}) do + current_user = authenticate!(conn) + + ApiKey.delete(api_key, current_user.id) + + redirect(conn, to: api_key_path(conn, :index)) + end + + defp empty_api_key, do: ApiKey.changeset() +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/auth_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/auth_controller.ex new file mode 100644 index 0000000..78080af --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/auth_controller.ex @@ -0,0 +1,74 @@ +defmodule BlockScoutWeb.Account.AuthController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.Models.UserFromAuth + alias Explorer.Account + alias Explorer.Repo.ConfigHelper + alias Plug.CSRFProtection + + plug(Ueberauth) + + def request(conn, _) do + not_found(conn) + end + + def logout(conn, _params) do + conn + |> configure_session(drop: true) + |> redirect(to: root()) + end + + def profile(conn, _params), + do: conn |> get_session(:current_user) |> do_profile(conn) + + defp do_profile(nil, conn), + do: redirect(conn, to: root()) + + defp do_profile(%{} = user, conn), + do: render(conn, :profile, user: user) + + def callback(%{assigns: %{ueberauth_failure: _fails}} = conn, _params) do + conn + |> put_flash(:error, "Failed to authenticate.") + |> redirect(to: root()) + end + + def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do + case UserFromAuth.find_or_create(auth) do + {:ok, user} -> + CSRFProtection.get_csrf_token() + + conn + |> put_session(:current_user, user) + |> redirect(to: root()) + + {:error, reason} -> + conn + |> put_flash(:error, reason) + |> redirect(to: root()) + end + end + + def callback(conn, _) do + not_found(conn) + end + + # for importing in other controllers + def authenticate!(conn) do + current_user(conn) || redirect(conn, to: root()) + end + + def current_user(%{private: %{plug_session: %{"current_user" => _}}} = conn) do + if Account.enabled?() do + get_session(conn, :current_user) + else + nil + end + end + + def current_user(_), do: nil + + defp root do + ConfigHelper.network_path() + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/custom_abi_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/custom_abi_controller.ex new file mode 100644 index 0000000..dcd8fca --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/custom_abi_controller.ex @@ -0,0 +1,87 @@ +defmodule BlockScoutWeb.Account.CustomABIController do + use BlockScoutWeb, :controller + + alias Ecto.Changeset + alias Explorer.Account.CustomABI + + import BlockScoutWeb.Account.AuthController, only: [authenticate!: 1] + + def new(conn, _params) do + authenticate!(conn) + + render(conn, "form.html", method: :create, custom_abi: empty_custom_abi()) + end + + def create(conn, %{"custom_abi" => custom_abi}) do + current_user = authenticate!(conn) + + case CustomABI.create(%{ + name: custom_abi["name"], + address_hash: custom_abi["address_hash"], + abi: custom_abi["abi"], + identity_id: current_user.id + }) do + {:ok, _} -> + redirect(conn, to: custom_abi_path(conn, :index)) + + {:error, invalid_custom_abi} -> + render(conn, "form.html", method: :create, custom_abi: invalid_custom_abi) + end + end + + def create(conn, _) do + redirect(conn, to: custom_abi_path(conn, :index)) + end + + def index(conn, _params) do + current_user = authenticate!(conn) + + render(conn, "index.html", custom_abis: CustomABI.get_custom_abis_by_identity_id(current_user.id)) + end + + def edit(conn, %{"id" => id}) do + current_user = authenticate!(conn) + + case CustomABI.get_custom_abi_by_id_and_identity_id(id, current_user.id) do + nil -> + not_found(conn) + + %CustomABI{} = custom_abi -> + render(conn, "form.html", method: :update, custom_abi: CustomABI.changeset_without_constraints(custom_abi)) + end + end + + def update(conn, %{"id" => id, "custom_abi" => %{"abi" => abi, "name" => name, "address_hash" => address_hash}}) do + current_user = authenticate!(conn) + + case CustomABI.update(%{ + id: id, + name: name, + address_hash: address_hash, + abi: abi, + identity_id: current_user.id + }) do + {:error, %Changeset{} = custom_abi} -> + render(conn, "form.html", method: :update, custom_abi: custom_abi) + + _ -> + redirect(conn, to: custom_abi_path(conn, :index)) + end + end + + def update(conn, _) do + authenticate!(conn) + + redirect(conn, to: custom_abi_path(conn, :index)) + end + + def delete(conn, %{"id" => id}) do + current_user = authenticate!(conn) + + CustomABI.delete(id, current_user.id) + + redirect(conn, to: custom_abi_path(conn, :index)) + end + + defp empty_custom_abi, do: CustomABI.changeset_without_constraints() +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/public_tags_request_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/public_tags_request_controller.ex new file mode 100644 index 0000000..a380a2c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/public_tags_request_controller.ex @@ -0,0 +1,114 @@ +defmodule BlockScoutWeb.Account.PublicTagsRequestController do + use BlockScoutWeb, :controller + + alias Ecto.Changeset + alias Explorer.Account.PublicTagsRequest + + import BlockScoutWeb.Account.AuthController, only: [authenticate!: 1] + + def index(conn, _params) do + current_user = authenticate!(conn) + + render(conn, "index.html", + public_tags_requests: PublicTagsRequest.get_public_tags_requests_by_identity_id(current_user.id) + ) + end + + def new(conn, _params) do + current_user = authenticate!(conn) + + render(conn, "form.html", + method: :create, + public_tags_request: + PublicTagsRequest.changeset_without_constraints(%PublicTagsRequest{}, %{ + full_name: current_user.name, + email: current_user.email + }) + ) + end + + def create(conn, %{"public_tags_request" => public_tags_request}) do + current_user = authenticate!(conn) + + case PublicTagsRequest.create(%{ + full_name: public_tags_request["full_name"], + email: public_tags_request["email"], + tags: public_tags_request["tags"], + website: public_tags_request["website"], + additional_comment: public_tags_request["additional_comment"], + addresses: public_tags_request["addresses"], + company: public_tags_request["company"], + is_owner: public_tags_request["is_owner"], + identity_id: current_user.id + }) do + {:ok, _} -> + redirect(conn, to: public_tags_request_path(conn, :index)) + + {:error, invalid_public_tags_request} -> + render(conn, "form.html", method: :create, public_tags_request: invalid_public_tags_request) + end + end + + def create(conn, _) do + redirect(conn, to: public_tags_request_path(conn, :index)) + end + + def edit(conn, %{"id" => id}) do + current_user = authenticate!(conn) + + case PublicTagsRequest.get_public_tags_request_by_id_and_identity_id(id, current_user.id) do + nil -> + not_found(conn) + + %PublicTagsRequest{} = public_tags_request -> + render(conn, "form.html", + method: :update, + public_tags_request: PublicTagsRequest.changeset_without_constraints(public_tags_request) + ) + end + end + + def update(conn, %{ + "id" => id, + "public_tags_request" => public_tags_request + }) do + current_user = authenticate!(conn) + + case PublicTagsRequest.update(%{ + id: id, + full_name: public_tags_request["full_name"], + email: public_tags_request["email"], + tags: public_tags_request["tags"], + website: public_tags_request["website"], + additional_comment: public_tags_request["additional_comment"], + addresses: public_tags_request["addresses"], + company: public_tags_request["company"], + is_owner: public_tags_request["is_owner"], + identity_id: current_user.id + }) do + {:error, %Changeset{} = public_tags_request} -> + render(conn, "form.html", method: :update, public_tags_request: public_tags_request) + + _ -> + redirect(conn, to: public_tags_request_path(conn, :index)) + end + end + + def update(conn, _) do + authenticate!(conn) + + redirect(conn, to: public_tags_request_path(conn, :index)) + end + + def delete(conn, %{"id" => id, "remove_reason" => remove_reason}) do + current_user = authenticate!(conn) + + PublicTagsRequest.mark_as_deleted_public_tags_request(%{ + id: id, + identity_id: current_user.id, + remove_reason: remove_reason + }) + + redirect(conn, to: public_tags_request_path(conn, :index)) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/tag_address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/tag_address_controller.ex new file mode 100644 index 0000000..8ca30a9 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/tag_address_controller.ex @@ -0,0 +1,49 @@ +defmodule BlockScoutWeb.Account.TagAddressController do + use BlockScoutWeb, :controller + + alias Explorer.Account.TagAddress + + import BlockScoutWeb.Account.AuthController, only: [authenticate!: 1] + + def index(conn, _params) do + current_user = authenticate!(conn) + + render(conn, "index.html", address_tags: TagAddress.get_tags_address_by_identity_id(current_user.id)) + end + + def new(conn, _params) do + authenticate!(conn) + + render(conn, "form.html", tag_address: new_tag()) + end + + def create(conn, %{"tag_address" => tag_address}) do + current_user = authenticate!(conn) + + case TagAddress.create(%{ + name: tag_address["name"], + address_hash: tag_address["address_hash"], + identity_id: current_user.id + }) do + {:ok, _} -> + redirect(conn, to: tag_address_path(conn, :index)) + + {:error, invalid_tag_address} -> + render(conn, "form.html", tag_address: invalid_tag_address) + end + end + + def create(conn, _) do + redirect(conn, to: tag_address_path(conn, :index)) + end + + def delete(conn, %{"id" => id}) do + current_user = authenticate!(conn) + + TagAddress.delete(id, current_user.id) + + redirect(conn, to: tag_address_path(conn, :index)) + end + + defp new_tag, do: TagAddress.changeset() +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/tag_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/tag_transaction_controller.ex new file mode 100644 index 0000000..cba3dea --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/tag_transaction_controller.ex @@ -0,0 +1,49 @@ +defmodule BlockScoutWeb.Account.TagTransactionController do + use BlockScoutWeb, :controller + + alias Explorer.Account.TagTransaction + + import BlockScoutWeb.Account.AuthController, only: [authenticate!: 1] + + def index(conn, _params) do + current_user = authenticate!(conn) + + render(conn, "index.html", tx_tags: TagTransaction.get_tags_transaction_by_identity_id(current_user.id)) + end + + def new(conn, _params) do + authenticate!(conn) + + render(conn, "form.html", tag_transaction: new_tag()) + end + + def create(conn, %{"tag_transaction" => tag_address}) do + current_user = authenticate!(conn) + + case TagTransaction.create(%{ + name: tag_address["name"], + tx_hash: tag_address["tx_hash"], + identity_id: current_user.id + }) do + {:ok, _} -> + redirect(conn, to: tag_transaction_path(conn, :index)) + + {:error, invalid_tag_transaction} -> + render(conn, "form.html", tag_transaction: invalid_tag_transaction) + end + end + + def create(conn, _) do + redirect(conn, to: tag_transaction_path(conn, :index)) + end + + def delete(conn, %{"id" => id}) do + current_user = authenticate!(conn) + + TagTransaction.delete(id, current_user.id) + + redirect(conn, to: tag_transaction_path(conn, :index)) + end + + defp new_tag, do: TagTransaction.changeset() +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/watchlist_address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/watchlist_address_controller.ex new file mode 100644 index 0000000..5523689 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/watchlist_address_controller.ex @@ -0,0 +1,92 @@ +defmodule BlockScoutWeb.Account.WatchlistAddressController do + use BlockScoutWeb, :controller + + alias Explorer.Account.WatchlistAddress + + import BlockScoutWeb.Account.AuthController, only: [authenticate!: 1] + + def new(conn, _params) do + authenticate!(conn) + + render(conn, "form.html", method: :create, watchlist_address: empty_watchlist_address()) + end + + def create(conn, %{"watchlist_address" => wa_params}) do + current_user = authenticate!(conn) + + case WatchlistAddress.create(params_to_attributes(wa_params, current_user.watchlist_id)) do + {:ok, _watchlist_address} -> + redirect(conn, to: watchlist_path(conn, :show)) + + {:error, changeset} -> + render(conn, "form.html", method: :create, watchlist_address: changeset) + end + end + + def edit(conn, %{"id" => id}) do + current_user = authenticate!(conn) + + case WatchlistAddress.get_watchlist_address_by_id_and_watchlist_id(id, current_user.watchlist_id) do + nil -> + not_found(conn) + + %WatchlistAddress{} = watchlist_address -> + render(conn, "form.html", method: :update, watchlist_address: WatchlistAddress.changeset(watchlist_address)) + end + end + + def update(conn, %{"id" => id, "watchlist_address" => wa_params}) do + current_user = authenticate!(conn) + + case wa_params + |> params_to_attributes(current_user.watchlist_id) + |> Map.put(:id, id) + |> WatchlistAddress.update() do + {:ok, _watchlist_address} -> + redirect(conn, to: watchlist_path(conn, :show)) + + {:error, changeset} -> + render(conn, "form.html", method: :update, watchlist_address: changeset) + end + end + + def delete(conn, %{"id" => id}) do + current_user = authenticate!(conn) + + WatchlistAddress.delete(id, current_user.watchlist_id) + + redirect(conn, to: watchlist_path(conn, :show)) + end + + defp empty_watchlist_address, do: WatchlistAddress.changeset() + + defp params_to_attributes( + %{ + "address_hash" => address_hash, + "name" => name, + "watch_coin_input" => watch_coin_input, + "watch_coin_output" => watch_coin_output, + "watch_erc_20_input" => watch_erc_20_input, + "watch_erc_20_output" => watch_erc_20_output, + "watch_erc_721_input" => watch_nft_input, + "watch_erc_721_output" => watch_nft_output, + "notify_email" => notify_email + }, + watchlist_id + ) do + %{ + address_hash: address_hash, + name: name, + watch_coin_input: watch_coin_input, + watch_coin_output: watch_coin_output, + watch_erc_20_input: watch_erc_20_input, + watch_erc_20_output: watch_erc_20_output, + watch_erc_721_input: watch_nft_input, + watch_erc_721_output: watch_nft_output, + watch_erc_1155_input: watch_nft_input, + watch_erc_1155_output: watch_nft_output, + notify_email: notify_email, + watchlist_id: watchlist_id + } + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/account/watchlist_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/account/watchlist_controller.ex new file mode 100644 index 0000000..f8c5483 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/account/watchlist_controller.ex @@ -0,0 +1,26 @@ +defmodule BlockScoutWeb.Account.WatchlistController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [authenticate!: 1] + import Ecto.Query, only: [from: 2] + + alias Explorer.Account.{Watchlist, WatchlistAddress} + alias Explorer.Repo + + def show(conn, _params) do + current_user = authenticate!(conn) + + render( + conn, + "show.html", + watchlist: watchlist_with_addresses(current_user) + ) + end + + defp watchlist_with_addresses(user) do + Watchlist + |> Repo.account_repo().get(user.watchlist_id) + |> Repo.account_repo().preload(watchlist_addresses: from(wa in WatchlistAddress, order_by: [desc: wa.id])) + |> WatchlistAddress.preload_address_fetched_coin_balance() + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_by_day_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_by_day_controller.ex new file mode 100644 index 0000000..030f80a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_by_day_controller.ex @@ -0,0 +1,27 @@ +defmodule BlockScoutWeb.AddressCoinBalanceByDayController do + @moduledoc """ + Manages the grouping by day of the coin balance history of an address + """ + + use BlockScoutWeb, :controller + + alias BlockScoutWeb.AccessHelpers + alias Explorer.Chain + + def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + balances_by_day = + address_hash + |> Chain.address_to_balances_by_day() + |> Enum.map(fn %{value: value} = map -> + Map.put(map, :value, Decimal.to_float(value)) + end) + + json(conn, balances_by_day) + else + _ -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex new file mode 100644 index 0000000..6f8f220 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex @@ -0,0 +1,119 @@ +defmodule BlockScoutWeb.AddressCoinBalanceController do + @moduledoc """ + Manages the displaying of information about the coin balance history of an address + """ + + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, AddressCoinBalanceView, Controller} + alias Explorer.{Chain, Market} + alias Explorer.Chain.{Address, Wei} + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + alias Phoenix.View + + def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + :ok <- Chain.check_address_exists(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + full_options = paging_options(params) + + coin_balances_plus_one = Chain.address_to_coin_balances(address_hash, full_options) + + {coin_balances, next_page} = split_list_by_page(coin_balances_plus_one) + + next_page_url = + case next_page_params(next_page, coin_balances, params) do + nil -> + nil + + next_page_params -> + address_coin_balance_path( + conn, + :index, + address_hash, + Map.delete(next_page_params, "type") + ) + end + + coin_balances_json = + Enum.map(coin_balances, fn coin_balance -> + View.render_to_string( + AddressCoinBalanceView, + "_coin_balances.html", + conn: conn, + coin_balance: coin_balance + ) + end) + + json(conn, %{items: coin_balances_json, next_page_path: next_page_url}) + else + {:restricted_access, _} -> + not_found(conn) + + :not_found -> + case Chain.Hash.Address.validate(address_hash_string) do + {:ok, _} -> + json(conn, %{items: [], next_page_path: ""}) + + _ -> + not_found(conn) + end + + :error -> + unprocessable_entity(conn) + end + end + + def index(conn, %{"address_id" => address_hash_string} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render(conn, "index.html", + address: address, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + current_path: Controller.current_full_path(conn), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + {:restricted_access, _} -> + not_found(conn) + + {:error, :not_found} -> + {:ok, address_hash} = Chain.string_to_address_hash(address_hash_string) + + address = %Chain.Address{ + hash: address_hash, + smart_contract: nil, + token: nil, + fetched_coin_balance: %Wei{value: Decimal.new(0)} + } + + case Chain.Hash.Address.validate(address_hash_string) do + {:ok, _} -> + render( + conn, + "index.html", + address: address, + coin_balance_status: nil, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + current_path: Controller.current_full_path(conn), + tags: get_address_tags(address_hash, current_user(conn)) + ) + + _ -> + not_found(conn) + end + + :error -> + unprocessable_entity(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex new file mode 100644 index 0000000..ffd65b2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex @@ -0,0 +1,49 @@ +# credo:disable-for-this-file +defmodule BlockScoutWeb.AddressContractController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.AccessHelpers + alias BlockScoutWeb.AddressContractVerificationController, as: VerificationController + alias Explorer.{Chain, Market} + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + + def index(conn, %{"address_id" => address_hash_string} = params) do + address_options = [ + necessity_by_association: %{ + :contracts_creation_internal_transaction => :optional, + :names => :optional, + :smart_contract => :optional, + :token => :optional, + :contracts_creation_transaction => :optional + } + ] + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params), + _ <- VerificationController.check_and_verify(address_hash_string), + {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do + render( + conn, + "index.html", + address: address, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + not_found(conn) + + {:error, :not_found} -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex new file mode 100644 index 0000000..7a56e64 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex @@ -0,0 +1,309 @@ +defmodule BlockScoutWeb.AddressContractVerificationController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.API.RPC.ContractController + alias BlockScoutWeb.Controller + alias Ecto.Changeset + alias Explorer.Chain + alias Explorer.Chain.Events.Publisher, as: EventsPublisher + alias Explorer.Chain.SmartContract + alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler} + alias Explorer.SmartContract.Solidity.PublisherWorker, as: SolidityPublisherWorker + alias Explorer.SmartContract.Vyper.PublisherWorker, as: VyperPublisherWorker + alias Explorer.ThirdPartyIntegrations.Sourcify + + def new(conn, %{"address_id" => address_hash_string}) do + if Chain.smart_contract_fully_verified?(address_hash_string) do + address_contract_path = + conn + |> address_contract_path(:index, address_hash_string) + |> Controller.full_path() + + redirect(conn, to: address_contract_path) + else + changeset = + SmartContract.changeset( + %SmartContract{address_hash: address_hash_string}, + %{} + ) + + compiler_versions = + case CompilerVersion.fetch_versions(:solc) do + {:ok, compiler_versions} -> + compiler_versions + + {:error, _} -> + [] + end + + render(conn, "new.html", + changeset: changeset, + compiler_versions: compiler_versions, + evm_versions: CodeCompiler.allowed_evm_versions(), + address_hash: address_hash_string + ) + end + end + + def create( + conn, + %{ + "smart_contract" => smart_contract, + "external_libraries" => external_libraries, + "file" => files, + "verification_type" => "multi-part-files" + } + ) do + files_array = + files + |> Map.values() + |> read_files() + + Que.add(SolidityPublisherWorker, {"multipart", smart_contract, files_array, external_libraries, conn}) + + send_resp(conn, 204, "") + end + + def create( + conn, + %{ + "smart_contract" => smart_contract, + "external_libraries" => external_libraries + } + ) do + Que.add(SolidityPublisherWorker, {"flattened", smart_contract, external_libraries, conn}) + + send_resp(conn, 204, "") + end + + # sobelow_skip ["Traversal.FileModule"] + def create( + conn, + %{ + "smart_contract" => smart_contract, + "file" => files, + "verification_type" => "json:standard" + } + ) do + files_array = prepare_files_array(files) + + with %Plug.Upload{path: path} <- get_one_json(files_array), + {:ok, json_input} <- File.read(path) do + Que.add(SolidityPublisherWorker, {"json_web", smart_contract, json_input, conn}) + else + _ -> + nil + end + + send_resp(conn, 204, "") + end + + def create( + conn, + %{ + "smart_contract" => smart_contract, + "verification_type" => "vyper" + } + ) do + Que.add(VyperPublisherWorker, {smart_contract["address_hash"], smart_contract, conn}) + + send_resp(conn, 204, "") + end + + def create( + conn, + %{ + "address_hash" => address_hash_string, + "file" => files, + "verification_type" => "json:metadata" + } + ) do + files_array = prepare_files_array(files) + + json_file = get_one_json(files_array) + + if json_file do + if Chain.smart_contract_fully_verified?(address_hash_string) do + EventsPublisher.broadcast( + prepare_verification_error( + "This contract already verified in Blockscout.", + address_hash_string, + conn + ), + :on_demand + ) + else + case Sourcify.check_by_address(address_hash_string) do + {:ok, _verified_status} -> + get_metadata_and_publish(address_hash_string, conn) + + _ -> + verify_and_publish(address_hash_string, files_array, conn) + end + end + else + EventsPublisher.broadcast( + prepare_verification_error( + "Please attach JSON file with metadata of contract's compilation.", + address_hash_string, + conn + ), + :on_demand + ) + end + + send_resp(conn, 204, "") + end + + def create(conn, _params) do + Que.add(SolidityPublisherWorker, {"", %{}, %{}, conn}) + + send_resp(conn, 204, "") + end + + defp verify_and_publish(address_hash_string, files_array, conn) do + with {:ok, _verified_status} <- Sourcify.verify(address_hash_string, files_array), + {:ok, _verified_status} <- Sourcify.check_by_address(address_hash_string) do + get_metadata_and_publish(address_hash_string, conn) + else + {:error, "partial"} -> + {:ok, status, metadata} = Sourcify.check_by_address_any(address_hash_string) + process_metadata_and_publish(address_hash_string, metadata, status == "partial", conn) + + {:error, %{"error" => error}} -> + EventsPublisher.broadcast( + prepare_verification_error(error, address_hash_string, conn), + :on_demand + ) + + {:error, error} -> + EventsPublisher.broadcast( + prepare_verification_error(error, address_hash_string, conn), + :on_demand + ) + + _ -> + EventsPublisher.broadcast( + prepare_verification_error("Unexpected error", address_hash_string, conn), + :on_demand + ) + end + end + + def get_metadata_and_publish(address_hash_string, conn) do + case Sourcify.get_metadata(address_hash_string) do + {:ok, verification_metadata} -> + process_metadata_and_publish(address_hash_string, verification_metadata, false, conn) + + {:error, %{"error" => error}} -> + return_sourcify_error(conn, error, address_hash_string) + end + end + + defp process_metadata_and_publish(address_hash_string, verification_metadata, is_partial, conn \\ nil) do + case Sourcify.parse_params_from_sourcify(address_hash_string, verification_metadata) do + %{ + "params_to_publish" => params_to_publish, + "abi" => abi, + "secondary_sources" => secondary_sources, + "compilation_target_file_path" => compilation_target_file_path + } -> + ContractController.publish(conn, %{ + "addressHash" => address_hash_string, + "params" => Map.put(params_to_publish, "partially_verified", is_partial), + "abi" => abi, + "secondarySources" => secondary_sources, + "compilationTargetFilePath" => compilation_target_file_path + }) + + {:error, :metadata} -> + return_sourcify_error(conn, Sourcify.no_metadata_message(), address_hash_string) + + _ -> + return_sourcify_error(conn, Sourcify.failed_verification_message(), address_hash_string) + end + end + + defp return_sourcify_error(nil, error, _address_hash_string) do + {:error, error: error} + end + + defp return_sourcify_error(conn, error, address_hash_string) do + EventsPublisher.broadcast( + prepare_verification_error(error, address_hash_string, conn), + :on_demand + ) + end + + def prepare_files_array(files) do + if is_map(files), do: Enum.map(files, fn {_, file} -> file end), else: [] + end + + defp get_one_json(files_array) do + files_array + |> Enum.filter(fn file -> file.content_type == "application/json" end) + |> Enum.at(0) + end + + # sobelow_skip ["Traversal.FileModule"] + defp read_files(plug_uploads) do + Enum.reduce(plug_uploads, %{}, fn %Plug.Upload{path: path, filename: file_name}, acc -> + {:ok, file_content} = File.read(path) + Map.put(acc, file_name, file_content) + end) + end + + defp prepare_verification_error(msg, address_hash_string, conn) do + [ + {:contract_verification_result, + {address_hash_string, + {:error, + %Changeset{ + action: :insert, + errors: [ + file: {msg, []} + ], + data: %SmartContract{address_hash: address_hash_string}, + valid?: false + }}, conn}} + ] + end + + def parse_optimization_runs(%{"runs" => runs}) do + case Integer.parse(runs) do + {integer, ""} -> integer + _ -> 200 + end + end + + def check_and_verify(address_hash_string) do + if Chain.smart_contract_fully_verified?(address_hash_string) do + {:ok, :already_fully_verified} + else + if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do + if Chain.smart_contract_verified?(address_hash_string) do + case Sourcify.check_by_address(address_hash_string) do + {:ok, _verified_status} -> + get_metadata_and_publish(address_hash_string, nil) + + _ -> + {:error, :not_verified} + end + else + case Sourcify.check_by_address_any(address_hash_string) do + {:ok, "full", metadata} -> + process_metadata_and_publish(address_hash_string, metadata, false) + + {:ok, "partial", metadata} -> + process_metadata_and_publish(address_hash_string, metadata, true) + + _ -> + {:error, :not_verified} + end + end + else + {:error, :sourcify_disabled} + end + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex new file mode 100644 index 0000000..632c057 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex @@ -0,0 +1,60 @@ +defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.Controller + alias Explorer.Chain + alias Explorer.Chain.SmartContract + alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler, Solidity.PublisherWorker} + + def new(conn, %{"address_id" => address_hash_string}) do + if Chain.smart_contract_fully_verified?(address_hash_string) do + address_contract_path = + conn + |> address_contract_path(:index, address_hash_string) + |> Controller.full_path() + + redirect(conn, to: address_contract_path) + else + changeset = + SmartContract.changeset( + %SmartContract{address_hash: address_hash_string}, + %{} + ) + + compiler_versions = + case CompilerVersion.fetch_versions(:solc) do + {:ok, compiler_versions} -> + compiler_versions + + {:error, _} -> + [] + end + + render(conn, "new.html", + changeset: changeset, + compiler_versions: compiler_versions, + evm_versions: CodeCompiler.allowed_evm_versions(), + address_hash: address_hash_string + ) + end + end + + def create( + conn, + %{ + "smart_contract" => smart_contract, + "external_libraries" => external_libraries + } + ) do + Que.add(PublisherWorker, {"flattened", smart_contract, external_libraries, conn}) + + send_resp(conn, 204, "") + end + + def parse_optimization_runs(%{"runs" => runs}) do + case Integer.parse(runs) do + {integer, ""} -> integer + _ -> 200 + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_json_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_json_controller.ex new file mode 100644 index 0000000..4b29a66 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_json_controller.ex @@ -0,0 +1,35 @@ +defmodule BlockScoutWeb.AddressContractVerificationViaJsonController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.AddressContractVerificationController, as: VerificationController + alias BlockScoutWeb.Controller + alias Explorer.Chain + alias Explorer.Chain.SmartContract + alias Explorer.ThirdPartyIntegrations.Sourcify + + def new(conn, %{"address_id" => address_hash_string}) do + address_contract_path = + conn + |> address_contract_path(:index, address_hash_string) + |> Controller.full_path() + + if Chain.smart_contract_fully_verified?(address_hash_string) do + redirect(conn, to: address_contract_path) + else + case Sourcify.check_by_address(address_hash_string) do + {:ok, _verified_status} -> + VerificationController.get_metadata_and_publish(address_hash_string, conn) + redirect(conn, to: address_contract_path) + + _ -> + changeset = + SmartContract.changeset( + %SmartContract{address_hash: address_hash_string}, + %{} + ) + + render(conn, "new.html", changeset: changeset, address_hash: address_hash_string) + end + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_multi_part_files_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_multi_part_files_controller.ex new file mode 100644 index 0000000..cdc6d44 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_multi_part_files_controller.ex @@ -0,0 +1,41 @@ +defmodule BlockScoutWeb.AddressContractVerificationViaMultiPartFilesController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.Controller + alias Explorer.Chain + alias Explorer.Chain.SmartContract + alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler} + + def new(conn, %{"address_id" => address_hash_string}) do + if Chain.smart_contract_fully_verified?(address_hash_string) do + address_contract_path = + conn + |> address_contract_path(:index, address_hash_string) + |> Controller.full_path() + + redirect(conn, to: address_contract_path) + else + changeset = + SmartContract.changeset( + %SmartContract{address_hash: address_hash_string}, + %{} + ) + + compiler_versions = + case CompilerVersion.fetch_versions(:solc) do + {:ok, compiler_versions} -> + compiler_versions + + {:error, _} -> + [] + end + + render(conn, "new.html", + changeset: changeset, + address_hash: address_hash_string, + evm_versions: CodeCompiler.allowed_evm_versions(), + compiler_versions: compiler_versions + ) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_standard_json_input_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_standard_json_input_controller.ex new file mode 100644 index 0000000..1938269 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_standard_json_input_controller.ex @@ -0,0 +1,40 @@ +defmodule BlockScoutWeb.AddressContractVerificationViaStandardJsonInputController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.Controller + alias Explorer.Chain + alias Explorer.Chain.SmartContract + alias Explorer.SmartContract.CompilerVersion + + def new(conn, %{"address_id" => address_hash_string}) do + if Chain.smart_contract_fully_verified?(address_hash_string) do + address_contract_path = + conn + |> address_contract_path(:index, address_hash_string) + |> Controller.full_path() + + redirect(conn, to: address_contract_path) + else + changeset = + SmartContract.changeset( + %SmartContract{address_hash: address_hash_string}, + %{} + ) + + compiler_versions = + case CompilerVersion.fetch_versions(:solc) do + {:ok, compiler_versions} -> + compiler_versions + + {:error, _} -> + [] + end + + render(conn, "new.html", + changeset: changeset, + address_hash: address_hash_string, + compiler_versions: compiler_versions + ) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_vyper_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_vyper_controller.ex new file mode 100644 index 0000000..ede3bc4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_vyper_controller.ex @@ -0,0 +1,58 @@ +defmodule BlockScoutWeb.AddressContractVerificationVyperController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.Controller + alias Explorer.Chain + alias Explorer.Chain.SmartContract + alias Explorer.SmartContract.{CompilerVersion, Vyper.PublisherWorker} + + def new(conn, %{"address_id" => address_hash_string}) do + if Chain.smart_contract_fully_verified?(address_hash_string) do + address_contract_path = + conn + |> address_contract_path(:index, address_hash_string) + |> Controller.full_path() + + redirect(conn, to: address_contract_path) + else + changeset = + SmartContract.changeset( + %SmartContract{address_hash: address_hash_string}, + %{} + ) + + compiler_versions = + case CompilerVersion.fetch_versions(:vyper) do + {:ok, compiler_versions} -> + compiler_versions + + {:error, _} -> + [] + end + + render(conn, "new.html", + changeset: changeset, + compiler_versions: compiler_versions, + address_hash: address_hash_string + ) + end + end + + def create( + conn, + %{ + "smart_contract" => smart_contract + } + ) do + Que.add(PublisherWorker, {smart_contract["address_hash"], smart_contract, conn}) + + send_resp(conn, 204, "") + end + + def parse_optimization_runs(%{"runs" => runs}) do + case Integer.parse(runs) do + {integer, ""} -> integer + _ -> 200 + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex new file mode 100644 index 0000000..b90321d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex @@ -0,0 +1,226 @@ +defmodule BlockScoutWeb.AddressController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.{ + AccessHelpers, + AddressTransactionController, + AddressView, + Controller + } + + alias Explorer.Counters.{AddressTokenTransfersCounter, AddressTransactionsCounter, AddressTransactionsGasUsageCounter} + alias Explorer.{Chain, Market} + alias Explorer.Chain.Wei + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + alias Phoenix.View + + def index(conn, %{"type" => "JSON"} = params) do + addresses = + params + |> paging_options() + |> Chain.list_top_addresses() + + {addresses_page, next_page} = split_list_by_page(addresses) + + next_page_path = + case next_page_params(next_page, addresses_page, params) do + nil -> + nil + + next_page_params -> + address_path( + conn, + :index, + Map.delete(next_page_params, "type") + ) + end + + exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + total_supply = Chain.total_supply() + + items_count_str = Map.get(params, "items_count") + + items_count = + if items_count_str do + {items_count, _} = Integer.parse(items_count_str) + items_count + else + 0 + end + + items = + addresses_page + |> Enum.with_index(1) + |> Enum.map(fn {{address, tx_count}, index} -> + View.render_to_string( + AddressView, + "_tile.html", + address: address, + index: items_count + index, + exchange_rate: exchange_rate, + total_supply: total_supply, + tx_count: tx_count + ) + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_path + } + ) + end + + def index(conn, _params) do + total_supply = Chain.total_supply() + + render(conn, "index.html", + current_path: Controller.current_full_path(conn), + address_count: Chain.address_estimated_count(), + total_supply: total_supply + ) + end + + def show(conn, %{"id" => address_hash_string, "type" => "JSON"} = params) do + AddressTransactionController.index(conn, Map.put(params, "address_id", address_hash_string)) + end + + def show(conn, %{"id" => address_hash_string} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "_show_address_transactions.html", + address: address, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + filter: params["filter"], + counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), + current_path: Controller.current_full_path(conn), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + :error -> + unprocessable_entity(conn) + + {:restricted_access, _} -> + not_found(conn) + + {:error, :not_found} -> + {:ok, address_hash} = Chain.string_to_address_hash(address_hash_string) + + address = %Chain.Address{ + hash: address_hash, + smart_contract: nil, + token: nil, + fetched_coin_balance: %Wei{value: Decimal.new(0)} + } + + case Chain.Hash.Address.validate(address_hash_string) do + {:ok, _} -> + render( + conn, + "_show_address_transactions.html", + address: address, + coin_balance_status: nil, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + filter: params["filter"], + counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), + current_path: Controller.current_full_path(conn), + tags: get_address_tags(address_hash, current_user(conn)) + ) + + _ -> + not_found(conn) + end + end + end + + def address_counters(conn, %{"id" => address_hash_string}) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash) do + {validation_count} = address_counters(address) + + transactions_from_db = address.transactions_count || 0 + token_transfers_from_db = address.token_transfers_count || 0 + address_gas_usage_from_db = address.gas_used || 0 + + json(conn, %{ + transaction_count: transactions_from_db, + token_transfer_count: token_transfers_from_db, + gas_usage_count: address_gas_usage_from_db, + validation_count: validation_count + }) + else + _ -> + json(conn, %{ + transaction_count: 0, + token_transfer_count: 0, + gas_usage_count: 0, + validation_count: 0 + }) + end + end + + defp address_counters(address) do + validation_count_task = + Task.async(fn -> + validation_count(address) + end) + + Task.start_link(fn -> + transaction_count(address) + end) + + Task.start_link(fn -> + token_transfers_count(address) + end) + + Task.start_link(fn -> + gas_usage_count(address) + end) + + [ + validation_count_task + ] + |> Task.yield_many(:infinity) + |> Enum.map(fn {_task, res} -> + case res do + {:ok, result} -> + result + + {:exit, reason} -> + raise "Query fetching address counters terminated: #{inspect(reason)}" + + nil -> + raise "Query fetching address counters timed out." + end + end) + |> List.to_tuple() + end + + def transaction_count(address) do + AddressTransactionsCounter.fetch(address) + end + + def token_transfers_count(address) do + AddressTokenTransfersCounter.fetch(address) + end + + def gas_usage_count(address) do + AddressTransactionsGasUsageCounter.fetch(address) + end + + defp validation_count(address) do + Chain.address_to_validation_count(address.hash) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_decompiled_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_decompiled_contract_controller.ex new file mode 100644 index 0000000..968326c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_decompiled_contract_controller.ex @@ -0,0 +1,36 @@ +defmodule BlockScoutWeb.AddressDecompiledContractController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.AccessHelpers + alias Explorer.{Chain, Market} + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + + def index(conn, %{"address_id" => address_hash_string} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_decompiled_contract_address(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + address: address, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + not_found(conn) + + {:error, :not_found} -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex new file mode 100644 index 0000000..bfc8ce8 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex @@ -0,0 +1,130 @@ +defmodule BlockScoutWeb.AddressInternalTransactionController do + @moduledoc """ + Manages the displaying of information about internal transactions as they relate to addresses + """ + + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, Controller, InternalTransactionView} + alias Explorer.{Chain, Market} + alias Explorer.Chain.{Address, Wei} + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + alias Phoenix.View + + def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- + Chain.hash_to_address(address_hash, [necessity_by_association: %{:smart_contract => :optional}], false), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + full_options = + [ + necessity_by_association: %{ + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + } + ] + |> Keyword.merge(paging_options(params)) + |> Keyword.merge(current_filter(params)) + + internal_transactions_plus_one = Chain.address_to_internal_transactions(address_hash, full_options) + {internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one) + + next_page_path = + case next_page_params(next_page, internal_transactions, params) do + nil -> + nil + + next_page_params -> + address_internal_transaction_path(conn, :index, address_hash, Map.delete(next_page_params, "type")) + end + + internal_transactions_json = + Enum.map(internal_transactions, fn internal_transaction -> + View.render_to_string( + InternalTransactionView, + "_tile.html", + current_address: address, + internal_transaction: internal_transaction + ) + end) + + json(conn, %{items: internal_transactions_json, next_page_path: next_page_path}) + else + {:restricted_access, _} -> + not_found(conn) + + {:error, :not_found} -> + case Chain.Hash.Address.validate(address_hash_string) do + {:ok, _} -> + json(conn, %{items: [], next_page_path: ""}) + + _ -> + not_found(conn) + end + + :error -> + not_found(conn) + end + end + + def index(conn, %{"address_id" => address_hash_string} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + address: address, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + current_path: Controller.current_full_path(conn), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + filter: params["filter"], + counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + {:restricted_access, _} -> + not_found(conn) + + {:error, :not_found} -> + {:ok, address_hash} = Chain.string_to_address_hash(address_hash_string) + + address = %Chain.Address{ + hash: address_hash, + smart_contract: nil, + token: nil, + fetched_coin_balance: %Wei{value: Decimal.new(0)} + } + + case Chain.Hash.Address.validate(address_hash_string) do + {:ok, _} -> + render( + conn, + "index.html", + address: address, + filter: params["filter"], + coin_balance_status: nil, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + current_path: Controller.current_full_path(conn), + tags: get_address_tags(address_hash, current_user(conn)) + ) + + _ -> + not_found(conn) + end + + :error -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex new file mode 100644 index 0000000..742c58c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex @@ -0,0 +1,125 @@ +defmodule BlockScoutWeb.AddressLogsController do + @moduledoc """ + Manages events logs tab. + """ + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, AddressLogsView, Controller} + alias Explorer.{Chain, Market} + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + alias Phoenix.View + + use BlockScoutWeb, :controller + + def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + :ok <- Chain.check_address_exists(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + logs_plus_one = Chain.address_to_logs(address_hash, paging_options(params)) + {results, next_page} = split_list_by_page(logs_plus_one) + + next_page_url = + case next_page_params(next_page, results, params) do + nil -> + nil + + next_page_params -> + address_logs_path(conn, :index, address_hash, Map.delete(next_page_params, "type")) + end + + items = + results + |> Enum.map(fn log -> + View.render_to_string( + AddressLogsView, + "_logs.html", + log: log, + conn: conn + ) + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_url + } + ) + else + _ -> + not_found(conn) + end + end + + def index(conn, %{"address_id" => address_hash_string} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + address: address, + current_path: Controller.current_full_path(conn), + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + _ -> + not_found(conn) + end + end + + def search_logs(conn, %{"topic" => topic, "address_id" => address_hash_string} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + :ok <- Chain.check_address_exists(address_hash) do + topic = String.trim(topic) + + formatted_topic = if String.starts_with?(topic, "0x"), do: topic, else: "0x" <> topic + + logs_plus_one = Chain.address_to_logs(address_hash, topic: formatted_topic) + + {results, next_page} = split_list_by_page(logs_plus_one) + + next_page_url = + case next_page_params(next_page, results, params) do + nil -> + nil + + next_page_params -> + address_logs_path(conn, :index, address_hash, Map.delete(next_page_params, "type")) + end + + items = + results + |> Enum.map(fn log -> + View.render_to_string( + AddressLogsView, + "_logs.html", + log: log, + conn: conn + ) + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_url + } + ) + else + _ -> + not_found(conn) + end + end + + def search_logs(conn, _), do: not_found(conn) +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex new file mode 100644 index 0000000..2751e69 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex @@ -0,0 +1,92 @@ +# credo:disable-for-this-file +# +# When moving the calls to ajax, this controller became very similar to the +# `address_contract_controller`, but both are necessary until we are able to +# address a better way to organize the controllers. +# +# So, for now, I'm adding this comment to disable the credo check for this file. +defmodule BlockScoutWeb.AddressReadContractController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.AccessHelpers + alias BlockScoutWeb.AddressView + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Explorer.ExchangeRates.Token + alias Explorer.SmartContract.Reader + alias Indexer.Fetcher.CoinBalanceOnDemand + + def index(conn, %{"address_id" => address_hash_string} = params) do + address_options = [ + necessity_by_association: %{ + :contracts_creation_internal_transaction => :optional, + :names => :optional, + :smart_contract => :optional, + :token => :optional, + :contracts_creation_transaction => :optional + } + ] + + custom_abi = AddressView.fetch_custom_abi(conn, address_hash_string) + custom_abi? = AddressView.check_custom_abi_for_having_read_functions(custom_abi) + + need_wallet_custom_abi? = + !is_nil(custom_abi) && Reader.read_functions_required_wallet_from_abi(custom_abi.abi) != [] + + base_params = [ + type: :regular, + action: :read, + custom_abi: custom_abi?, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + ] + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true), + false <- is_nil(address.smart_contract), + need_wallet? <- Reader.read_functions_required_wallet_from_abi(address.smart_contract.abi) != [], + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + base_params ++ + [ + address: address, + non_custom_abi: true, + need_wallet: need_wallet? || need_wallet_custom_abi?, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ] + ) + else + _ -> + if custom_abi? do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_contract_address(address_hash, address_options, false), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + base_params ++ + [ + address: address, + non_custom_abi: false, + need_wallet: need_wallet_custom_abi?, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ] + ) + else + _ -> + not_found(conn) + end + else + not_found(conn) + end + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_read_proxy_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_read_proxy_controller.ex new file mode 100644 index 0000000..564655a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_read_proxy_controller.ex @@ -0,0 +1,45 @@ +# credo:disable-for-this-file +defmodule BlockScoutWeb.AddressReadProxyController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.AccessHelpers + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + + def index(conn, %{"address_id" => address_hash_string} = params) do + address_options = [ + necessity_by_association: %{ + :contracts_creation_internal_transaction => :optional, + :names => :optional, + :smart_contract => :optional, + :token => :optional, + :contracts_creation_transaction => :optional + } + ] + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true), + false <- is_nil(address.smart_contract), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + address: address, + type: :proxy, + action: :read, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + _ -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex new file mode 100644 index 0000000..9c7ab89 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_balance_controller.ex @@ -0,0 +1,50 @@ +defmodule BlockScoutWeb.AddressTokenBalanceController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.AccessHelpers + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Indexer.Fetcher.TokenBalanceOnDemand + + def index(conn, %{"address_id" => address_hash_string} = params) do + with true <- ajax?(conn), + {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string) do + token_balances = + address_hash + |> Chain.fetch_last_token_balances() + + Task.start_link(fn -> + TokenBalanceOnDemand.trigger_fetch(address_hash, token_balances) + end) + + token_balances_with_price = + token_balances + |> Market.add_price() + + case AccessHelpers.restricted_access?(address_hash_string, params) do + {:ok, false} -> + conn + |> put_status(200) + |> put_layout(false) + |> render("_token_balances.html", + address_hash: Address.checksum(address_hash), + token_balances: token_balances_with_price, + conn: conn + ) + + _ -> + conn + |> put_status(200) + |> put_layout(false) + |> render("_token_balances.html", + address_hash: Address.checksum(address_hash), + token_balances: [], + conn: conn + ) + end + else + _ -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex new file mode 100644 index 0000000..5470034 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex @@ -0,0 +1,93 @@ +defmodule BlockScoutWeb.AddressTokenController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, only: [next_page_params: 3, paging_options: 1, split_list_by_page: 1] + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, AddressTokenView, Controller} + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + alias Phoenix.View + + def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash, [], false), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + token_balances_plus_one = + address_hash + |> Chain.fetch_last_token_balances(paging_options(params)) + |> Market.add_price() + + {tokens, next_page} = split_list_by_page(token_balances_plus_one) + + next_page_path = + case next_page_params(next_page, tokens, params) do + nil -> + nil + + next_page_params -> + address_token_path(conn, :index, address, Map.delete(next_page_params, "type")) + end + + items = + tokens + |> Market.add_price() + |> Enum.map(fn {token_balance, token} -> + View.render_to_string( + AddressTokenView, + "_tokens.html", + token_balance: token_balance, + token: token, + address: address, + conn: conn + ) + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_path + } + ) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + end + end + + def index(conn, %{"address_id" => address_hash_string} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + address: address, + current_path: Controller.current_full_path(conn), + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex new file mode 100644 index 0000000..eeebc32 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex @@ -0,0 +1,221 @@ +defmodule BlockScoutWeb.AddressTokenTransferController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, Controller, TransactionView} + alias Explorer.ExchangeRates.Token + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Indexer.Fetcher.CoinBalanceOnDemand + alias Phoenix.View + + import BlockScoutWeb.Chain, + only: [current_filter: 1, next_page_params: 3, paging_options: 1, split_list_by_page: 1] + + @transaction_necessity_by_association [ + necessity_by_association: %{ + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional, + [token_transfers: :token] => :optional, + [token_transfers: :to_address] => :optional, + [token_transfers: :from_address] => :optional, + [token_transfers: :token_contract_address] => :optional, + :block => :required + } + ] + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + def index( + conn, + %{ + "address_id" => address_hash_string, + "address_token_id" => token_hash_string, + "type" => "JSON" + } = params + ) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, token_hash} <- Chain.string_to_address_hash(token_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash), + {:ok, _} <- Chain.token_from_address_hash(token_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + transactions = + Chain.address_to_transactions_with_token_transfers( + address_hash, + token_hash, + paging_options(params) + ) + + {transactions_paginated, next_page} = split_list_by_page(transactions) + + next_page_path = + case next_page_params(next_page, transactions_paginated, params) do + nil -> + nil + + next_page_params -> + address_token_transfers_path( + conn, + :index, + address_hash_string, + token_hash_string, + Map.delete(next_page_params, "type") + ) + end + + transfers_json = + Enum.map(transactions_paginated, fn transaction -> + View.render_to_string( + TransactionView, + "_tile.html", + conn: conn, + transaction: transaction, + burn_address_hash: @burn_address_hash, + current_address: address + ) + end) + + json(conn, %{items: transfers_json, next_page_path: next_page_path}) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + end + end + + def index( + conn, + %{"address_id" => address_hash_string, "address_token_id" => token_hash_string} = params + ) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, token_hash} <- Chain.string_to_address_hash(token_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash), + {:ok, token} <- Chain.token_from_address_hash(token_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + address: address, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + current_path: Controller.current_full_path(conn), + token: token, + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + end + end + + def index( + conn, + %{ + "address_id" => address_hash_string, + "type" => "JSON" + } = params + ) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + options = + @transaction_necessity_by_association + |> Keyword.merge(paging_options(params)) + |> Keyword.merge(current_filter(params)) + + transactions = + Chain.address_hash_to_token_transfers( + address_hash, + options + ) + + {transactions_paginated, next_page} = split_list_by_page(transactions) + + next_page_path = + case next_page_params(next_page, transactions_paginated, params) do + nil -> + nil + + next_page_params -> + address_token_transfers_path( + conn, + :index, + address_hash_string, + Map.delete(next_page_params, "type") + ) + end + + transfers_json = + Enum.map(transactions_paginated, fn transaction -> + View.render_to_string( + TransactionView, + "_tile.html", + conn: conn, + transaction: transaction, + burn_address_hash: @burn_address_hash, + current_address: address + ) + end) + + json(conn, %{items: transfers_json, next_page_path: next_page_path}) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + end + end + + def index( + conn, + %{"address_id" => address_hash_string} = params + ) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + address: address, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + filter: params["filter"], + current_path: Controller.current_full_path(conn), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex new file mode 100644 index 0000000..679b0f6 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex @@ -0,0 +1,292 @@ +defmodule BlockScoutWeb.AddressTransactionController do + @moduledoc """ + Display all the Transactions that terminate at this Address. + """ + + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + + import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1] + + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, Controller, TransactionView} + alias Explorer.{Chain, Market} + + alias Explorer.Chain.{ + AddressInternalTransactionCsvExporter, + AddressLogCsvExporter, + AddressTokenTransferCsvExporter, + AddressTransactionCsvExporter, + Wei + } + + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + alias Phoenix.View + + alias Plug.Conn + + @transaction_necessity_by_association [ + necessity_by_association: %{ + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + :block => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + } + ] + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do + address_options = [necessity_by_association: %{:names => :optional, :smart_contract => :optional}] + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash, address_options, false), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + options = + @transaction_necessity_by_association + |> Keyword.merge(paging_options(params)) + |> Keyword.merge(current_filter(params)) + + results_plus_one = Chain.address_to_transactions_with_rewards(address_hash, options) + {results, next_page} = split_list_by_page(results_plus_one) + + next_page_url = + case next_page_params(next_page, results, params) do + nil -> + nil + + next_page_params -> + address_transaction_path( + conn, + :index, + address, + Map.delete(next_page_params, "type") + ) + end + + items_json = + Enum.map(results, fn result -> + case result do + {%Chain.Block.Reward{} = emission_reward, %Chain.Block.Reward{} = validator_reward} -> + View.render_to_string( + TransactionView, + "_emission_reward_tile.html", + current_address: address, + emission_funds: emission_reward, + validator: validator_reward + ) + + %Chain.Transaction{} = transaction -> + View.render_to_string( + TransactionView, + "_tile.html", + conn: conn, + current_address: address, + transaction: transaction, + burn_address_hash: @burn_address_hash + ) + end + end) + + json(conn, %{items: items_json, next_page_path: next_page_url}) + else + :error -> + unprocessable_entity(conn) + + {:restricted_access, _} -> + not_found(conn) + + {:error, :not_found} -> + case Chain.Hash.Address.validate(address_hash_string) do + {:ok, _} -> + json(conn, %{items: [], next_page_path: ""}) + + _ -> + not_found(conn) + end + end + end + + def index(conn, %{"address_id" => address_hash_string} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + address: address, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + filter: params["filter"], + counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), + current_path: Controller.current_full_path(conn), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + :error -> + unprocessable_entity(conn) + + {:restricted_access, _} -> + not_found(conn) + + {:error, :not_found} -> + {:ok, address_hash} = Chain.string_to_address_hash(address_hash_string) + + address = %Chain.Address{ + hash: address_hash, + smart_contract: nil, + token: nil, + fetched_coin_balance: %Wei{value: Decimal.new(0)} + } + + case Chain.Hash.Address.validate(address_hash_string) do + {:ok, _} -> + render( + conn, + "index.html", + address: address, + coin_balance_status: nil, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + filter: params["filter"], + counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), + current_path: Controller.current_full_path(conn), + tags: get_address_tags(address_hash, current_user(conn)) + ) + + _ -> + not_found(conn) + end + end + end + + defp captcha_helper do + :block_scout_web + |> Application.get_env(:captcha_helper) + end + + defp put_resp_params(conn, file_name) do + conn + |> put_resp_content_type("application/csv") + |> put_resp_header("content-disposition", "attachment; filename=#{file_name}") + |> put_resp_cookie("csv-downloaded", "true", max_age: 86_400, http_only: false) + |> send_chunked(200) + end + + defp items_csv( + conn, + %{ + "address_id" => address_hash_string, + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => recaptcha_response + }, + csv_export_module, + file_name + ) + when is_binary(address_hash_string) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.hash_to_address(address_hash), + {:recaptcha, true} <- {:recaptcha, captcha_helper().recaptcha_passed?(recaptcha_response)} do + address + |> csv_export_module.export(from_period, to_period) + |> Enum.reduce_while(put_resp_params(conn, file_name), fn chunk, conn -> + case Conn.chunk(conn, chunk) do + {:ok, conn} -> + {:cont, conn} + + {:error, :closed} -> + {:halt, conn} + end + end) + else + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + + {:recaptcha, false} -> + not_found(conn) + end + end + + defp items_csv(conn, _, _, _), do: not_found(conn) + + def token_transfers_csv(conn, params) do + items_csv( + conn, + %{ + "address_id" => params["address_id"], + "from_period" => params["from_period"], + "to_period" => params["to_period"], + "recaptcha_response" => params["recaptcha_response"] + }, + AddressTokenTransferCsvExporter, + "token_transfers.csv" + ) + end + + def transactions_csv(conn, %{ + "address_id" => address_hash_string, + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => recaptcha_response + }) do + items_csv( + conn, + %{ + "address_id" => address_hash_string, + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => recaptcha_response + }, + AddressTransactionCsvExporter, + "transactions.csv" + ) + end + + def internal_transactions_csv(conn, %{ + "address_id" => address_hash_string, + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => recaptcha_response + }) do + items_csv( + conn, + %{ + "address_id" => address_hash_string, + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => recaptcha_response + }, + AddressInternalTransactionCsvExporter, + "internal_transactions.csv" + ) + end + + def logs_csv(conn, %{ + "address_id" => address_hash_string, + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => recaptcha_response + }) do + items_csv( + conn, + %{ + "address_id" => address_hash_string, + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => recaptcha_response + }, + AddressLogCsvExporter, + "logs.csv" + ) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex new file mode 100644 index 0000000..3963c26 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex @@ -0,0 +1,100 @@ +defmodule BlockScoutWeb.AddressValidationController do + @moduledoc """ + Display all the blocks that this address validates. + """ + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + + import BlockScoutWeb.Chain, + only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, BlockView, Controller} + alias Explorer.ExchangeRates.Token + alias Explorer.{Chain, Market} + alias Indexer.Fetcher.CoinBalanceOnDemand + alias Phoenix.View + + def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, _} <- Chain.find_or_insert_address_from_hash(address_hash, [], false), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + full_options = + Keyword.merge( + [ + necessity_by_association: %{ + miner: :required, + nephews: :optional, + transactions: :optional, + rewards: :optional + } + ], + paging_options(params) + ) + + blocks_plus_one = Chain.get_blocks_validated_by_address(full_options, address_hash) + {blocks, next_page} = split_list_by_page(blocks_plus_one) + + next_page_path = + case next_page_params(next_page, blocks, params) do + nil -> + nil + + next_page_params -> + address_validation_path( + conn, + :index, + address_hash_string, + Map.delete(next_page_params, "type") + ) + end + + items = + Enum.map(blocks, fn block -> + View.render_to_string( + BlockView, + "_tile.html", + conn: conn, + block: block, + block_type: BlockView.block_type(block) + ) + end) + + json(conn, %{items: items, next_page_path: next_page_path}) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + unprocessable_entity(conn) + end + end + + def index(conn, %{"address_id" => address_hash_string} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_or_insert_address_from_hash(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + address: address, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + current_path: Controller.current_full_path(conn), + counters_path: address_path(conn, :address_counters, %{"id" => address_hash_string}), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_write_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_write_contract_controller.ex new file mode 100644 index 0000000..1accce5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_write_contract_controller.ex @@ -0,0 +1,84 @@ +# credo:disable-for-this-file +# +# When moving the calls to ajax, this controller became very similar to the +# `address_contract_controller`, but both are necessary until we are able to +# address a better way to organize the controllers. +# +# So, for now, I'm adding this comment to disable the credo check for this file. +defmodule BlockScoutWeb.AddressWriteContractController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.AccessHelpers + alias BlockScoutWeb.AddressView + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + + def index(conn, %{"address_id" => address_hash_string} = params) do + address_options = [ + necessity_by_association: %{ + :contracts_creation_internal_transaction => :optional, + :names => :optional, + :smart_contract => :optional, + :token => :optional, + :contracts_creation_transaction => :optional + } + ] + + custom_abi? = AddressView.has_address_custom_abi_with_write_functions?(conn, address_hash_string) + + base_params = [ + type: :regular, + action: :write, + custom_abi: custom_abi?, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + ] + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true), + false <- is_nil(address.smart_contract), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + base_params ++ + [ + address: address, + non_custom_abi: true, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ] + ) + else + _ -> + if custom_abi? do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_contract_address(address_hash, address_options, false), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + base_params ++ + [ + address: address, + non_custom_abi: false, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ] + ) + else + _ -> + not_found(conn) + end + else + not_found(conn) + end + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_write_proxy_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_write_proxy_controller.ex new file mode 100644 index 0000000..fc5a7c7 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_write_proxy_controller.ex @@ -0,0 +1,45 @@ +# credo:disable-for-this-file +defmodule BlockScoutWeb.AddressWriteProxyController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.AccessHelpers + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Explorer.ExchangeRates.Token + alias Indexer.Fetcher.CoinBalanceOnDemand + + def index(conn, %{"address_id" => address_hash_string} = params) do + address_options = [ + necessity_by_association: %{ + :contracts_creation_internal_transaction => :optional, + :names => :optional, + :smart_contract => :optional, + :token => :optional, + :contracts_creation_transaction => :optional + } + ] + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true), + false <- is_nil(address.smart_contract), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + address: address, + type: :proxy, + action: :write, + coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address), + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + counters_path: address_path(conn, :address_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + _ -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/admin/dashboard_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/admin/dashboard_controller.ex new file mode 100644 index 0000000..4455e0f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/admin/dashboard_controller.ex @@ -0,0 +1,7 @@ +defmodule BlockScoutWeb.Admin.DashboardController do + use BlockScoutWeb, :controller + + def index(conn, _) do + render(conn, "index.html") + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/admin/session_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/admin/session_controller.ex new file mode 100644 index 0000000..6c200e3 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/admin/session_controller.ex @@ -0,0 +1,37 @@ +defmodule BlockScoutWeb.Admin.SessionController do + use BlockScoutWeb, :controller + + alias Ecto.Changeset + alias Explorer.{Accounts, Admin} + alias Explorer.Accounts.User.Authenticate + + def new(conn, _) do + changeset = Authenticate.changeset() + render(conn, "login_form.html", changeset: changeset) + end + + def create(conn, %{"authenticate" => params}) do + with {:user, {:ok, user}} <- {:user, Accounts.authenticate(params)}, + {:admin, {:ok, _}} <- {:admin, Admin.from_user(user)} do + conn + |> put_session(:user_id, user.id) + |> redirect(to: AdminRoutes.dashboard_path(conn, :index)) + else + {:user, {:error, :invalid_credentials}} -> + changeset = Authenticate.changeset(params) + render(conn, "login_form.html", changeset: changeset) + + {:user, {:error, %Changeset{} = changeset}} -> + render(conn, "login_form.html", changeset: changeset) + + {:admin, {:error, :not_found}} -> + changeset = Authenticate.changeset() + render(conn, "login_form.html", changeset: changeset) + end + end + + def create(conn, _) do + changeset = Authenticate.changeset() + render(conn, "login_form.html", changeset: changeset) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/admin/setup_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/admin/setup_controller.ex new file mode 100644 index 0000000..9005fe3 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/admin/setup_controller.ex @@ -0,0 +1,90 @@ +defmodule BlockScoutWeb.Admin.SetupController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.AdminRouter.Helpers + + alias BlockScoutWeb.Endpoint + alias Explorer.Accounts.User.Registration + alias Explorer.Admin + alias Phoenix.Token + + @admin_registration_salt "admin-registration" + + plug(:redirect_to_login_if_configured) + + # Step 2: enter new admin credentials + def configure(conn, %{"state" => state}) do + case valid_state?(state) do + true -> + changeset = Registration.changeset() + render(conn, "admin_registration.html", changeset: changeset) + + false -> + render(conn, "verify.html") + end + end + + # Step 1: enter recovery key + def configure(conn, _) do + render(conn, "verify.html") + end + + # Step 1: verify recovery token + def configure_admin(conn, %{"verify" => %{"recovery_key" => key}}) do + if key == Admin.recovery_key() do + redirect(conn, to: setup_path(conn, :configure, %{state: generate_secure_token()})) + else + render(conn, "verify.html") + end + end + + # Step 2: register new admin + def configure_admin(conn, %{"state" => state, "registration" => registration}) do + with true <- valid_state?(state), + {:ok, %{user: user, admin: _admin}} <- Admin.register_owner(registration) do + conn + |> put_session(:user_id, user.id) + |> redirect(to: dashboard_path(conn, :index)) + else + false -> + render(conn, "verify.html") + + {:error, changeset} -> + render(conn, "admin_registration.html", changeset: changeset) + end + end + + # Step 1: enter recovery key + def configure_admin(conn, _) do + render(conn, "verify.html") + end + + @doc false + def generate_secure_token do + key = Admin.recovery_key() + Token.sign(Endpoint, @admin_registration_salt, key) + end + + defp valid_state?(state) do + # 5 minutes + max_age_in_seconds = 300 + opts = [max_age: max_age_in_seconds] + + case Token.verify(Endpoint, @admin_registration_salt, state, opts) do + {:ok, _} -> true + _ -> false + end + end + + defp redirect_to_login_if_configured(conn, _) do + case Admin.owner() do + {:ok, _} -> + conn + |> redirect(to: session_path(conn, :new)) + |> halt() + + _ -> + conn + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/admin/tasks_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/admin/tasks_controller.ex new file mode 100644 index 0000000..1e94fc5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/admin/tasks_controller.ex @@ -0,0 +1,22 @@ +defmodule BlockScoutWeb.Admin.TaskController do + use BlockScoutWeb, :controller + + require Logger + + alias Explorer.Chain.ContractMethod + + @ok_resp Poison.encode!(%{status: "success"}) + @not_ok_resp Poison.encode!(%{status: "failure"}) + + def create_contract_methods(conn, _) do + case ContractMethod.import_all() do + :ok -> + send_resp(conn, 200, Poison.encode!(@ok_resp)) + + {:error, error} -> + Logger.error(fn -> ["Something went wrong while creating contract methods: ", inspect(error)] end) + + send_resp(conn, 500, Poison.encode!(@not_ok_resp)) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/api_logger.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/api_logger.ex new file mode 100644 index 0000000..bc2d77d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/api_logger.ex @@ -0,0 +1,25 @@ +defmodule BlockScoutWeb.API.APILogger do + @moduledoc """ + Logger for API endpoints usage + """ + require Logger + + def log(conn) do + endpoint = + if conn.query_string do + "#{conn.request_path}?#{conn.query_string}" + else + conn.request_path + end + + Logger.debug(endpoint, + fetcher: :api + ) + end + + def message(text) do + Logger.debug(text, + fetcher: :api + ) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/eth_rpc/eth_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/eth_rpc/eth_controller.ex new file mode 100644 index 0000000..178cb32 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/eth_rpc/eth_controller.ex @@ -0,0 +1,63 @@ +defmodule BlockScoutWeb.API.EthRPC.EthController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.AccessHelpers + alias BlockScoutWeb.API.EthRPC.View, as: EthRPCView + alias Explorer.EthRPC + + def eth_request(%{body_params: %{"_json" => requests}} = conn, _) when is_list(requests) do + case AccessHelpers.check_rate_limit(conn) do + :ok -> + responses = EthRPC.responses(requests) + + conn + |> put_status(200) + |> put_view(EthRPCView) + |> render("responses.json", %{responses: responses}) + + :rate_limit_reached -> + AccessHelpers.handle_rate_limit_deny(conn) + end + end + + def eth_request(%{body_params: %{"_json" => request}} = conn, _) do + case AccessHelpers.check_rate_limit(conn) do + :ok -> + [response] = EthRPC.responses([request]) + + conn + |> put_status(200) + |> put_view(EthRPCView) + |> render("response.json", %{response: response}) + + :rate_limit_reached -> + AccessHelpers.handle_rate_limit_deny(conn) + end + end + + def eth_request(conn, request) do + case AccessHelpers.check_rate_limit(conn) do + :ok -> + # In the case that the JSON body is sent up w/o a json content type, + # Phoenix encodes it as a single key value pair, with the value being + # nil and the body being the key (as in a CURL request w/ no content type header) + decoded_request = + with [{single_key, nil}] <- Map.to_list(request), + {:ok, decoded} <- Jason.decode(single_key) do + decoded + else + _ -> request + end + + [response] = EthRPC.responses([decoded_request]) + + conn + |> put_status(200) + |> put_view(EthRPCView) + |> render("response.json", %{response: response}) + + :rate_limit_reached -> + AccessHelpers.handle_rate_limit_deny(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/address_controller.ex new file mode 100644 index 0000000..394c301 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/address_controller.ex @@ -0,0 +1,524 @@ +defmodule BlockScoutWeb.API.RPC.AddressController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.API.RPC.Helpers + alias Explorer.{Chain, Etherscan} + alias Explorer.Chain.{Address, Wei} + alias Explorer.Etherscan.{Addresses, Blocks} + alias Indexer.Fetcher.CoinBalanceOnDemand + + def listaccounts(conn, params) do + options = + params + |> optional_params() + |> Map.put_new(:page_number, 0) + |> Map.put_new(:page_size, 10) + + accounts = list_accounts(options) + + conn + |> put_status(200) + |> render(:listaccounts, %{accounts: accounts}) + end + + def eth_get_balance(conn, params) do + with {:address_param, {:ok, address_param}} <- fetch_address(params), + {:block_param, {:ok, block}} <- {:block_param, fetch_block_param(params)}, + {:format, {:ok, address_hash}} <- to_address_hash(address_param), + {:balance, {:ok, balance}} <- {:balance, Blocks.get_balance_as_of_block(address_hash, block)} do + render(conn, :eth_get_balance, %{balance: Wei.hex_format(balance)}) + else + {:address_param, :error} -> + conn + |> put_status(400) + |> render(:eth_get_balance_error, %{message: "Query parameter 'address' is required"}) + + {:format, :error} -> + conn + |> put_status(400) + |> render(:eth_get_balance_error, %{error: "Invalid address hash"}) + + {:block_param, :error} -> + conn + |> put_status(400) + |> render(:eth_get_balance_error, %{error: "Invalid block"}) + + {:balance, {:error, :not_found}} -> + conn + |> put_status(404) + |> render(:eth_get_balance_error, %{error: "Balance not found"}) + end + end + + def balance(conn, params, template \\ :balance) do + with {:address_param, {:ok, address_param}} <- fetch_address(params), + {:format, {:ok, address_hashes}} <- to_address_hashes(address_param) do + addresses = hashes_to_addresses(address_hashes) + render(conn, template, %{addresses: addresses}) + else + {:address_param, :error} -> + conn + |> put_status(200) + |> render(:error, error: "Query parameter 'address' is required") + + {:format, :error} -> + conn + |> put_status(200) + |> render(:error, error: "Invalid address hash") + end + end + + def balancemulti(conn, params) do + balance(conn, params, :balancemulti) + end + + def pendingtxlist(conn, params) do + options = optional_params(params) + + with {:address_param, {:ok, address_param}} <- fetch_address(params), + {:format, {:ok, address_hash}} <- to_address_hash(address_param), + {:ok, transactions} <- list_pending_transactions(address_hash, options) do + render(conn, :pendingtxlist, %{transactions: transactions}) + else + {:address_param, :error} -> + conn + |> put_status(200) + |> render(:error, error: "Query parameter 'address' is required") + + {:format, :error} -> + conn + |> put_status(200) + |> render(:error, error: "Invalid address format") + + {:error, :not_found} -> + render(conn, :error, error: "No transactions found", data: []) + end + end + + def txlist(conn, params) do + options = optional_params(params) + + with {:address_param, {:ok, address_param}} <- fetch_address(params), + {:format, {:ok, address_hash}} <- to_address_hash(address_param), + {:address, :ok} <- {:address, Chain.check_address_exists(address_hash)}, + {:ok, transactions} <- list_transactions(address_hash, options) do + render(conn, :txlist, %{transactions: transactions}) + else + {:address_param, :error} -> + conn + |> put_status(200) + |> render(:error, error: "Query parameter 'address' is required") + + {:format, :error} -> + conn + |> put_status(200) + |> render(:error, error: "Invalid address format") + + {_, :not_found} -> + render(conn, :error, error: "No transactions found", data: []) + end + end + + def txlistinternal(conn, params) do + case {Map.fetch(params, "txhash"), Map.fetch(params, "address")} do + {:error, :error} -> + render(conn, :error, error: "Query parameter txhash or address is required") + + {{:ok, txhash_param}, :error} -> + txlistinternal(conn, txhash_param, :txhash) + + {:error, {:ok, address_param}} -> + txlistinternal(conn, params, address_param, :address) + end + end + + def txlistinternal(conn, txhash_param, :txhash) do + with {:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param), + {:ok, internal_transactions} <- list_internal_transactions(transaction_hash) do + render(conn, :txlistinternal, %{internal_transactions: internal_transactions}) + else + {:format, :error} -> + render(conn, :error, error: "Invalid txhash format") + + {:error, :not_found} -> + render(conn, :error, error: "No internal transactions found", data: []) + end + end + + def txlistinternal(conn, params, address_param, :address) do + options = optional_params(params) + + with {:format, {:ok, address_hash}} <- to_address_hash(address_param), + {:address, :ok} <- {:address, Chain.check_address_exists(address_hash)}, + {:ok, internal_transactions} <- list_internal_transactions(address_hash, options) do + render(conn, :txlistinternal, %{internal_transactions: internal_transactions}) + else + {:format, :error} -> + render(conn, :error, error: "Invalid address format") + + {_, :not_found} -> + render(conn, :error, error: "No internal transactions found", data: []) + end + end + + def tokentx(conn, params) do + options = optional_params(params) + + with {:address_param, {:ok, address_param}} <- fetch_address(params), + {:format, {:ok, address_hash}} <- to_address_hash(address_param), + {:contract_address, {:ok, contract_address_hash}} <- to_contract_address_hash(params["contractaddress"]), + {:address, :ok} <- {:address, Chain.check_address_exists(address_hash)}, + {:ok, token_transfers} <- list_token_transfers(address_hash, contract_address_hash, options) do + render(conn, :tokentx, %{token_transfers: token_transfers}) + else + {:address_param, :error} -> + render(conn, :error, error: "Query parameter address is required") + + {:format, :error} -> + render(conn, :error, error: "Invalid address format") + + {:contract_address, :error} -> + render(conn, :error, error: "Invalid contract address format") + + {_, :not_found} -> + render(conn, :error, error: "No token transfers found", data: []) + end + end + + @tokenbalance_required_params ~w(contractaddress address) + + def tokenbalance(conn, params) do + with {:required_params, {:ok, fetched_params}} <- fetch_required_params(params, @tokenbalance_required_params), + {:format, {:ok, validated_params}} <- to_valid_format(fetched_params, :tokenbalance) do + token_balance = get_token_balance(validated_params) + render(conn, "tokenbalance.json", %{token_balance: token_balance}) + else + {:required_params, {:error, missing_params}} -> + error = "Required query parameters missing: #{Enum.join(missing_params, ", ")}" + render(conn, :error, error: error) + + {:format, {:error, param}} -> + render(conn, :error, error: "Invalid #{param} format") + end + end + + def tokenlist(conn, params) do + with {:address_param, {:ok, address_param}} <- fetch_address(params), + {:format, {:ok, address_hash}} <- to_address_hash(address_param), + {:address, :ok} <- {:address, Chain.check_address_exists(address_hash)}, + {:ok, token_list} <- list_tokens(address_hash) do + render(conn, :token_list, %{token_list: token_list}) + else + {:address_param, :error} -> + render(conn, :error, error: "Query parameter address is required") + + {:format, :error} -> + render(conn, :error, error: "Invalid address format") + + {_, :not_found} -> + render(conn, :error, error: "No tokens found", data: []) + end + end + + def getminedblocks(conn, params) do + options = Helpers.put_pagination_options(%{}, params) + + with {:address_param, {:ok, address_param}} <- fetch_address(params), + {:format, {:ok, address_hash}} <- to_address_hash(address_param), + {:address, :ok} <- {:address, Chain.check_address_exists(address_hash)}, + {:ok, blocks} <- list_blocks(address_hash, options) do + render(conn, :getminedblocks, %{blocks: blocks}) + else + {:address_param, :error} -> + render(conn, :error, error: "Query parameter 'address' is required") + + {:format, :error} -> + render(conn, :error, error: "Invalid address format") + + {_, :not_found} -> + render(conn, :error, error: "No blocks found", data: []) + end + end + + @doc """ + Sanitizes optional params. + + """ + @spec optional_params(map()) :: map() + def optional_params(params) do + %{} + |> put_order_by_direction(params) + |> Helpers.put_pagination_options(params) + |> put_block(params, "start_block") + |> put_block(params, "end_block") + |> put_filter_by(params) + |> put_timestamp(params, "start_timestamp") + |> put_timestamp(params, "end_timestamp") + end + + @doc """ + Fetches required params. Returns error tuple if required params are missing. + + """ + @spec fetch_required_params(map(), list()) :: {:required_params, {:ok, map()} | {:error, [String.t(), ...]}} + def fetch_required_params(params, required_params) do + fetched_params = Map.take(params, required_params) + + result = + if all_of_required_keys_found?(fetched_params, required_params) do + {:ok, fetched_params} + else + missing_params = get_missing_required_params(fetched_params, required_params) + {:error, missing_params} + end + + {:required_params, result} + end + + defp fetch_block_param(%{"block" => "latest"}), do: {:ok, :latest} + defp fetch_block_param(%{"block" => "earliest"}), do: {:ok, :earliest} + defp fetch_block_param(%{"block" => "pending"}), do: {:ok, :pending} + + defp fetch_block_param(%{"block" => string_integer}) when is_bitstring(string_integer) do + case Integer.parse(string_integer) do + {integer, ""} -> {:ok, integer} + _ -> :error + end + end + + defp fetch_block_param(%{"block" => _block}), do: :error + defp fetch_block_param(_), do: {:ok, :latest} + + defp to_valid_format(params, :tokenbalance) do + result = + with {:ok, contract_address_hash} <- to_address_hash(params, "contractaddress"), + {:ok, address_hash} <- to_address_hash(params, "address") do + {:ok, %{contract_address_hash: contract_address_hash, address_hash: address_hash}} + else + {:error, _param_key} = error -> error + end + + {:format, result} + end + + defp all_of_required_keys_found?(fetched_params, required_params) do + Enum.all?(required_params, &Map.has_key?(fetched_params, &1)) + end + + defp get_missing_required_params(fetched_params, required_params) do + fetched_keys = fetched_params |> Map.keys() |> MapSet.new() + + required_params + |> MapSet.new() + |> MapSet.difference(fetched_keys) + |> MapSet.to_list() + end + + defp fetch_address(params) do + {:address_param, Map.fetch(params, "address")} + end + + defp to_address_hashes(address_param) when is_binary(address_param) do + address_param + |> String.split(",") + |> Enum.take(20) + |> to_address_hashes() + end + + defp to_address_hashes(address_param) when is_list(address_param) do + address_hashes = address_param_to_address_hashes(address_param) + + if any_errors?(address_hashes) do + {:format, :error} + else + {:format, {:ok, address_hashes}} + end + end + + defp address_param_to_address_hashes(address_param) do + Enum.map(address_param, fn single_address -> + case Chain.string_to_address_hash(single_address) do + {:ok, address_hash} -> address_hash + :error -> :error + end + end) + end + + defp any_errors?(address_hashes) do + Enum.any?(address_hashes, &(&1 == :error)) + end + + defp list_accounts(%{page_number: page_number, page_size: page_size}) do + offset = (max(page_number, 1) - 1) * page_size + + # limit is just page_size + offset + |> Addresses.list_ordered_addresses(page_size) + |> trigger_balances_and_add_status() + end + + defp hashes_to_addresses(address_hashes) do + address_hashes + |> Chain.hashes_to_addresses() + |> trigger_balances_and_add_status() + |> add_not_found_addresses(address_hashes) + end + + defp add_not_found_addresses(addresses, hashes) do + found_hashes = MapSet.new(addresses, & &1.hash) + + hashes + |> MapSet.new() + |> MapSet.difference(found_hashes) + |> hashes_to_addresses(:not_found) + |> Enum.concat(addresses) + end + + defp hashes_to_addresses(hashes, :not_found) do + Enum.map(hashes, fn hash -> + %Address{ + hash: hash, + fetched_coin_balance: %Wei{value: 0} + } + end) + end + + defp trigger_balances_and_add_status(addresses) do + Enum.map(addresses, fn address -> + case CoinBalanceOnDemand.trigger_fetch(address) do + :current -> + %{address | stale?: false} + + _ -> + %{address | stale?: true} + end + end) + end + + defp to_contract_address_hash(nil), do: {:contract_address, {:ok, nil}} + + defp to_contract_address_hash(address_hash_string) do + {:contract_address, Chain.string_to_address_hash(address_hash_string)} + end + + defp to_address_hash(address_hash_string) do + {:format, Chain.string_to_address_hash(address_hash_string)} + end + + defp to_address_hash(params, param_key) do + case Chain.string_to_address_hash(params[param_key]) do + {:ok, address_hash} -> {:ok, address_hash} + :error -> {:error, param_key} + end + end + + defp to_transaction_hash(transaction_hash_string) do + {:format, Chain.string_to_transaction_hash(transaction_hash_string)} + end + + defp put_order_by_direction(options, params) do + case params do + %{"sort" => sort} when sort in ["asc", "desc"] -> + order_by_direction = String.to_existing_atom(sort) + Map.put(options, :order_by_direction, order_by_direction) + + _ -> + options + end + end + + # sobelow_skip ["DOS.StringToAtom"] + defp put_block(options, params, key) do + with %{^key => block_param} <- params, + {block_number, ""} <- Integer.parse(block_param) do + Map.put(options, String.to_atom(key), block_number) + else + _ -> + options + end + end + + # sobelow_skip ["DOS.StringToAtom"] + defp put_filter_by(options, params) do + case params do + %{"filter_by" => filter_by} when filter_by in ["from", "to"] -> + Map.put(options, String.to_atom("filter_by"), filter_by) + + _ -> + options + end + end + + def put_timestamp({:ok, options}, params, timestamp_param_key) do + options = put_timestamp(options, params, timestamp_param_key) + {:ok, options} + end + + # sobelow_skip ["DOS.StringToAtom"] + def put_timestamp(options, params, timestamp_param_key) do + with %{^timestamp_param_key => timestamp_param} <- params, + {unix_timestamp, ""} <- Integer.parse(timestamp_param), + {:ok, timestamp} <- DateTime.from_unix(unix_timestamp) do + Map.put(options, String.to_atom(timestamp_param_key), timestamp) + else + _ -> + options + end + end + + defp list_transactions(address_hash, options) do + case Etherscan.list_transactions(address_hash, options) do + [] -> {:error, :not_found} + transactions -> {:ok, transactions} + end + end + + defp list_pending_transactions(address_hash, options) do + case Etherscan.list_pending_transactions(address_hash, options) do + [] -> {:error, :not_found} + pending_transactions -> {:ok, pending_transactions} + end + end + + defp list_internal_transactions(transaction_hash) do + case Etherscan.list_internal_transactions(transaction_hash) do + [] -> {:error, :not_found} + internal_transactions -> {:ok, internal_transactions} + end + end + + defp list_internal_transactions(address_hash, options) do + case Etherscan.list_internal_transactions(address_hash, options) do + [] -> {:error, :not_found} + internal_transactions -> {:ok, internal_transactions} + end + end + + defp list_token_transfers(address_hash, contract_address_hash, options) do + case Etherscan.list_token_transfers(address_hash, contract_address_hash, options) do + [] -> {:error, :not_found} + token_transfers -> {:ok, token_transfers} + end + end + + defp list_blocks(address_hash, options) do + case Etherscan.list_blocks(address_hash, options) do + [] -> {:error, :not_found} + blocks -> {:ok, blocks} + end + end + + defp get_token_balance(%{contract_address_hash: contract_address_hash, address_hash: address_hash}) do + case Etherscan.get_token_balance(contract_address_hash, address_hash) do + nil -> 0 + token_balance -> token_balance.value + end + end + + defp list_tokens(address_hash) do + case Etherscan.list_tokens(address_hash) do + [] -> {:error, :not_found} + token_list -> {:ok, token_list} + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/block_controller.ex new file mode 100644 index 0000000..68256be --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/block_controller.ex @@ -0,0 +1,60 @@ +defmodule BlockScoutWeb.API.RPC.BlockController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.Chain, as: ChainWeb + alias Explorer.Chain + alias Explorer.Chain.Cache.BlockNumber + + def getblockreward(conn, params) do + with {:block_param, {:ok, unsafe_block_number}} <- {:block_param, Map.fetch(params, "blockno")}, + {:ok, block_number} <- ChainWeb.param_to_block_number(unsafe_block_number), + {:ok, block} <- Chain.number_to_block(block_number) do + reward = Chain.block_reward(block_number) + + render(conn, :block_reward, block: block, reward: reward) + else + {:block_param, :error} -> + render(conn, :error, error: "Query parameter 'blockno' is required") + + {:error, :invalid} -> + render(conn, :error, error: "Invalid block number") + + {:error, :not_found} -> + render(conn, :error, error: "Block does not exist") + end + end + + def getblocknobytime(conn, params) do + from_api = true + + with {:timestamp_param, {:ok, unsafe_timestamp}} <- {:timestamp_param, Map.fetch(params, "timestamp")}, + {:closest_param, {:ok, unsafe_closest}} <- {:closest_param, Map.fetch(params, "closest")}, + {:ok, timestamp} <- ChainWeb.param_to_block_timestamp(unsafe_timestamp), + {:ok, closest} <- ChainWeb.param_to_block_closest(unsafe_closest), + {:ok, block_number} <- Chain.timestamp_to_block_number(timestamp, closest, from_api) do + render(conn, block_number: block_number) + else + {:timestamp_param, :error} -> + render(conn, :error, error: "Query parameter 'timestamp' is required") + + {:closest_param, :error} -> + render(conn, :error, error: "Query parameter 'closest' is required") + + {:error, :invalid_timestamp} -> + render(conn, :error, error: "Invalid `timestamp` param") + + {:error, :invalid_closest} -> + render(conn, :error, error: "Invalid `closest` param") + + {:error, :not_found} -> + render(conn, :error, error: "Block does not exist") + end + end + + def eth_block_number(conn, params) do + id = Map.get(params, "id", 1) + max_block_number = BlockNumber.get_max() + + render(conn, :eth_block_number, number: max_block_number, id: id) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex new file mode 100644 index 0000000..cc19805 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex @@ -0,0 +1,611 @@ +defmodule BlockScoutWeb.API.RPC.ContractController do + use BlockScoutWeb, :controller + + require Logger + + alias BlockScoutWeb.AddressContractVerificationController, as: VerificationController + alias BlockScoutWeb.API.RPC.{AddressController, Helpers} + alias Explorer.Chain + alias Explorer.Chain.Events.Publisher, as: EventsPublisher + alias Explorer.Chain.{Address, Hash, SmartContract} + alias Explorer.Chain.SmartContract.VerificationStatus + alias Explorer.Etherscan.Contracts + alias Explorer.SmartContract.Helper + alias Explorer.SmartContract.Solidity.Publisher + alias Explorer.SmartContract.Solidity.PublisherWorker, as: SolidityPublisherWorker + alias Explorer.SmartContract.Vyper.Publisher, as: VyperPublisher + alias Explorer.ThirdPartyIntegrations.Sourcify + + @smth_went_wrong "Something went wrong while publishing the contract" + @verified "Smart-contract already verified." + @invalid_address "Invalid address hash" + @invalid_args "Invalid args format" + @address_required "Query parameter address is required" + + def verify(conn, %{"addressHash" => address_hash} = params) do + with {:params, {:ok, fetched_params}} <- {:params, fetch_verify_params(params)}, + {:format, {:ok, casted_address_hash}} <- to_address_hash(address_hash), + {:params, external_libraries} <- + {:params, fetch_external_libraries(params)}, + {:publish, {:ok, _}} <- + {:publish, Publisher.publish(address_hash, fetched_params, external_libraries)} do + address = Contracts.address_hash_to_address_with_source_code(casted_address_hash) + + render(conn, :verify, %{contract: address}) + else + {:publish, + {:error, + %Ecto.Changeset{ + errors: [ + address_hash: + {"has already been taken", + [ + constraint: :unique, + constraint_name: "smart_contracts_address_hash_index" + ]} + ] + }}} -> + render(conn, :error, error: @verified) + + {:publish, {:error, error}} -> + Logger.error(fn -> + [ + @smth_went_wrong, + ": ", + inspect(error) + ] + end) + + render(conn, :error, error: "#{@smth_went_wrong}: #{inspect(error.errors)}") + + {:publish, error} -> + Logger.error(fn -> + [ + @smth_went_wrong, + ": ", + inspect(error) + ] + end) + + render(conn, :error, error: @smth_went_wrong) + + {:format, :error} -> + render(conn, :error, error: @invalid_address) + + {:params, {:error, error}} -> + render(conn, :error, error: error) + end + end + + def verify_via_sourcify(conn, %{"addressHash" => address_hash} = input) do + files = + if Map.has_key?(input, "files") do + input["files"] + else + [] + end + + if Chain.smart_contract_fully_verified?(address_hash) do + render(conn, :error, error: @verified) + else + case Sourcify.check_by_address(address_hash) do + {:ok, _verified_status} -> + get_metadata_and_publish(address_hash, conn) + + _ -> + with {:ok, files_array} <- prepare_params(files), + {:ok, validated_files} <- validate_files(files_array) do + verify_and_publish(address_hash, validated_files, conn) + else + {:error, error} -> + render(conn, :error, error: error) + + _ -> + render(conn, :error, error: "Invalid body") + end + end + end + end + + def verifysourcecode( + conn, + %{ + "codeformat" => "solidity-standard-json-input", + "contractaddress" => address_hash, + "sourceCode" => json_input + } = params + ) do + with {:check_verified_status, false} <- + {:check_verified_status, Chain.smart_contract_fully_verified?(address_hash)}, + {:format, {:ok, _casted_address_hash}} <- to_address_hash(address_hash), + {:params, {:ok, fetched_params}} <- {:params, fetch_verifysourcecode_params(params)}, + uid <- VerificationStatus.generate_uid(address_hash) do + Que.add(SolidityPublisherWorker, {"json_api", fetched_params, json_input, uid}) + + render(conn, :show, %{result: uid}) + else + {:check_verified_status, true} -> + render(conn, :error, error: @verified, data: @verified) + + {:format, :error} -> + render(conn, :error, error: @invalid_address, data: @invalid_address) + + {:params, {:error, error}} -> + render(conn, :error, error: error, data: error) + end + end + + def verifysourcecode(conn, %{"codeformat" => "solidity-standard-json-input"}) do + render(conn, :error, error: "Missing sourceCode or contractaddress fields") + end + + def checkverifystatus(conn, %{"guid" => guid}) do + case VerificationStatus.fetch_status(guid) do + :pending -> + render(conn, :show, %{result: "Pending in queue"}) + + :pass -> + render(conn, :show, %{result: "Pass - Verified"}) + + :fail -> + render(conn, :show, %{result: "Fail - Unable to verify"}) + + :unknown_uid -> + render(conn, :show, %{result: "Unknown UID"}) + end + end + + defp prepare_params(files) when is_struct(files) do + {:error, @invalid_args} + end + + defp prepare_params(files) when is_map(files) do + {:ok, VerificationController.prepare_files_array(files)} + end + + defp prepare_params(files) when is_list(files) do + {:ok, files} + end + + defp prepare_params(_arg) do + {:error, @invalid_args} + end + + defp validate_files(files) do + if length(files) < 2 do + {:error, "You should attach at least 2 files"} + else + files_array = + files + |> Enum.filter(fn file -> validate_filename(file.filename) end) + + jsons = + files_array + |> Enum.filter(fn file -> Helper.json_file?(file.filename) end) + + sols = + files_array + |> Enum.filter(fn file -> Helper.sol_file?(file.filename) end) + + if length(jsons) > 0 and length(sols) > 0 do + {:ok, files_array} + else + {:error, "You should attach at least one *.json and one *.sol files"} + end + end + end + + defp validate_filename(filename) do + case List.last(String.split(String.downcase(filename), ".")) do + "sol" -> + true + + "json" -> + true + + _ -> + false + end + end + + defp get_metadata_and_publish(address_hash_string, conn) do + case Sourcify.get_metadata(address_hash_string) do + {:ok, verification_metadata} -> + case Sourcify.parse_params_from_sourcify(address_hash_string, verification_metadata) do + %{"params_to_publish" => params_to_publish, "abi" => abi, "secondary_sources" => secondary_sources} -> + publish_and_handle_response_without_broadcast( + address_hash_string, + params_to_publish, + abi, + secondary_sources, + conn + ) + + {:error, :metadata} -> + render(conn, :error, error: Sourcify.no_metadata_message()) + + _ -> + render(conn, :error, error: Sourcify.failed_verification_message()) + end + + {:error, %{"error" => error}} -> + render(conn, :error, error: error) + end + end + + defp publish_and_handle_response_without_broadcast( + address_hash_string, + params_to_publish, + abi, + secondary_sources, + conn + ) do + case publish_without_broadcast(%{ + "addressHash" => address_hash_string, + "params" => params_to_publish, + "abi" => abi, + "secondarySources" => secondary_sources + }) do + {:ok, _contract} -> + {:format, {:ok, address_hash}} = to_address_hash(address_hash_string) + address = Contracts.address_hash_to_address_with_source_code(address_hash) + render(conn, :verify, %{contract: address}) + + {:error, changeset} -> + render(conn, :error, error: changeset) + end + end + + defp verify_and_publish(address_hash_string, files_array, conn) do + case Sourcify.verify(address_hash_string, files_array) do + {:ok, _verified_status} -> + case Sourcify.check_by_address(address_hash_string) do + {:ok, _verified_status} -> + get_metadata_and_publish(address_hash_string, conn) + + {:error, %{"error" => error}} -> + render(conn, :error, error: error) + + {:error, error} -> + render(conn, :error, error: error) + end + + {:error, %{"error" => error}} -> + render(conn, :error, error: error) + + {:error, error} -> + render(conn, :error, error: error) + end + end + + def verify_vyper_contract(conn, %{"addressHash" => address_hash} = params) do + with {:params, {:ok, fetched_params}} <- {:params, fetch_vyper_verify_params(params)}, + {:format, {:ok, casted_address_hash}} <- to_address_hash(address_hash), + {:publish, {:ok, _}} <- + {:publish, VyperPublisher.publish(address_hash, fetched_params)} do + address = Contracts.address_hash_to_address_with_source_code(casted_address_hash) + + render(conn, :verify, %{contract: address}) + else + {:publish, + {:error, + %Ecto.Changeset{ + errors: [ + address_hash: + {"has already been taken", + [ + constraint: :unique, + constraint_name: "smart_contracts_address_hash_index" + ]} + ] + }}} -> + render(conn, :error, error: @verified) + + {:publish, _} -> + render(conn, :error, error: @smth_went_wrong) + + {:format, :error} -> + render(conn, :error, error: @invalid_address) + + {:params, {:error, error}} -> + render(conn, :error, error: error) + end + end + + def publish_without_broadcast( + %{"addressHash" => address_hash, "abi" => abi, "compilationTargetFilePath" => file_path} = input + ) do + params = proccess_params(input) + + address_hash + |> Publisher.publish_smart_contract(params, abi, file_path) + |> proccess_response() + end + + def publish_without_broadcast(%{"addressHash" => address_hash, "abi" => abi} = input) do + params = proccess_params(input) + + address_hash + |> Publisher.publish_smart_contract(params, abi) + |> proccess_response() + end + + def publish(nil, %{"addressHash" => _address_hash} = input) do + publish_without_broadcast(input) + end + + def publish(conn, %{"addressHash" => address_hash} = input) do + result = publish_without_broadcast(input) + + EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand) + end + + def proccess_params(input) do + if Map.has_key?(input, "secondarySources") do + input["params"] + |> Map.put("secondary_sources", Map.get(input, "secondarySources")) + else + input["params"] + end + end + + def proccess_response(response) do + case response do + {:ok, _contract} = result -> + result + + {:error, changeset} -> + {:error, changeset} + end + end + + def listcontracts(conn, params) do + with pagination_options <- Helpers.put_pagination_options(%{}, params), + {:params, {:ok, options}} <- {:params, add_filters(pagination_options, params)} do + options_with_defaults = + options + |> Map.put_new(:page_number, 0) + |> Map.put_new(:page_size, 10) + + contracts = list_contracts(options_with_defaults) + + conn + |> put_status(200) + |> render(:listcontracts, %{contracts: contracts}) + else + {:params, {:error, error}} -> + conn + |> put_status(400) + |> render(:error, error: error) + end + end + + def getabi(conn, params) do + with {:address_param, {:ok, address_param}} <- fetch_address(params), + {:format, {:ok, address_hash}} <- to_address_hash(address_param), + {:contract, {:ok, contract}} <- to_smart_contract(address_hash) do + render(conn, :getabi, %{abi: contract.abi}) + else + {:address_param, :error} -> + render(conn, :error, error: @address_required) + + {:format, :error} -> + render(conn, :error, error: @invalid_address) + + {:contract, :not_found} -> + render(conn, :error, error: "Contract source code not verified") + end + end + + def getsourcecode(conn, params) do + with {:address_param, {:ok, address_param}} <- fetch_address(params), + {:format, {:ok, address_hash}} <- to_address_hash(address_param) do + _ = VerificationController.check_and_verify(address_param) + address = Contracts.address_hash_to_address_with_source_code(address_hash) + + render(conn, :getsourcecode, %{ + contract: address || %Address{hash: address_hash, smart_contract: nil} + }) + else + {:address_param, :error} -> + render(conn, :error, error: @address_required) + + {:format, :error} -> + render(conn, :error, error: @invalid_address) + end + end + + defp list_contracts(%{page_number: page_number, page_size: page_size} = opts) do + offset = (max(page_number, 1) - 1) * page_size + + case Map.get(opts, :filter) do + :verified -> + Contracts.list_verified_contracts(page_size, offset, opts) + + :decompiled -> + not_decompiled_with_version = Map.get(opts, :not_decompiled_with_version) + Contracts.list_decompiled_contracts(page_size, offset, not_decompiled_with_version) + + :unverified -> + Contracts.list_unordered_unverified_contracts(page_size, offset) + + :not_decompiled -> + Contracts.list_unordered_not_decompiled_contracts(page_size, offset) + + :empty -> + Contracts.list_empty_contracts(page_size, offset) + + _ -> + Contracts.list_contracts(page_size, offset) + end + end + + defp add_filters(options, params) do + options + |> add_filter(params) + |> add_param(params, :not_decompiled_with_version) + |> AddressController.put_timestamp(params, "verified_at_start_timestamp") + |> AddressController.put_timestamp(params, "verified_at_end_timestamp") + end + + defp add_filter(options, params) do + with {:param, {:ok, value}} <- {:param, Map.fetch(params, "filter")}, + {:validation, {:ok, filter}} <- {:validation, contracts_filter(value)} do + {:ok, Map.put(options, :filter, filter)} + else + {:param, :error} -> {:ok, options} + {:validation, {:error, error}} -> {:error, error} + end + end + + defp add_param({:ok, options}, params, key) do + case Map.fetch(params, Atom.to_string(key)) do + {:ok, value} -> {:ok, Map.put(options, key, value)} + :error -> {:ok, options} + end + end + + defp add_param(options, _params, _key) do + options + end + + defp contracts_filter(nil), do: {:ok, nil} + defp contracts_filter(1), do: {:ok, :verified} + defp contracts_filter(2), do: {:ok, :decompiled} + defp contracts_filter(3), do: {:ok, :unverified} + defp contracts_filter(4), do: {:ok, :not_decompiled} + defp contracts_filter(5), do: {:ok, :empty} + defp contracts_filter("verified"), do: {:ok, :verified} + defp contracts_filter("decompiled"), do: {:ok, :decompiled} + defp contracts_filter("unverified"), do: {:ok, :unverified} + defp contracts_filter("not_decompiled"), do: {:ok, :not_decompiled} + defp contracts_filter("empty"), do: {:ok, :empty} + + defp contracts_filter(filter) when is_bitstring(filter) do + case Integer.parse(filter) do + {number, ""} -> contracts_filter(number) + _ -> {:error, contracts_filter_error_message(filter)} + end + end + + defp contracts_filter(filter), do: {:error, contracts_filter_error_message(filter)} + + defp contracts_filter_error_message(filter) do + "#{filter} is not a valid value for `filter`. Please use one of: verified, decompiled, unverified, not_decompiled, 1, 2, 3, 4." + end + + defp fetch_address(params) do + {:address_param, Map.fetch(params, "address")} + end + + defp to_address_hash(address_hash_string) do + {:format, Chain.string_to_address_hash(address_hash_string)} + end + + defp to_smart_contract(address_hash) do + _ = VerificationController.check_and_verify(Hash.to_string(address_hash)) + + result = + case Chain.address_hash_to_smart_contract(address_hash) do + nil -> + :not_found + + contract -> + {:ok, SmartContract.preload_decompiled_smart_contract(contract)} + end + + {:contract, result} + end + + defp fetch_verify_params(params) do + {:ok, %{}} + |> required_param(params, "addressHash", "address_hash") + |> required_param(params, "name", "name") + |> required_param(params, "compilerVersion", "compiler_version") + |> required_param(params, "optimization", "optimization") + |> required_param(params, "contractSourceCode", "contract_source_code") + |> optional_param(params, "evmVersion", "evm_version") + |> optional_param(params, "constructorArguments", "constructor_arguments") + |> optional_param(params, "autodetectConstructorArguments", "autodetect_constructor_args") + |> optional_param(params, "optimizationRuns", "optimization_runs") + |> parse_optimization_runs() + end + + defp fetch_vyper_verify_params(params) do + {:ok, %{}} + |> required_param(params, "addressHash", "address_hash") + |> required_param(params, "name", "name") + |> required_param(params, "compilerVersion", "compiler_version") + |> required_param(params, "contractSourceCode", "contract_source_code") + |> optional_param(params, "constructorArguments", "constructor_arguments") + end + + defp fetch_verifysourcecode_params(params) do + {:ok, %{}} + |> required_param(params, "contractaddress", "address_hash") + |> required_param(params, "contractname", "name") + |> required_param(params, "compilerversion", "compiler_version") + |> optional_param(params, "constructorArguements", "constructor_arguments") + |> optional_param(params, "constructorArguments", "constructor_arguments") + end + + defp parse_optimization_runs({:ok, %{"optimization_runs" => runs} = opts}) when is_bitstring(runs) do + case Integer.parse(runs) do + {runs_int, _} -> + {:ok, Map.put(opts, "optimization_runs", runs_int)} + + _ -> + {:ok, Map.put(opts, "optimization_runs", 200)} + end + end + + defp parse_optimization_runs({:ok, %{"optimization_runs" => runs} = opts}) when is_integer(runs) do + {:ok, opts} + end + + defp parse_optimization_runs({:ok, opts}) do + {:ok, Map.put(opts, "optimization_runs", 200)} + end + + defp parse_optimization_runs(other), do: other + + defp fetch_external_libraries(params) do + Enum.reduce(1..Application.get_env(:block_scout_web, :verification_max_libraries), %{}, fn number, acc -> + case Map.fetch(params, "library#{number}Name") do + {:ok, library_name} -> + library_address = Map.get(params, "library#{number}Address") + + acc + |> Map.put("library#{number}_name", library_name) + |> Map.put("library#{number}_address", library_address) + + :error -> + acc + end + end) + end + + defp required_param({:error, _} = error, _, _, _), do: error + + defp required_param({:ok, map}, params, key, new_key) do + case Map.fetch(params, key) do + {:ok, value} -> + {:ok, Map.put(map, new_key, value)} + + :error -> + {:error, "#{key} is required."} + end + end + + defp optional_param({:error, _} = error, _, _, _), do: error + + defp optional_param({:ok, map}, params, key, new_key) do + case Map.fetch(params, key) do + {:ok, value} -> + {:ok, Map.put(map, new_key, value)} + + :error -> + {:ok, map} + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/helpers.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/helpers.ex new file mode 100644 index 0000000..1196af9 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/helpers.ex @@ -0,0 +1,42 @@ +defmodule BlockScoutWeb.API.RPC.Helpers do + @moduledoc """ + Small helpers for RPC api controllers. + """ + alias Explorer.Etherscan + + def put_pagination_options(options, params) do + options + |> put_page_option(params) + |> put_offset_option(params) + end + + def put_page_option(options, %{"page" => page}) do + case Integer.parse(page) do + {page_number, ""} when page_number > 0 -> + Map.put(options, :page_number, page_number) + + _ -> + options + end + end + + def put_page_option(options, _), do: options + + def put_offset_option(options, %{"offset" => offset}) do + with {page_size, ""} when page_size > 0 <- Integer.parse(offset), + :ok <- validate_max_page_size(page_size) do + Map.put(options, :page_size, page_size) + else + _ -> + options + end + end + + def put_offset_option(options, _) do + options + end + + defp validate_max_page_size(page_size) do + if page_size <= Etherscan.page_size_max(), do: :ok, else: :error + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/logs_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/logs_controller.ex new file mode 100644 index 0000000..388b99d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/logs_controller.ex @@ -0,0 +1,238 @@ +defmodule BlockScoutWeb.API.RPC.LogsController do + use BlockScoutWeb, :controller + + alias Explorer.{Chain, Etherscan} + + def getlogs(conn, params) do + with {:required_params, {:ok, fetched_params}} <- fetch_required_params(params), + {:format, {:ok, validated_params}} <- to_valid_format(fetched_params), + {:ok, logs} <- list_logs(validated_params) do + render(conn, :getlogs, %{logs: logs}) + else + {:required_params, {:error, missing_params}} -> + error = "Required query parameters missing: #{Enum.join(missing_params, ", ")}" + render(conn, :error, error: error) + + {:format, {:error, param}} -> + render(conn, :error, error: "Invalid #{param} format") + + {:error, :not_found} -> + render(conn, :error, error: "No logs found", data: []) + end + end + + # Interpretation of `@maybe_required_params`: + # + # If a pair of `topic{x}` params is provided, then the corresponding + # `topic{x}_{x}_opr` param is required. + # + # For example, if "topic0" and "topic1" are provided, then "topic0_1_opr" is + # required. + # + @maybe_required_params %{ + ["topic0", "topic1"] => "topic0_1_opr", + ["topic0", "topic2"] => "topic0_2_opr", + ["topic0", "topic3"] => "topic0_3_opr", + ["topic1", "topic2"] => "topic1_2_opr", + ["topic1", "topic3"] => "topic1_3_opr", + ["topic2", "topic3"] => "topic2_3_opr" + } + + @required_params %{ + # all_of: all of these parameters are required + all_of: ["fromBlock", "toBlock"], + # one_of: at least one of these parameters is required + one_of: ["address", "topic0", "topic1", "topic2", "topic3"] + } + + @doc """ + Fetches required params. Returns error tuple if required params are missing. + + """ + @spec fetch_required_params(map()) :: {:required_params, {:ok, map()} | {:error, [String.t(), ...]}} + def fetch_required_params(params) do + all_of_params = fetch_required_params(params, :all_of) + one_of_params = fetch_required_params(params, :one_of) + maybe_params = fetch_required_params(params, :maybe) + + result = + case {all_of_params, one_of_params, maybe_params} do + {{:error, missing_params}, {:error, _}, _} -> + {:error, Enum.concat(missing_params, ["address and/or topic{x}"])} + + {{:error, missing_params}, {:ok, _}, _} -> + {:error, missing_params} + + {{:ok, _}, {:error, _}, _} -> + {:error, ["address and/or topic{x}"]} + + {{:ok, _}, {:ok, _}, {:error, missing_params}} -> + {:error, missing_params} + + {{:ok, all_of_params}, {:ok, one_of_params}, {:ok, maybe_params}} -> + fetched_params = + all_of_params + |> Map.merge(one_of_params) + |> Map.merge(maybe_params) + + {:ok, fetched_params} + end + + {:required_params, result} + end + + @doc """ + Prepares params for processing. Returns error tuple if invalid format is + found. + + """ + @spec to_valid_format(map()) :: {:format, {:ok, map()} | {:error, String.t()}} + def to_valid_format(params) do + result = + with {:ok, from_block} <- to_block_number(params, "fromBlock"), + {:ok, to_block} <- to_block_number(params, "toBlock"), + {:ok, address_hash} <- to_address_hash(params["address"]), + :ok <- validate_topic_operators(params) do + validated_params = %{ + from_block: from_block, + to_block: to_block, + address_hash: address_hash, + first_topic: params["topic0"], + second_topic: params["topic1"], + third_topic: params["topic2"], + fourth_topic: params["topic3"], + topic0_1_opr: params["topic0_1_opr"], + topic0_2_opr: params["topic0_2_opr"], + topic0_3_opr: params["topic0_3_opr"], + topic1_2_opr: params["topic1_2_opr"], + topic1_3_opr: params["topic1_3_opr"], + topic2_3_opr: params["topic2_3_opr"] + } + + {:ok, validated_params} + else + {:error, param_key} -> + {:error, param_key} + end + + {:format, result} + end + + defp fetch_required_params(params, :all_of) do + fetched_params = Map.take(params, @required_params.all_of) + + if all_of_required_keys_found?(fetched_params) do + {:ok, fetched_params} + else + missing_params = get_missing_required_params(fetched_params, :all_of) + {:error, missing_params} + end + end + + defp fetch_required_params(params, :one_of) do + fetched_params = Map.take(params, @required_params.one_of) + found_keys = Map.keys(fetched_params) + + if length(found_keys) > 0 do + {:ok, fetched_params} + else + {:error, @required_params.one_of} + end + end + + defp fetch_required_params(params, :maybe) do + case get_missing_required_params(params, :maybe) do + [] -> + keys_to_fetch = Map.values(@maybe_required_params) + {:ok, Map.take(params, keys_to_fetch)} + + missing_params -> + {:error, Enum.reverse(missing_params)} + end + end + + defp all_of_required_keys_found?(fetched_params) do + Enum.all?(@required_params.all_of, &Map.has_key?(fetched_params, &1)) + end + + defp get_missing_required_params(fetched_params, :all_of) do + fetched_keys = fetched_params |> Map.keys() |> MapSet.new() + + @required_params.all_of + |> MapSet.new() + |> MapSet.difference(fetched_keys) + |> MapSet.to_list() + end + + defp get_missing_required_params(fetched_params, :maybe) do + Enum.reduce(@maybe_required_params, [], fn {[key1, key2], expectation}, missing_params -> + has_key1? = Map.has_key?(fetched_params, key1) + has_key2? = Map.has_key?(fetched_params, key2) + has_expectation? = Map.has_key?(fetched_params, expectation) + + case {has_key1?, has_key2?, has_expectation?} do + {true, true, false} -> + [expectation | missing_params] + + _ -> + missing_params + end + end) + end + + defp to_block_number(params, param_key) do + case params[param_key] do + "latest" -> + Chain.max_consensus_block_number() + + _ -> + to_integer(params, param_key) + end + end + + defp to_integer(params, param_key) do + case Integer.parse(params[param_key]) do + {integer, ""} -> + {:ok, integer} + + _ -> + {:error, param_key} + end + end + + defp to_address_hash(nil), do: {:ok, nil} + + defp to_address_hash(address_hash_string) do + case Chain.string_to_address_hash(address_hash_string) do + :error -> + {:error, "address"} + + {:ok, address_hash} -> + {:ok, address_hash} + end + end + + defp validate_topic_operators(params) do + topic_operator_keys = Map.values(@maybe_required_params) + + first_invalid_topic_operator = + Enum.find(topic_operator_keys, fn topic_operator -> + params[topic_operator] not in ["and", "or", nil] + end) + + case first_invalid_topic_operator do + nil -> + :ok + + invalid_topic_operator -> + {:error, invalid_topic_operator} + end + end + + defp list_logs(filter) do + case Etherscan.list_logs(filter) do + [] -> {:error, :not_found} + logs -> {:ok, logs} + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex new file mode 100644 index 0000000..5e57d51 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex @@ -0,0 +1,127 @@ +defmodule BlockScoutWeb.API.RPC.RPCTranslator do + @moduledoc """ + Converts an RPC-style request into a controller action. + + Requests are expected to have URL query params like `?module=module&action=action`. + + ## Configuration + + The plugs needs a map relating a `module` string to a controller module. + + # In a router + forward "/api", RPCTranslator, %{"block" => BlockController} + + """ + + require Logger + + import Plug.Conn + import Phoenix.Controller, only: [put_view: 2] + + alias BlockScoutWeb.AccessHelpers + alias BlockScoutWeb.API.APILogger + alias BlockScoutWeb.API.RPC.RPCView + alias Phoenix.Controller + alias Plug.Conn + + def init(opts) do + opts + end + + def call(%Conn{params: %{"module" => module, "action" => action}} = conn, translations) do + with true <- valid_api_request_path(conn), + {:ok, {controller, write_actions}} <- translate_module(translations, module), + {:ok, action} <- translate_action(action), + true <- action_accessed?(action, write_actions), + :ok <- AccessHelpers.check_rate_limit(conn), + {:ok, conn} <- call_controller(conn, controller, action) do + APILogger.log(conn) + conn + else + {:error, :no_action} -> + conn + |> put_status(400) + |> put_view(RPCView) + |> Controller.render(:error, error: "Unknown action") + |> halt() + + {:error, error} -> + Logger.error(fn -> ["Error while calling RPC action", inspect(error)] end) + + conn + |> put_status(500) + |> put_view(RPCView) + |> Controller.render(:error, error: "Something went wrong.") + |> halt() + + :rate_limit_reached -> + AccessHelpers.handle_rate_limit_deny(conn) + + _ -> + conn + |> put_status(500) + |> put_view(RPCView) + |> Controller.render(:error, error: "Something went wrong.") + |> halt() + end + end + + def call(%Conn{} = conn, _) do + conn + |> put_status(400) + |> put_view(RPCView) + |> Controller.render(:error, error: "Params 'module' and 'action' are required parameters") + |> halt() + end + + @doc false + @spec translate_module(map(), String.t()) :: {:ok, {module(), list(atom())}} | {:error, :no_action} + defp translate_module(translations, module) do + module_lowercase = String.downcase(module) + + case Map.fetch(translations, module_lowercase) do + {:ok, module} -> {:ok, module} + _ -> {:error, :no_action} + end + end + + @doc false + @spec translate_action(String.t()) :: {:ok, atom()} | {:error, :no_action} + defp translate_action(action) do + action_lowercase = String.downcase(action) + {:ok, String.to_existing_atom(action_lowercase)} + rescue + ArgumentError -> {:error, :no_action} + end + + defp action_accessed?(action, write_actions) do + conf = Application.get_env(:block_scout_web, BlockScoutWeb.ApiRouter) + + if action in write_actions do + conf[:writing_enabled] || {:error, :no_action} + else + conf[:reading_enabled] || {:error, :no_action} + end + end + + @doc false + @spec call_controller(Conn.t(), module(), atom()) :: {:ok, Conn.t()} | {:error, :no_action} | {:error, Exception.t()} + defp call_controller(conn, controller, action) do + if :erlang.function_exported(controller, action, 2) do + {:ok, controller.call(conn, action)} + else + {:error, :no_action} + end + rescue + e -> + {:error, Exception.format(:error, e, __STACKTRACE__)} + end + + defp valid_api_request_path(conn) do + if conn.request_path == "/api" || conn.request_path == "/api/v1" do + true + else + false + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex new file mode 100644 index 0000000..8c6d995 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex @@ -0,0 +1,90 @@ +defmodule BlockScoutWeb.API.RPC.StatsController do + use BlockScoutWeb, :controller + + use Explorer.Schema + + alias Explorer + alias Explorer.{Chain, Etherscan, ExchangeRates} + alias Explorer.Chain.Cache.{AddressSum, AddressSumMinusBurnt} + alias Explorer.Chain.Wei + + def tokensupply(conn, params) do + with {:contractaddress_param, {:ok, contractaddress_param}} <- fetch_contractaddress(params), + {:format, {:ok, address_hash}} <- to_address_hash(contractaddress_param), + {:token, {:ok, token}} <- {:token, Chain.token_from_address_hash(address_hash)} do + render(conn, "tokensupply.json", total_supply: Decimal.to_string(token.total_supply)) + else + {:contractaddress_param, :error} -> + render(conn, :error, error: "Query parameter contract address is required") + + {:format, :error} -> + render(conn, :error, error: "Invalid contract address format") + + {:token, {:error, :not_found}} -> + render(conn, :error, error: "contract address not found") + end + end + + def ethsupplyexchange(conn, _params) do + wei_total_supply = + Chain.total_supply() + |> Decimal.new() + |> Wei.from(:ether) + |> Wei.to(:wei) + |> Decimal.to_string() + + render(conn, "ethsupplyexchange.json", total_supply: wei_total_supply) + end + + def ethsupply(conn, _params) do + cached_wei_total_supply = AddressSum.get_sum() + + render(conn, "ethsupply.json", total_supply: cached_wei_total_supply) + end + + def coinsupply(conn, _params) do + cached_coin_total_supply_wei = AddressSumMinusBurnt.get_sum_minus_burnt() + + coin_total_supply_wei = + if Decimal.compare(cached_coin_total_supply_wei, 0) == :gt do + cached_coin_total_supply_wei + else + Chain.get_last_fetched_counter("sum_coin_total_supply_minus_burnt") + end + + cached_coin_total_supply = + %Wei{value: Decimal.new(coin_total_supply_wei)} + |> Wei.to(:ether) + |> Decimal.to_string(:normal) + + render(conn, "coinsupply.json", total_supply: cached_coin_total_supply) + end + + def coinprice(conn, _params) do + symbol = Explorer.coin() + rates = ExchangeRates.lookup(symbol) + + render(conn, "coinprice.json", rates: rates) + end + + defp fetch_contractaddress(params) do + {:contractaddress_param, Map.fetch(params, "contractaddress")} + end + + defp to_address_hash(address_hash_string) do + {:format, Chain.string_to_address_hash(address_hash_string)} + end + + def totalfees(conn, params) do + case Map.fetch(params, "date") do + {:ok, date} -> + case Etherscan.get_total_fees_per_day(date) do + {:ok, total_fees} -> render(conn, "totalfees.json", total_fees: total_fees) + {:error, error} -> render(conn, :error, error: error) + end + + _ -> + render(conn, :error, error: "Required date input is missing.") + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/token_controller.ex new file mode 100644 index 0000000..f2f8afb --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/token_controller.ex @@ -0,0 +1,60 @@ +defmodule BlockScoutWeb.API.RPC.TokenController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.API.RPC.Helpers + alias Explorer.{Chain, PagingOptions} + + def gettoken(conn, params) do + with {:contractaddress_param, {:ok, contractaddress_param}} <- fetch_contractaddress(params), + {:format, {:ok, address_hash}} <- to_address_hash(contractaddress_param), + {:token, {:ok, token}} <- {:token, Chain.token_from_address_hash(address_hash)} do + render(conn, "gettoken.json", %{token: token}) + else + {:contractaddress_param, :error} -> + render(conn, :error, error: "Query parameter contract address is required") + + {:format, :error} -> + render(conn, :error, error: "Invalid contract address hash") + + {:token, {:error, :not_found}} -> + render(conn, :error, error: "contract address not found") + end + end + + def gettokenholders(conn, params) do + with pagination_options <- Helpers.put_pagination_options(%{}, params), + {:contractaddress_param, {:ok, contractaddress_param}} <- fetch_contractaddress(params), + {:format, {:ok, address_hash}} <- to_address_hash(contractaddress_param) do + options_with_defaults = + pagination_options + |> Map.put_new(:page_number, 0) + |> Map.put_new(:page_size, 10) + + options = [ + paging_options: %PagingOptions{ + key: nil, + page_number: options_with_defaults.page_number, + page_size: options_with_defaults.page_size + } + ] + + from_api = true + token_holders = Chain.fetch_token_holders_from_token_hash(address_hash, from_api, options) + render(conn, "gettokenholders.json", %{token_holders: token_holders}) + else + {:contractaddress_param, :error} -> + render(conn, :error, error: "Query parameter contract address is required") + + {:format, :error} -> + render(conn, :error, error: "Invalid contract address hash") + end + end + + defp fetch_contractaddress(params) do + {:contractaddress_param, Map.fetch(params, "contractaddress")} + end + + defp to_address_hash(address_hash_string) do + {:format, Chain.string_to_address_hash(address_hash_string)} + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex new file mode 100644 index 0000000..8cf1e55 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex @@ -0,0 +1,102 @@ +defmodule BlockScoutWeb.API.RPC.TransactionController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + + alias Explorer.Chain + alias Explorer.Chain.Transaction + + def gettxinfo(conn, params) do + with {:txhash_param, {:ok, txhash_param}} <- fetch_txhash(params), + {:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param), + {:transaction, {:ok, %Transaction{revert_reason: revert_reason, error: error} = transaction}} <- + transaction_from_hash(transaction_hash), + paging_options <- paging_options(params) do + from_api = true + logs = Chain.transaction_to_logs(transaction_hash, from_api, paging_options) + {logs, next_page} = split_list_by_page(logs) + + transaction_updated = + if (error == "Reverted" || error == "execution reverted") && !revert_reason do + %Transaction{transaction | revert_reason: Chain.fetch_tx_revert_reason(transaction)} + else + transaction + end + + render(conn, :gettxinfo, %{ + transaction: transaction_updated, + block_height: Chain.block_height(), + logs: logs, + next_page_params: next_page_params(next_page, logs, params) + }) + else + {:transaction, :error} -> + render(conn, :error, error: "Transaction not found") + + {:txhash_param, :error} -> + render(conn, :error, error: "Query parameter txhash is required") + + {:format, :error} -> + render(conn, :error, error: "Invalid txhash format") + end + end + + def gettxreceiptstatus(conn, params) do + with {:txhash_param, {:ok, txhash_param}} <- fetch_txhash(params), + {:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param) do + status = to_transaction_status(transaction_hash) + render(conn, :gettxreceiptstatus, %{status: status}) + else + {:txhash_param, :error} -> + render(conn, :error, error: "Query parameter txhash is required") + + {:format, :error} -> + render(conn, :error, error: "Invalid txhash format") + end + end + + def getstatus(conn, params) do + with {:txhash_param, {:ok, txhash_param}} <- fetch_txhash(params), + {:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param) do + error = to_transaction_error(transaction_hash) + render(conn, :getstatus, %{error: error}) + else + {:txhash_param, :error} -> + render(conn, :error, error: "Query parameter txhash is required") + + {:format, :error} -> + render(conn, :error, error: "Invalid txhash format") + end + end + + defp fetch_txhash(params) do + {:txhash_param, Map.fetch(params, "txhash")} + end + + defp transaction_from_hash(transaction_hash) do + case Chain.hash_to_transaction(transaction_hash, necessity_by_association: %{block: :required}) do + {:error, :not_found} -> {:transaction, :error} + {:ok, transaction} -> {:transaction, {:ok, transaction}} + end + end + + defp to_transaction_hash(transaction_hash_string) do + {:format, Chain.string_to_transaction_hash(transaction_hash_string)} + end + + defp to_transaction_status(transaction_hash) do + case Chain.hash_to_transaction(transaction_hash) do + {:error, :not_found} -> "" + {:ok, transaction} -> transaction.status + end + end + + defp to_transaction_error(transaction_hash) do + with {:ok, transaction} <- Chain.hash_to_transaction(transaction_hash), + {:error, error} <- Chain.transaction_to_status(transaction) do + error + else + _ -> "" + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller.ex new file mode 100644 index 0000000..653eb14 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller.ex @@ -0,0 +1,69 @@ +defmodule BlockScoutWeb.API.V1.DecompiledSmartContractController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.API.APILogger + alias Explorer.Chain + alias Explorer.Chain.Hash.Address + + def create(conn, params) do + APILogger.log(conn) + + if auth_token(conn) == actual_token() do + with {:ok, hash} <- validate_address_hash(params["address_hash"]), + :ok <- Chain.check_address_exists(hash), + {:contract, :not_found} <- + {:contract, Chain.check_decompiled_contract_exists(params["address_hash"], params["decompiler_version"])} do + case Chain.create_decompiled_smart_contract(params) do + {:ok, decompiled_smart_contract} -> + send_resp(conn, :created, encode(decompiled_smart_contract)) + + {:error, changeset} -> + errors = + changeset.errors + |> Enum.into(%{}, fn {field, {message, _}} -> + {field, message} + end) + + send_resp(conn, :unprocessable_entity, encode(errors)) + end + else + :invalid_address -> + send_resp(conn, :unprocessable_entity, encode(%{error: "address_hash is invalid"})) + + :not_found -> + send_resp(conn, :unprocessable_entity, encode(%{error: "address is not found"})) + + {:contract, :ok} -> + send_resp( + conn, + :unprocessable_entity, + encode(%{error: "decompiled code already exists for the decompiler version"}) + ) + end + else + send_resp(conn, :forbidden, "") + end + end + + defp validate_address_hash(address_hash) do + case Address.cast(address_hash) do + {:ok, hash} -> {:ok, hash} + :error -> :invalid_address + end + end + + defp auth_token(conn) do + case get_req_header(conn, "auth_token") do + [token] -> token + other -> other + end + end + + defp actual_token do + Application.get_env(:block_scout_web, :decompiled_smart_contract_token) + end + + defp encode(data) do + Jason.encode!(data) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/gas_price_oracle_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/gas_price_oracle_controller.ex new file mode 100644 index 0000000..7b70b4b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/gas_price_oracle_controller.ex @@ -0,0 +1,48 @@ +defmodule BlockScoutWeb.API.V1.GasPriceOracleController do + use BlockScoutWeb, :controller + + alias Explorer.Chain.Cache.GasPriceOracle + + require Logger + + def gas_price_oracle(conn, _) do + case GasPriceOracle.get_gas_prices() do + {:ok, gas_prices} -> + send_with_content_type(conn, :ok, result(gas_prices)) + + nil -> + empty_gas_prices = %{ + "slow" => nil, + "average" => nil, + "fast" => nil + } + + send_with_content_type(conn, :internal_server_error, result(empty_gas_prices)) + + status -> + send_with_content_type(conn, :internal_server_error, error(status)) + end + end + + defp send_with_content_type(conn, status, result) do + conn + |> put_resp_content_type("application/json") + |> send_resp(status, result) + end + + def result(gas_prices) do + gas_prices + |> Jason.encode!() + end + + def error({:error, error}) do + Logger.error(fn -> ["Something went wrong while estimates gas prices in the gas price oracle: ", inspect(error)] end) + + %{ + "error_code" => 6001, + "error_title" => "Error", + "error_description" => "Internal server error" + } + |> Jason.encode!() + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/health_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/health_controller.ex new file mode 100644 index 0000000..52352d5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/health_controller.ex @@ -0,0 +1,64 @@ +defmodule BlockScoutWeb.API.V1.HealthController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.API.APILogger + alias Explorer.Chain + alias Timex.Duration + + def health(conn, _) do + APILogger.log(conn) + + with {:ok, number, timestamp} <- Chain.last_db_block_status(), + {:ok, cache_number, cache_timestamp} <- Chain.last_cache_block_status() do + send_resp(conn, :ok, result(number, timestamp, cache_number, cache_timestamp)) + else + status -> send_resp(conn, :internal_server_error, error(status)) + end + end + + def result(number, timestamp, cache_number, cache_timestamp) do + %{ + "healthy" => true, + "data" => %{ + "latest_block_number" => to_string(number), + "latest_block_inserted_at" => to_string(timestamp), + "cache_latest_block_number" => to_string(cache_number), + "cache_latest_block_inserted_at" => to_string(cache_timestamp) + } + } + |> Jason.encode!() + end + + def error({:error, :no_blocks}) do + %{ + "healthy" => false, + "error_code" => 5002, + "error_title" => "no blocks in db", + "error_description" => "There are no blocks in the DB" + } + |> Jason.encode!() + end + + def error({:error, number, timestamp}) do + healthy_blocks_period = Application.get_env(:explorer, :healthy_blocks_period) + + healthy_blocks_period_formatted = + healthy_blocks_period + |> Duration.from_milliseconds() + |> Duration.to_minutes() + |> trunc() + + %{ + "healthy" => false, + "error_code" => 5001, + "error_title" => "blocks fetching is stuck", + "error_description" => + "There are no new blocks in the DB for the last #{healthy_blocks_period_formatted} mins. Check the healthiness of Ethereum archive node or the Blockscout DB instance", + "data" => %{ + "latest_block_number" => to_string(number), + "latest_block_inserted_at" => to_string(timestamp) + } + } + |> Jason.encode!() + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/supply_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/supply_controller.ex new file mode 100644 index 0000000..869843a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/supply_controller.ex @@ -0,0 +1,14 @@ +defmodule BlockScoutWeb.API.V1.SupplyController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.API.APILogger + alias Explorer.Chain + + def supply(conn, _) do + APILogger.log(conn) + total_supply = Chain.total_supply() + circulating_supply = Chain.circulating_supply() + + render(conn, :supply, total: total_supply, circulating: circulating_supply) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex new file mode 100644 index 0000000..6794b51 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex @@ -0,0 +1,65 @@ +defmodule BlockScoutWeb.API.V1.VerifiedSmartContractController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.API.APILogger + alias Explorer.Chain + alias Explorer.Chain.Hash.Address + alias Explorer.SmartContract.Solidity.Publisher + + def create(conn, params) do + APILogger.log(conn) + + with {:ok, hash} <- validate_address_hash(params["address_hash"]), + :ok <- Chain.check_address_exists(hash), + {:contract, :not_found} <- {:contract, Chain.check_verified_smart_contract_exists(hash)} do + external_libraries = fetch_external_libraries(params) + + case Publisher.publish(hash, params, external_libraries) do + {:ok, _} -> + send_resp(conn, :created, encode(%{status: :success})) + + {:error, changeset} -> + errors = + changeset.errors + |> Enum.into(%{}, fn {field, {message, _}} -> + {field, message} + end) + + send_resp(conn, :unprocessable_entity, encode(errors)) + end + else + :invalid_address -> + send_resp(conn, :unprocessable_entity, encode(%{error: "address_hash is invalid"})) + + :not_found -> + send_resp(conn, :unprocessable_entity, encode(%{error: "address is not found"})) + + {:contract, :ok} -> + send_resp( + conn, + :unprocessable_entity, + encode(%{error: "verified code already exists for this address"}) + ) + end + end + + defp validate_address_hash(address_hash) do + case Address.cast(address_hash) do + {:ok, hash} -> {:ok, hash} + :error -> :invalid_address + end + end + + defp encode(data) do + Jason.encode!(data) + end + + defp fetch_external_libraries(params) do + keys = + Enum.flat_map(1..Application.get_env(:block_scout_web, :verification_max_libraries), fn i -> + ["library#{i}_name", "library#{i}_address"] + end) + + Map.take(params, keys) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex new file mode 100644 index 0000000..717210d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -0,0 +1,238 @@ +defmodule BlockScoutWeb.API.V2.AddressController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, + only: [ + next_page_params: 3, + paging_options: 1, + split_list_by_page: 1, + current_filter: 1 + ] + + alias BlockScoutWeb.API.V2.{AddressView, BlockView, TransactionView} + alias Explorer.{Chain, Market} + alias Indexer.Fetcher.TokenBalanceOnDemand + + @transaction_necessity_by_association [ + necessity_by_association: %{ + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + :block => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + } + ] + + @transaction_with_tt_necessity_by_association [ + necessity_by_association: %{ + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional, + [token_transfers: :token] => :optional, + [token_transfers: :to_address] => :optional, + [token_transfers: :from_address] => :optional, + [token_transfers: :token_contract_address] => :optional, + :block => :required + } + ] + + action_fallback(BlockScoutWeb.API.V2.FallbackController) + + def address(conn, %{"address_hash" => address_hash_string}) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:not_found, {:ok, address}} <- {:not_found, Chain.hash_to_address(address_hash)} do + conn + |> put_status(200) + |> render(:address, %{address: address}) + end + end + + def token_balances(conn, %{"address_hash" => address_hash_string}) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + token_balances = + address_hash + |> Chain.fetch_last_token_balances() + + Task.start_link(fn -> + TokenBalanceOnDemand.trigger_fetch(address_hash, token_balances) + end) + + token_balances_with_price = + token_balances + |> Market.add_price() + + conn + |> put_status(200) + |> render(:token_balances, %{token_balances: token_balances_with_price}) + end + end + + def transactions(conn, %{"address_hash" => address_hash_string} = params) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + options = + @transaction_necessity_by_association + |> Keyword.merge(paging_options(params)) + |> Keyword.merge(current_filter(params)) + + results_plus_one = Chain.address_to_transactions_with_rewards(address_hash, options) + {transactions, next_page} = split_list_by_page(results_plus_one) + + next_page_params = next_page_params(next_page, transactions, params) + + conn + |> put_status(200) + |> put_view(TransactionView) + |> render(:transactions, %{transactions: transactions, next_page_params: next_page_params}) + end + end + + def token_transfers(conn, %{"address_hash" => address_hash_string} = params) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + options = + @transaction_with_tt_necessity_by_association + |> Keyword.merge(paging_options(params)) + |> Keyword.merge(current_filter(params)) + + results_plus_one = + Chain.address_hash_to_token_transfers( + address_hash, + options + ) + + {transactions, next_page} = split_list_by_page(results_plus_one) + + next_page_params = next_page_params(next_page, transactions, params) + + conn + |> put_status(200) + |> put_view(TransactionView) + |> render(:transactions, %{transactions: transactions, next_page_params: next_page_params}) + end + end + + def internal_transactions(conn, %{"address_hash" => address_hash_string} = params) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + full_options = + [ + necessity_by_association: %{ + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + } + ] + |> Keyword.merge(paging_options(params)) + |> Keyword.merge(current_filter(params)) + + results_plus_one = Chain.address_to_internal_transactions(address_hash, full_options) + {internal_transactions, next_page} = split_list_by_page(results_plus_one) + + next_page_params = next_page_params(next_page, internal_transactions, params) + + conn + |> put_status(200) + |> put_view(TransactionView) + |> render(:internal_transactions, %{ + internal_transactions: internal_transactions, + next_page_params: next_page_params + }) + end + end + + def logs(conn, %{"address_hash" => address_hash_string, "topic" => topic} = params) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + prepared_topic = String.trim(topic) + + formatted_topic = if String.starts_with?(prepared_topic, "0x"), do: prepared_topic, else: "0x" <> prepared_topic + + results_plus_one = Chain.address_to_logs(address_hash, topic: formatted_topic) + + {logs, next_page} = split_list_by_page(results_plus_one) + + next_page_params = next_page_params(next_page, logs, params) + + conn + |> put_status(200) + |> put_view(TransactionView) + |> render(:logs, %{logs: logs, next_page_params: next_page_params}) + end + end + + def logs(conn, %{"address_hash" => address_hash_string} = params) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + results_plus_one = Chain.address_to_logs(address_hash, paging_options(params)) + {logs, next_page} = split_list_by_page(results_plus_one) + + next_page_params = next_page_params(next_page, logs, params) + + conn + |> put_status(200) + |> put_view(TransactionView) + |> render(:logs, %{logs: logs, next_page_params: next_page_params}) + end + end + + def blocks_validated(conn, %{"address_hash" => address_hash_string} = params) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + full_options = + Keyword.merge( + [ + necessity_by_association: %{ + miner: :required, + nephews: :optional, + transactions: :optional, + rewards: :optional + } + ], + paging_options(params) + ) + + results_plus_one = Chain.get_blocks_validated_by_address(full_options, address_hash) + {blocks, next_page} = split_list_by_page(results_plus_one) + + next_page_params = next_page_params(next_page, blocks, params) + + conn + |> put_status(200) + |> put_view(BlockView) + |> render(:blocks, %{blocks: blocks, next_page_params: next_page_params}) + end + end + + def coin_balance_history(conn, %{"address_hash" => address_hash_string} = params) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + full_options = paging_options(params) + + results_plus_one = Chain.address_to_coin_balances(address_hash, full_options) + + {coin_balances, next_page} = split_list_by_page(results_plus_one) + + next_page_params = next_page_params(next_page, coin_balances, params) + + conn + |> put_status(200) + |> put_view(AddressView) + |> render(:coin_balances, %{coin_balances: coin_balances, next_page_params: next_page_params}) + end + end + + def coin_balance_history_by_day(conn, %{"address_hash" => address_hash_string}) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + balances_by_day = + address_hash + |> Chain.address_to_balances_by_day(true) + + conn + |> put_status(200) + |> put_view(AddressView) + |> render(:coin_balances_by_day, %{coin_balances_by_day: balances_by_day}) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex new file mode 100644 index 0000000..24a3daf --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex @@ -0,0 +1,81 @@ +defmodule BlockScoutWeb.API.V2.BlockController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, + only: [next_page_params: 3, paging_options: 1, put_key_value_to_paging_options: 3, split_list_by_page: 1] + + import BlockScoutWeb.PagingHelper, only: [select_block_type: 1] + + alias BlockScoutWeb.API.V2.TransactionView + alias BlockScoutWeb.BlockTransactionController + alias Explorer.Chain + + @transaction_necessity_by_association [ + necessity_by_association: %{ + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + :block => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + } + ] + + action_fallback(BlockScoutWeb.API.V2.FallbackController) + + def block(conn, %{"block_hash_or_number" => block_hash_or_number}) do + with {:ok, block} <- + BlockTransactionController.param_block_hash_or_number_to_block(block_hash_or_number, + necessity_by_association: %{ + [miner: :names] => :required, + :uncles => :optional, + :nephews => :optional, + :rewards => :optional, + :transactions => :optional + } + ) do + conn + |> put_status(200) + |> render(:block, %{block: block}) + end + end + + def blocks(conn, params) do + full_options = select_block_type(params) + + blocks_plus_one = + full_options + |> Keyword.merge(paging_options(params)) + |> Chain.list_blocks() + + {blocks, next_page} = split_list_by_page(blocks_plus_one) + + next_page_params = next_page_params(next_page, blocks, params) + + conn + |> put_status(200) + |> render(:blocks, %{blocks: blocks, next_page_params: next_page_params}) + end + + def transactions(conn, %{"block_hash_or_number" => block_hash_or_number} = params) do + with {:ok, block} <- BlockTransactionController.param_block_hash_or_number_to_block(block_hash_or_number, []) do + full_options = + Keyword.merge( + @transaction_necessity_by_association, + put_key_value_to_paging_options(paging_options(params), :is_index_in_asc_order, true) + ) + + transactions_plus_one = Chain.block_to_transactions(block.hash, full_options, false) + + {transactions, next_page} = split_list_by_page(transactions_plus_one) + + next_page_params = next_page_params(next_page, transactions, params) + + conn + |> put_status(200) + |> put_view(TransactionView) + |> render(:transactions, %{transactions: transactions, next_page_params: next_page_params}) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex new file mode 100644 index 0000000..9d76eee --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/config_controller.ex @@ -0,0 +1,11 @@ +defmodule BlockScoutWeb.API.V2.ConfigController do + use BlockScoutWeb, :controller + + def json_rpc_url(conn, _params) do + json_rpc_url = Application.get_env(:block_scout_web, :json_rpc) + + conn + |> put_status(200) + |> render(:json_rpc_url, %{url: json_rpc_url}) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex new file mode 100644 index 0000000..20f9000 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex @@ -0,0 +1,38 @@ +defmodule BlockScoutWeb.API.V2.FallbackController do + use Phoenix.Controller + + alias BlockScoutWeb.API.V2.ApiView + + def call(conn, {:format, _}) do + conn + |> put_status(:unprocessable_entity) + |> put_view(ApiView) + |> render(:message, %{message: "Invalid parameter(s)"}) + end + + def call(conn, {:not_found, _}) do + conn + |> put_status(:not_found) + |> put_view(ApiView) + |> render(:message, %{message: "Not found"}) + end + + def call(conn, {:error, {:invalid, :hash}}) do + conn + |> put_status(:unprocessable_entity) + |> put_view(ApiView) + |> render(:message, %{message: "Invalid hash"}) + end + + def call(conn, {:error, {:invalid, :number}}) do + conn + |> put_status(:unprocessable_entity) + |> put_view(ApiView) + |> render(:message, %{message: "Invalid number"}) + end + + def call(conn, {:error, :not_found}) do + conn + |> call({:not_found, nil}) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex new file mode 100644 index 0000000..417eaf4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/main_page_controller.ex @@ -0,0 +1,40 @@ +defmodule BlockScoutWeb.API.V2.MainPageController do + use Phoenix.Controller + + alias Explorer.{Chain, PagingOptions} + alias BlockScoutWeb.API.V2.{BlockView, TransactionView} + alias Explorer.{Chain, Repo} + + def blocks(conn, _params) do + blocks = + [paging_options: %PagingOptions{page_size: 4}] + |> Chain.list_blocks() + |> Repo.preload([[miner: :names], :transactions, :rewards]) + + conn + |> put_status(200) + |> put_view(BlockView) + |> render(:blocks, %{blocks: blocks}) + end + + def transactions(conn, _params) do + recent_transactions = + Chain.recent_collated_transactions(false, + necessity_by_association: %{ + :block => :required, + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + }, + paging_options: %PagingOptions{page_size: 5} + ) + + conn + |> put_status(200) + |> put_view(TransactionView) + |> render(:transactions, %{transactions: recent_transactions}) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex new file mode 100644 index 0000000..59b06a3 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/search_controller.ex @@ -0,0 +1,24 @@ +defmodule BlockScoutWeb.API.V2.SearchController do + use Phoenix.Controller + + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + + alias Explorer.Chain + + def search(conn, %{"q" => query} = params) do + [paging_options: paging_options] = paging_options(params) + offset = (max(paging_options.page_number, 1) - 1) * paging_options.page_size + + search_results_plus_one = + paging_options + |> Chain.joint_search(offset, query) + + {search_results, next_page} = split_list_by_page(search_results_plus_one) + + next_page_params = next_page_params(next_page, search_results, params) + + conn + |> put_status(200) + |> render(:search_results, %{search_results: search_results, next_page_params: next_page_params}) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex new file mode 100644 index 0000000..fa1a46a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex @@ -0,0 +1,104 @@ +defmodule BlockScoutWeb.API.V2.StatsController do + use Phoenix.Controller + + alias BlockScoutWeb.API.V2.Helper + alias Explorer.{Chain, Market} + alias Explorer.Chain.Cache.Block, as: BlockCache + alias Explorer.Chain.Cache.{GasPriceOracle, GasUsage} + alias Explorer.Chain.Cache.Transaction, as: TransactionCache + alias Explorer.Chain.Supply.RSK + alias Explorer.Chain.Transaction.History.TransactionStats + alias Explorer.Counters.AverageBlockTime + alias Explorer.ExchangeRates.Token + alias Timex.Duration + + def stats(conn, _params) do + market_cap_type = + case Application.get_env(:explorer, :supply) do + RSK -> + RSK + + _ -> + :standard + end + + exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + + transaction_stats = Helper.get_transaction_stats() + + gas_prices = + case GasPriceOracle.get_gas_prices() do + {:ok, gas_prices} -> + gas_prices + + _ -> + nil + end + + gas_price = Application.get_env(:block_scout_web, :gas_price) + + json( + conn, + %{ + "total_blocks" => BlockCache.estimated_count() |> to_string(), + "total_addresses" => Chain.address_estimated_count() |> to_string(), + "total_transactions" => TransactionCache.estimated_count() |> to_string(), + "average_block_time" => AverageBlockTime.average_block_time() |> Duration.to_milliseconds(), + "coin_price" => exchange_rate.usd_value, + "total_gas_used" => GasUsage.total() |> to_string(), + "transactions_today" => Enum.at(transaction_stats, 0).number_of_transactions |> to_string(), + "gas_used_today" => Enum.at(transaction_stats, 0).gas_used, + "gas_prices" => gas_prices, + "static_gas_price" => gas_price, + "market_cap" => Helper.market_cap(market_cap_type, exchange_rate) + } + ) + end + + def transactions_chart(conn, _params) do + [{:history_size, history_size}] = + Application.get_env(:block_scout_web, BlockScoutWeb.Chain.TransactionHistoryChartController, [{:history_size, 30}]) + + today = Date.utc_today() + latest = Date.add(today, -1) + earliest = Date.add(latest, -1 * history_size) + + date_range = TransactionStats.by_date_range(earliest, latest) + + transaction_history_data = + date_range + |> Enum.map(fn row -> + %{date: row.date, tx_count: row.number_of_transactions} + end) + + json(conn, %{ + chart_data: transaction_history_data + }) + end + + def market_chart(conn, _params) do + exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + + recent_market_history = Market.fetch_recent_history() + + market_history_data = + recent_market_history + |> case do + [today | the_rest] -> + [%{today | closing_price: exchange_rate.usd_value} | the_rest] + + data -> + data + end + |> Enum.map(fn day -> Map.take(day, [:closing_price, :date]) end) + + json(conn, %{ + chart_data: market_history_data, + available_supply: available_supply(Chain.supply_for_days(), exchange_rate) + }) + end + + defp available_supply(:ok, exchange_rate), do: exchange_rate.available_supply || 0 + + defp available_supply({:ok, supply_for_days}, _exchange_rate), do: supply_for_days +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex new file mode 100644 index 0000000..cce9bd7 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -0,0 +1,221 @@ +defmodule BlockScoutWeb.API.V2.TransactionController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, only: [next_page_params: 3, paging_options: 1, split_list_by_page: 1] + + import BlockScoutWeb.PagingHelper, + only: [paging_options: 2, filter_options: 1, method_filter_options: 1, type_filter_options: 1] + + alias Explorer.Chain + alias Explorer.Chain.Import + alias Explorer.Chain.Import.Runner.InternalTransactions + + action_fallback(BlockScoutWeb.API.V2.FallbackController) + + @transaction_necessity_by_association %{ + :block => :optional, + [created_contract_address: :names] => :optional, + [created_contract_address: :token] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [to_address: :smart_contract] => :optional + } + + @token_transfers_neccessity_by_association %{ + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + from_address: :required, + to_address: :required, + token: :required + } + + @internal_transaction_neccessity_by_association [ + necessity_by_association: %{ + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [transaction: :block] => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + } + ] + + def transaction(conn, %{"transaction_hash" => transaction_hash_string}) do + with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, + {:not_found, {:ok, transaction}} <- + {:not_found, + Chain.hash_to_transaction( + transaction_hash, + necessity_by_association: @transaction_necessity_by_association + )}, + preloaded <- Chain.preload_token_transfers(transaction, @token_transfers_neccessity_by_association, false) do + conn + |> put_status(200) + |> render(:transaction, %{transaction: preloaded}) + end + end + + def transactions(conn, params) do + filter_options = filter_options(params) + method_filter_options = method_filter_options(params) + type_filter_options = type_filter_options(params) + + full_options = + Keyword.merge( + [ + necessity_by_association: @transaction_necessity_by_association + ], + paging_options(params, filter_options) + ) + + transactions_plus_one = + Chain.recent_transactions(full_options, filter_options, method_filter_options, type_filter_options) + + {transactions, next_page} = split_list_by_page(transactions_plus_one) + + next_page_params = next_page_params(next_page, transactions, params) + + conn + |> put_status(200) + |> render(:transactions, %{transactions: transactions, next_page_params: next_page_params}) + end + + def raw_trace(conn, %{"transaction_hash" => transaction_hash_string}) do + with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, + {:not_found, {:ok, transaction}} <- + {:not_found, Chain.hash_to_transaction(transaction_hash)} do + if is_nil(transaction.block_number) do + conn + |> put_status(200) + |> render(:raw_trace, %{internal_transactions: []}) + else + internal_transactions = Chain.all_transaction_to_internal_transactions(transaction_hash) + + first_trace_exists = + Enum.find_index(internal_transactions, fn trace -> + trace.index == 0 + end) + + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + internal_transactions = + if first_trace_exists do + internal_transactions + else + response = + Chain.fetch_first_trace( + [ + %{ + block_hash: transaction.block_hash, + block_number: transaction.block_number, + hash_data: transaction_hash_string, + transaction_index: transaction.index + } + ], + json_rpc_named_arguments + ) + + case response do + {:ok, first_trace_params} -> + InternalTransactions.run_insert_only(first_trace_params, %{ + timeout: :infinity, + timestamps: Import.timestamps(), + internal_transactions: %{params: first_trace_params} + }) + + Chain.all_transaction_to_internal_transactions(transaction_hash) + + {:error, _} -> + internal_transactions + + :ignore -> + internal_transactions + end + end + + conn + |> put_status(200) + |> render(:raw_trace, %{internal_transactions: internal_transactions}) + end + end + end + + def token_transfers(conn, %{"transaction_hash" => transaction_hash_string} = params) do + with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)} do + full_options = + Keyword.merge( + [ + necessity_by_association: @token_transfers_neccessity_by_association + ], + paging_options(params) + ) + + token_transfers_plus_one = Chain.transaction_to_token_transfers(transaction_hash, full_options) + + {token_transfers, next_page} = split_list_by_page(token_transfers_plus_one) + + next_page_params = next_page_params(next_page, token_transfers, params) + + conn + |> put_status(200) + |> render(:token_transfers, %{token_transfers: token_transfers, next_page_params: next_page_params}) + end + end + + def internal_transactions(conn, %{"transaction_hash" => transaction_hash_string} = params) do + with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)} do + full_options = + Keyword.merge( + @internal_transaction_neccessity_by_association, + paging_options(params) + ) + + internal_transactions_plus_one = Chain.transaction_to_internal_transactions(transaction_hash, full_options) + + {internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one) + + next_page_params = next_page_params(next_page, internal_transactions, params) + + conn + |> put_status(200) + |> render(:internal_transactions, %{ + internal_transactions: internal_transactions, + next_page_params: next_page_params + }) + end + end + + def logs(conn, %{"transaction_hash" => transaction_hash_string} = params) do + with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)} do + full_options = + Keyword.merge( + [ + necessity_by_association: %{ + [address: :names] => :optional, + [address: :smart_contract] => :optional, + address: :optional + } + ], + paging_options(params) + ) + + from_api = true + logs_plus_one = Chain.transaction_to_logs(transaction_hash, from_api, full_options) + + {logs, next_page} = split_list_by_page(logs_plus_one) + + next_page_params = next_page_params(next_page, logs, params) + + conn + |> put_status(200) + |> render(:logs, %{ + tx_hash: transaction_hash, + logs: logs, + next_page_params: next_page_params + }) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api_docs_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api_docs_controller.ex new file mode 100644 index 0000000..4a658c4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api_docs_controller.ex @@ -0,0 +1,18 @@ +defmodule BlockScoutWeb.APIDocsController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.Etherscan + alias Explorer.EthRPC + + def index(conn, _params) do + conn + |> assign(:documentation, Etherscan.get_documentation()) + |> render("index.html") + end + + def eth_rpc(conn, _params) do + conn + |> assign(:documentation, EthRPC.methods()) + |> render("eth_rpc.html") + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/block_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/block_controller.ex new file mode 100644 index 0000000..ed117ef --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/block_controller.ex @@ -0,0 +1,116 @@ +defmodule BlockScoutWeb.BlockController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + + alias BlockScoutWeb.{BlockView, Controller} + alias Explorer.Chain + alias Phoenix.View + + def index(conn, params) do + case params["block_type"] do + "Uncle" -> + uncle(conn, params) + + "Reorg" -> + reorg(conn, params) + + _ -> + [ + necessity_by_association: %{ + :transactions => :optional, + [miner: :names] => :optional, + :rewards => :optional + }, + block_type: "Block" + ] + |> handle_render(conn, params) + end + end + + def show(conn, %{"hash_or_number" => hash_or_number}) do + block_transaction_path = + conn + |> block_transaction_path(:index, hash_or_number) + |> Controller.full_path() + + redirect(conn, to: block_transaction_path) + end + + def reorg(conn, params) do + [ + necessity_by_association: %{ + :transactions => :optional, + [miner: :names] => :optional, + :rewards => :optional + }, + block_type: "Reorg" + ] + |> handle_render(conn, params) + end + + def uncle(conn, params) do + [ + necessity_by_association: %{ + :transactions => :optional, + [miner: :names] => :optional, + :nephews => :required, + :rewards => :optional + }, + block_type: "Uncle" + ] + |> handle_render(conn, params) + end + + defp handle_render(full_options, conn, %{"type" => "JSON"} = params) do + blocks_plus_one = + full_options + |> Keyword.merge(paging_options(params)) + |> Chain.list_blocks() + + {blocks, next_page} = split_list_by_page(blocks_plus_one) + + block_type = Keyword.get(full_options, :block_type, "Block") + + next_page_path = + case next_page_params(next_page, blocks, params) do + nil -> + nil + + next_page_params -> + params_with_block_type = + next_page_params + |> Map.delete("type") + |> Map.put("block_type", block_type) + + blocks_path( + conn, + :index, + params_with_block_type + ) + end + + json( + conn, + %{ + items: + Enum.map(blocks, fn block -> + View.render_to_string( + BlockView, + "_tile.html", + block: block, + block_type: block_type + ) + end), + next_page_path: next_page_path + } + ) + end + + defp handle_render(full_options, conn, _params) do + render(conn, "index.html", + current_path: Controller.current_full_path(conn), + block_type: Keyword.get(full_options, :block_type, "Block") + ) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex new file mode 100644 index 0000000..facda14 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex @@ -0,0 +1,169 @@ +defmodule BlockScoutWeb.BlockTransactionController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, + only: [paging_options: 1, put_key_value_to_paging_options: 3, next_page_params: 3, split_list_by_page: 1] + + import Explorer.Chain, only: [hash_to_block: 2, number_to_block: 2, string_to_block_hash: 1] + + alias BlockScoutWeb.{Controller, TransactionView} + alias Explorer.Chain + alias Phoenix.View + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number, "type" => "JSON"} = params) do + case param_block_hash_or_number_to_block(formatted_block_hash_or_number, []) do + {:ok, block} -> + full_options = + Keyword.merge( + [ + necessity_by_association: %{ + :block => :optional, + [created_contract_address: :names] => :optional, + [from_address: :names] => :required, + [to_address: :names] => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + } + ], + put_key_value_to_paging_options(paging_options(params), :is_index_in_asc_order, true) + ) + + transactions_plus_one = Chain.block_to_transactions(block.hash, full_options) + + {transactions, next_page} = split_list_by_page(transactions_plus_one) + + next_page_path = + case next_page_params(next_page, transactions, params) do + nil -> + nil + + next_page_params -> + block_transaction_path( + conn, + :index, + block, + Map.delete(next_page_params, "type") + ) + end + + items = + transactions + |> Enum.map(fn transaction -> + token_transfers_filtered_by_block_hash = + transaction.token_transfers + |> Enum.filter(fn token_transfer -> + token_transfer.block_hash == transaction.block_hash + end) + + transaction_with_transfers_filtered = + Map.put(transaction, :token_transfers, token_transfers_filtered_by_block_hash) + + View.render_to_string( + TransactionView, + "_tile.html", + transaction: transaction_with_transfers_filtered, + burn_address_hash: @burn_address_hash, + conn: conn + ) + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_path + } + ) + + {:error, {:invalid, :hash}} -> + not_found(conn) + + {:error, {:invalid, :number}} -> + not_found(conn) + + {:error, :not_found} -> + conn + |> put_status(:not_found) + |> render( + "404.html", + block: nil, + block_above_tip: block_above_tip(formatted_block_hash_or_number) + ) + end + end + + def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number}) do + case param_block_hash_or_number_to_block(formatted_block_hash_or_number, + necessity_by_association: %{ + [miner: :names] => :required, + :uncles => :optional, + :nephews => :optional, + :rewards => :optional + } + ) do + {:ok, block} -> + block_transaction_count = Chain.block_to_transaction_count(block.hash) + + render( + conn, + "index.html", + block: block, + block_transaction_count: block_transaction_count, + current_path: Controller.current_full_path(conn) + ) + + {:error, {:invalid, :hash}} -> + not_found(conn) + + {:error, {:invalid, :number}} -> + not_found(conn) + + {:error, :not_found} -> + conn + |> put_status(:not_found) + |> render( + "404.html", + block: nil, + block_above_tip: block_above_tip(formatted_block_hash_or_number) + ) + end + end + + def param_block_hash_or_number_to_block("0x" <> _ = param, options) do + case string_to_block_hash(param) do + {:ok, hash} -> + hash_to_block(hash, options) + + :error -> + {:error, {:invalid, :hash}} + end + end + + def param_block_hash_or_number_to_block(number_string, options) + when is_binary(number_string) do + case BlockScoutWeb.Chain.param_to_block_number(number_string) do + {:ok, number} -> + number_to_block(number, options) + + {:error, :invalid} -> + {:error, {:invalid, :number}} + end + end + + defp block_above_tip("0x" <> _), do: {:error, :hash} + + defp block_above_tip(block_hash_or_number) when is_binary(block_hash_or_number) do + case Chain.max_consensus_block_number() do + {:ok, max_consensus_block_number} -> + {block_number, _} = Integer.parse(block_hash_or_number) + {:ok, block_number > max_consensus_block_number} + + {:error, :not_found} -> + {:ok, true} + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex new file mode 100644 index 0000000..238f858 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain/market_history_chart_controller.ex @@ -0,0 +1,53 @@ +defmodule BlockScoutWeb.Chain.MarketHistoryChartController do + use BlockScoutWeb, :controller + + alias Explorer.{Chain, Market} + alias Explorer.ExchangeRates.Token + + def show(conn, _params) do + if ajax?(conn) do + exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + + recent_market_history = Market.fetch_recent_history() + + market_history_data = + case recent_market_history do + [today | the_rest] -> + encode_market_history_data([%{today | closing_price: exchange_rate.usd_value} | the_rest]) + + data -> + encode_market_history_data(data) + end + + json(conn, %{ + history_data: market_history_data, + supply_data: available_supply(Chain.supply_for_days(), exchange_rate) + }) + else + unprocessable_entity(conn) + end + end + + defp available_supply(:ok, exchange_rate) do + to_string(exchange_rate.available_supply || 0) + end + + defp available_supply({:ok, supply_for_days}, _exchange_rate) do + supply_for_days + |> Jason.encode() + |> case do + {:ok, data} -> data + _ -> [] + end + end + + defp encode_market_history_data(market_history_data) do + market_history_data + |> Enum.map(fn day -> Map.take(day, [:closing_price, :date]) end) + |> Jason.encode() + |> case do + {:ok, data} -> data + _ -> [] + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain/transaction_history_chart_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain/transaction_history_chart_controller.ex new file mode 100644 index 0000000..dec6f4f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain/transaction_history_chart_controller.ex @@ -0,0 +1,43 @@ +defmodule BlockScoutWeb.Chain.TransactionHistoryChartController do + use BlockScoutWeb, :controller + + alias Explorer.Chain.Transaction.History.TransactionStats + + def show(conn, _params) do + if ajax?(conn) do + [{:history_size, history_size}] = Application.get_env(:block_scout_web, __MODULE__, [{:history_size, 30}]) + + today = Date.utc_today() + latest = Date.add(today, -1) + earliest = Date.add(latest, -1 * history_size) + + date_range = TransactionStats.by_date_range(earliest, latest) + + transaction_history_data = + date_range + |> extract_history + |> encode_transaction_history_data + + json(conn, %{ + history_data: transaction_history_data + }) + else + unprocessable_entity(conn) + end + end + + defp extract_history(db_results) do + Enum.map(db_results, fn row -> + %{date: row.date, number_of_transactions: row.number_of_transactions} + end) + end + + defp encode_transaction_history_data(transaction_history_data) do + transaction_history_data + |> Jason.encode() + |> case do + {:ok, data} -> data + _ -> [] + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex new file mode 100644 index 0000000..61ac896 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex @@ -0,0 +1,178 @@ +defmodule BlockScoutWeb.ChainController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, only: [paging_options: 1] + + alias BlockScoutWeb.API.V2.Helper + alias BlockScoutWeb.{ChainView, Controller} + alias Explorer.{Chain, PagingOptions, Repo} + alias Explorer.Chain.{Address, Block, Transaction} + alias Explorer.Chain.Cache.Block, as: BlockCache + alias Explorer.Chain.Cache.GasUsage + alias Explorer.Chain.Cache.Transaction, as: TransactionCache + alias Explorer.Chain.Supply.RSK + alias Explorer.Counters.AverageBlockTime + alias Explorer.ExchangeRates.Token + alias Explorer.Market + alias Phoenix.View + + def show(conn, _params) do + transaction_estimated_count = TransactionCache.estimated_count() + total_gas_usage = GasUsage.total() + block_count = BlockCache.estimated_count() + address_count = Chain.address_estimated_count() + + market_cap_calculation = + case Application.get_env(:explorer, :supply) do + RSK -> + RSK + + _ -> + :standard + end + + exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + + transaction_stats = Helper.get_transaction_stats() + + chart_data_paths = %{ + market: market_history_chart_path(conn, :show), + transaction: transaction_history_chart_path(conn, :show) + } + + chart_config = Application.get_env(:block_scout_web, :chart_config, %{}) + + render( + conn, + "show.html", + address_count: address_count, + average_block_time: AverageBlockTime.average_block_time(), + exchange_rate: exchange_rate, + chart_config: chart_config, + chart_config_json: Jason.encode!(chart_config), + chart_data_paths: chart_data_paths, + market_cap_calculation: market_cap_calculation, + transaction_estimated_count: transaction_estimated_count, + total_gas_usage: total_gas_usage, + transactions_path: recent_transactions_path(conn, :index), + transaction_stats: transaction_stats, + block_count: block_count, + gas_price: Application.get_env(:block_scout_web, :gas_price) + ) + end + + def search(conn, %{"q" => ""}) do + show(conn, []) + end + + def search(conn, %{"q" => query}) do + query + |> String.trim() + |> BlockScoutWeb.Chain.from_param() + |> case do + {:ok, item} -> + redirect_search_results(conn, item) + + {:error, :not_found} -> + search_path = + conn + |> search_path(:search_results, q: query) + |> Controller.full_path() + + redirect(conn, to: search_path) + end + end + + def search(conn, _), do: not_found(conn) + + def token_autocomplete(conn, %{"q" => term} = params) when is_binary(term) do + [paging_options: paging_options] = paging_options(params) + offset = (max(paging_options.page_number, 1) - 1) * paging_options.page_size + + results = + paging_options + |> Chain.joint_search(offset, term) + + encoded_results = + results + |> Enum.map(fn item -> + tx_hash_bytes = Map.get(item, :tx_hash) + block_hash_bytes = Map.get(item, :block_hash) + + item = + if tx_hash_bytes do + item + |> Map.replace(:tx_hash, "0x" <> Base.encode16(tx_hash_bytes, case: :lower)) + else + item + end + + item = + if block_hash_bytes do + item + |> Map.replace(:block_hash, "0x" <> Base.encode16(block_hash_bytes, case: :lower)) + else + item + end + + item + end) + + json(conn, encoded_results) + end + + def token_autocomplete(conn, _) do + json(conn, "{}") + end + + def chain_blocks(conn, _params) do + if ajax?(conn) do + blocks = + [paging_options: %PagingOptions{page_size: 4}] + |> Chain.list_blocks() + |> Repo.preload([[miner: :names], :transactions, :rewards]) + |> Enum.map(fn block -> + %{ + chain_block_html: + View.render_to_string( + ChainView, + "_block.html", + block: block + ), + block_number: block.number + } + end) + + json(conn, %{blocks: blocks}) + else + unprocessable_entity(conn) + end + end + + defp redirect_search_results(conn, %Address{} = item) do + address_path = + conn + |> address_path(:show, item) + |> Controller.full_path() + + redirect(conn, to: address_path) + end + + defp redirect_search_results(conn, %Block{} = item) do + block_path = + conn + |> block_path(:show, item) + |> Controller.full_path() + + redirect(conn, to: block_path) + end + + defp redirect_search_results(conn, %Transaction{} = item) do + transaction_path = + conn + |> transaction_path(:show, item) + |> Controller.full_path() + + redirect(conn, to: transaction_path) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/common_components_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/common_components_controller.ex new file mode 100644 index 0000000..379bd15 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/common_components_controller.ex @@ -0,0 +1,12 @@ +defmodule BlockScoutWeb.CommonComponentsController do + use BlockScoutWeb, :controller + + def index(conn, params) do + [] + |> handle_render(conn, params) + end + + defp handle_render(_full_options, conn, _params) do + render(conn, "index.html") + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/csv_export_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/csv_export_controller.ex new file mode 100644 index 0000000..8cab1d2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/csv_export_controller.ex @@ -0,0 +1,35 @@ +defmodule BlockScoutWeb.CsvExportController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.AccessHelpers + alias Explorer.Chain + + def index(conn, %{"address" => address_hash_string, "type" => type} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + :ok <- Chain.check_address_exists(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params), + true <- supported_export_type(type) do + render(conn, "index.html", address_hash_string: address_hash_string, type: type) + else + _ -> + not_found(conn) + end + end + + def index(conn, _params) do + not_found(conn) + end + + defp supported_export_type(type) do + Enum.member?(supported_types(), type) + end + + defp supported_types do + [ + "internal-transactions", + "transactions", + "token-transfers", + "logs" + ] + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/page_not_found_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/page_not_found_controller.ex new file mode 100644 index 0000000..fabcb3b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/page_not_found_controller.ex @@ -0,0 +1,9 @@ +defmodule BlockScoutWeb.PageNotFoundController do + use BlockScoutWeb, :controller + + def index(conn, _params) do + conn + |> put_status(:not_found) + |> render("index.html", token: nil) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex new file mode 100644 index 0000000..906fbc3 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex @@ -0,0 +1,69 @@ +defmodule BlockScoutWeb.PendingTransactionController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + + alias BlockScoutWeb.{Controller, TransactionView} + alias Explorer.Chain + alias Phoenix.View + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + def index(conn, %{"type" => "JSON"} = params) do + full_options = + Keyword.merge( + [ + necessity_by_association: %{ + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [created_contract_address: :names] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + } + ], + paging_options(params) + ) + + {transactions, next_page} = get_pending_transactions_and_next_page(full_options) + + next_page_url = + case next_page_params(next_page, transactions, params) do + nil -> + nil + + next_page_params -> + pending_transaction_path( + conn, + :index, + Map.delete(next_page_params, "type") + ) + end + + json( + conn, + %{ + items: + Enum.map(transactions, fn transaction -> + View.render_to_string( + TransactionView, + "_tile.html", + transaction: transaction, + burn_address_hash: @burn_address_hash, + conn: conn + ) + end), + next_page_path: next_page_url + } + ) + end + + def index(conn, _params) do + render(conn, "index.html", current_path: Controller.current_full_path(conn)) + end + + defp get_pending_transactions_and_next_page(options) do + transactions_plus_one = Chain.recent_pending_transactions(options, true) + split_list_by_page(transactions_plus_one) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex new file mode 100644 index 0000000..d692d6d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/recent_transactions_controller.ex @@ -0,0 +1,45 @@ +defmodule BlockScoutWeb.RecentTransactionsController do + use BlockScoutWeb, :controller + + alias Explorer.{Chain, PagingOptions} + alias Explorer.Chain.Hash + alias Phoenix.View + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + def index(conn, _params) do + if ajax?(conn) do + recent_transactions = + Chain.recent_collated_transactions(true, + necessity_by_association: %{ + :block => :required, + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + }, + paging_options: %PagingOptions{page_size: 5} + ) + + transactions = + Enum.map(recent_transactions, fn transaction -> + %{ + transaction_hash: Hash.to_string(transaction.hash), + transaction_html: + View.render_to_string(BlockScoutWeb.TransactionView, "_tile.html", + transaction: transaction, + burn_address_hash: @burn_address_hash, + conn: conn + ) + } + end) + + json(conn, %{transactions: transactions}) + else + unprocessable_entity(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex new file mode 100644 index 0000000..b2f639a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/search_controller.ex @@ -0,0 +1,77 @@ +defmodule BlockScoutWeb.SearchController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + + alias BlockScoutWeb.{Controller, SearchView} + alias Explorer.Chain + alias Phoenix.View + + def search_results(conn, %{"q" => query, "type" => "JSON"} = params) do + [paging_options: paging_options] = paging_options(params) + offset = (max(paging_options.page_number, 1) - 1) * paging_options.page_size + + search_results_plus_one = + paging_options + |> Chain.joint_search(offset, query) + + {search_results, next_page} = split_list_by_page(search_results_plus_one) + + next_page_url = + case next_page_params(next_page, search_results, params) do + nil -> + nil + + next_page_params -> + search_path(conn, :search_results, Map.delete(next_page_params, "type")) + end + + items = + search_results + |> Enum.with_index(1) + |> Enum.map(fn {result, _index} -> + View.render_to_string( + SearchView, + "_tile.html", + result: result, + conn: conn, + query: query + ) + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_url + } + ) + end + + def search_results(conn, %{"type" => "JSON"}) do + json( + conn, + %{ + items: [] + } + ) + end + + def search_results(conn, %{"q" => query}) do + render( + conn, + "results.html", + query: query, + current_path: Controller.current_full_path(conn) + ) + end + + def search_results(conn, %{}) do + render( + conn, + "results.html", + query: nil, + current_path: Controller.current_full_path(conn) + ) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex new file mode 100644 index 0000000..ab3819c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/smart_contract_controller.ex @@ -0,0 +1,252 @@ +defmodule BlockScoutWeb.SmartContractController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.AddressView + alias Explorer.Chain + alias Explorer.SmartContract.{Reader, Writer} + + import Explorer.SmartContract.Solidity.Verifier, only: [parse_boolean: 1] + + @burn_address "0x0000000000000000000000000000000000000000" + + def index(conn, %{"hash" => address_hash_string, "type" => contract_type, "action" => action} = params) do + address_options = [ + necessity_by_association: %{ + :smart_contract => :optional + } + ] + + is_custom_abi = parse_boolean(params["is_custom_abi"]) + + with true <- ajax?(conn), + {:custom_abi, false} <- {:custom_abi, is_custom_abi}, + {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, address} <- Chain.find_contract_address(address_hash, address_options, true) do + implementation_address_hash_string = + if contract_type == "proxy" do + address.hash + |> Chain.get_implementation_address_hash(address.smart_contract.abi) + |> Tuple.to_list() + |> List.first() || @burn_address + else + @burn_address + end + + functions = + if action == "write" do + if contract_type == "proxy" do + Writer.write_functions_proxy(implementation_address_hash_string) + else + Writer.write_functions(address_hash) + end + else + if contract_type == "proxy" do + Reader.read_only_functions_proxy(address_hash, implementation_address_hash_string) + else + Reader.read_only_functions(address_hash) + end + end + + read_functions_required_wallet = + if action == "read" do + if contract_type == "proxy" do + Reader.read_functions_required_wallet_proxy(implementation_address_hash_string) + else + Reader.read_functions_required_wallet(address_hash) + end + else + [] + end + + contract_abi = Poison.encode!(address.smart_contract.abi) + + implementation_abi = + if contract_type == "proxy" do + implementation_address_hash_string + |> Chain.get_implementation_abi() + |> Poison.encode!() + else + [] + end + + conn + |> put_status(200) + |> put_layout(false) + |> render( + "_functions.html", + read_functions_required_wallet: read_functions_required_wallet, + read_only_functions: functions, + address: address, + contract_abi: contract_abi, + implementation_address: implementation_address_hash_string, + implementation_abi: implementation_abi, + contract_type: contract_type, + action: action + ) + else + {:custom_abi, true} -> + custom_abi_render(conn, params) + + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + + _ -> + not_found(conn) + end + end + + def index(conn, _), do: not_found(conn) + + defp custom_abi_render(conn, %{"hash" => address_hash_string, "type" => contract_type, "action" => action}) do + with custom_abi <- AddressView.fetch_custom_abi(conn, address_hash_string), + false <- is_nil(custom_abi), + abi <- custom_abi.abi, + {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string) do + functions = + if action == "write" do + Writer.filter_write_functions(abi) + else + Reader.read_only_functions_from_abi(abi, address_hash) + end + + read_functions_required_wallet = + if action == "read" do + Reader.read_functions_required_wallet_from_abi(abi) + else + [] + end + + contract_abi = Poison.encode!(abi) + + conn + |> put_status(200) + |> put_layout(false) + |> render( + "_functions.html", + read_functions_required_wallet: read_functions_required_wallet, + read_only_functions: functions, + address: %{hash: address_hash}, + custom_abi: true, + contract_abi: contract_abi, + implementation_address: @burn_address, + implementation_abi: [], + contract_type: contract_type, + action: action + ) + else + :error -> + unprocessable_entity(conn) + + _ -> + not_found(conn) + end + end + + def show(conn, params) do + address_options = [ + necessity_by_association: %{ + :contracts_creation_internal_transaction => :optional, + :names => :optional, + :smart_contract => :optional, + :token => :optional, + :contracts_creation_transaction => :optional + } + ] + + custom_abi = + if parse_boolean(params["is_custom_abi"]), do: AddressView.fetch_custom_abi(conn, params["id"]), else: nil + + with true <- ajax?(conn), + {:ok, address_hash} <- Chain.string_to_address_hash(params["id"]), + {:ok, _address} <- Chain.find_contract_address(address_hash, address_options, true) do + contract_type = if params["type"] == "proxy", do: :proxy, else: :regular + + args = + if is_nil(params["args_count"]) do + # we should convert: %{"0" => _, "1" => _} to [_, _] + params["args"] |> convert_map_to_array() + else + {args_count, _} = Integer.parse(params["args_count"]) + + if args_count < 1, + do: [], + else: for(x <- 0..(args_count - 1), do: params["arg_" <> to_string(x)] |> convert_map_to_array()) + end + + %{output: outputs, names: names} = + if custom_abi do + Reader.query_function_with_names_custom_abi( + address_hash, + %{method_id: params["method_id"], args: args}, + params["from"], + custom_abi.abi + ) + else + Reader.query_function_with_names( + address_hash, + %{method_id: params["method_id"], args: args}, + contract_type, + params["from"] + ) + end + + conn + |> put_status(200) + |> put_layout(false) + |> render( + "_function_response.html", + function_name: params["function_name"], + method_id: params["method_id"], + outputs: outputs, + names: names, + smart_contract_address: address_hash + ) + else + :error -> + unprocessable_entity(conn) + + :not_found -> + not_found(conn) + + _ -> + not_found(conn) + end + end + + defp convert_map_to_array(map) do + if is_turned_out_array?(map) do + map |> Map.values() |> try_to_map_elements() + else + try_to_map_elements(map) + end + end + + defp try_to_map_elements(values) do + if Enumerable.impl_for(values) do + Enum.map(values, &convert_map_to_array/1) + else + values + end + end + + defp is_turned_out_array?(map) when is_map(map), do: Enum.all?(Map.keys(map), &is_integer?/1) + + defp is_turned_out_array?(_), do: false + + defp is_integer?(string) when is_binary(string) do + case string |> String.trim() |> Integer.parse() do + {_, ""} -> + true + + _ -> + false + end + end + + defp is_integer?(integer) when is_integer(integer), do: true + + defp is_integer?(_), do: false +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/contract_controller.ex new file mode 100644 index 0000000..9a6bee2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/contract_controller.ex @@ -0,0 +1,56 @@ +defmodule BlockScoutWeb.Tokens.ContractController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, TabHelpers} + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + + def index(conn, %{"token_id" => address_hash_string} = params) do + options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + :ok <- Chain.check_verified_smart_contract_exists(address_hash), + {:ok, token} <- Chain.token_from_address_hash(address_hash, options), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + %{type: type, action: action} = + cond do + TabHelpers.tab_active?("read-contract", conn.request_path) -> + %{type: :regular, action: :read} + + TabHelpers.tab_active?("write-contract", conn.request_path) -> + %{type: :regular, action: :write} + + TabHelpers.tab_active?("read-proxy", conn.request_path) -> + %{type: :proxy, action: :read} + + TabHelpers.tab_active?("write-proxy", conn.request_path) -> + %{type: :proxy, action: :write} + end + + render( + conn, + "index.html", + type: type, + action: action, + token: Market.add_price(token), + counters_path: token_path(conn, :token_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + {:restricted_access, _} -> + not_found(conn) + + :not_found -> + not_found(conn) + + :error -> + not_found(conn) + + {:error, :not_found} -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex new file mode 100644 index 0000000..710ff74 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex @@ -0,0 +1,86 @@ +defmodule BlockScoutWeb.Tokens.HolderController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, Controller} + alias BlockScoutWeb.Tokens.HolderView + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Phoenix.View + + import BlockScoutWeb.Chain, + only: [ + split_list_by_page: 1, + paging_options: 1, + next_page_params: 3 + ] + + def index(conn, %{"token_id" => address_hash_string, "type" => "JSON"} = params) do + from_api = false + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, token} <- Chain.token_from_address_hash(address_hash), + token_balances <- Chain.fetch_token_holders_from_token_hash(address_hash, from_api, paging_options(params)), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + {token_balances_paginated, next_page} = split_list_by_page(token_balances) + + next_page_path = + case next_page_params(next_page, token_balances_paginated, params) do + nil -> + nil + + next_page_params -> + token_holder_path(conn, :index, address_hash, Map.delete(next_page_params, "type")) + end + + token_balances_json = + Enum.map(token_balances_paginated, fn token_balance -> + View.render_to_string(HolderView, "_token_balances.html", + address_hash: address_hash, + token_balance: token_balance, + token: token, + conn: conn + ) + end) + + json(conn, %{items: token_balances_json, next_page_path: next_page_path}) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + not_found(conn) + + {:error, :not_found} -> + not_found(conn) + end + end + + def index(conn, %{"token_id" => address_hash_string} = params) do + options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, token} <- Chain.token_from_address_hash(address_hash, options), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + current_path: Controller.current_full_path(conn), + token: Market.add_price(token), + counters_path: token_path(conn, :token_counters, %{"id" => Address.checksum(address_hash)}), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + not_found(conn) + + {:error, :not_found} -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/holder_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/holder_controller.ex new file mode 100644 index 0000000..3ee291f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/holder_controller.ex @@ -0,0 +1,78 @@ +defmodule BlockScoutWeb.Tokens.Instance.HolderController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.Controller + alias BlockScoutWeb.Tokens.HolderView + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Phoenix.View + + import BlockScoutWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3] + + def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id, "type" => "JSON"} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(token_address_hash), + {:ok, token} <- Chain.token_from_address_hash(address_hash), + token_holders <- + Chain.fetch_token_holders_from_token_hash_and_token_id(address_hash, token_id, paging_options(params)) do + {token_holders_paginated, next_page} = split_list_by_page(token_holders) + + next_page_path = + case next_page_params(next_page, token_holders_paginated, params) do + nil -> + nil + + next_page_params -> + token_instance_holder_path( + conn, + :index, + Address.checksum(token.contract_address_hash), + token_id, + Map.delete(next_page_params, "type") + ) + end + + holders_json = + token_holders_paginated + |> Enum.sort_by(& &1.value, &>=/2) + |> Enum.map(fn current_token_balance -> + View.render_to_string( + HolderView, + "_token_balances.html", + address_hash: address_hash, + token_balance: current_token_balance, + token: token + ) + end) + + json(conn, %{items: holders_json, next_page_path: next_page_path}) + else + _ -> + not_found(conn) + end + end + + def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id}) do + options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] + + with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), + {:ok, token} <- Chain.token_from_address_hash(hash, options), + {:ok, token_instance} <- + Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, hash) do + render( + conn, + "index.html", + token_instance: %{instance: token_instance, token_id: Decimal.new(token_id)}, + current_path: Controller.current_full_path(conn), + token: Market.add_price(token), + total_token_transfers: Chain.count_token_transfers_from_token_hash_and_token_id(hash, token_id) + ) + else + _ -> + not_found(conn) + end + end + + def index(conn, _) do + not_found(conn) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/metadata_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/metadata_controller.ex new file mode 100644 index 0000000..b404cc9 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/metadata_controller.ex @@ -0,0 +1,35 @@ +defmodule BlockScoutWeb.Tokens.Instance.MetadataController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.Controller + alias Explorer.{Chain, Market} + + def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id}) do + options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] + + with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), + {:ok, token} <- Chain.token_from_address_hash(hash, options), + {:ok, token_instance} <- + Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, hash) do + if token_instance.metadata do + render( + conn, + "index.html", + token_instance: %{instance: token_instance, token_id: Decimal.new(token_id)}, + current_path: Controller.current_full_path(conn), + token: Market.add_price(token), + total_token_transfers: Chain.count_token_transfers_from_token_hash_and_token_id(hash, token_id) + ) + else + not_found(conn) + end + else + _ -> + not_found(conn) + end + end + + def index(conn, _) do + not_found(conn) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex new file mode 100644 index 0000000..1e92527 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance/transfer_controller.ex @@ -0,0 +1,80 @@ +defmodule BlockScoutWeb.Tokens.Instance.TransferController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.Controller + alias BlockScoutWeb.Tokens.TransferView + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Phoenix.View + + import BlockScoutWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3] + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id, "type" => "JSON"} = params) do + with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), + {:ok, token} <- Chain.token_from_address_hash(hash), + token_transfers <- + Chain.fetch_token_transfers_from_token_hash_and_token_id(hash, token_id, paging_options(params)) do + {token_transfers_paginated, next_page} = split_list_by_page(token_transfers) + + next_page_path = + case next_page_params(next_page, token_transfers_paginated, params) do + nil -> + nil + + next_page_params -> + token_instance_transfer_path( + conn, + :index, + Address.checksum(token.contract_address_hash), + token_id, + Map.delete(next_page_params, "type") + ) + end + + transfers_json = + Enum.map(token_transfers_paginated, fn transfer -> + View.render_to_string( + TransferView, + "_token_transfer.html", + conn: conn, + token: token, + token_transfer: transfer, + burn_address_hash: @burn_address_hash + ) + end) + + json(conn, %{items: transfers_json, next_page_path: next_page_path}) + else + _ -> + not_found(conn) + end + end + + def index(conn, %{"token_id" => token_address_hash, "instance_id" => token_id}) do + options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] + + with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), + {:ok, token} <- Chain.token_from_address_hash(hash, options), + {:ok, token_instance} <- + Chain.erc721_or_erc1155_token_instance_from_token_id_and_token_address(token_id, hash) do + render( + conn, + "index.html", + token_instance: %{instance: token_instance, token_id: Decimal.new(token_id)}, + current_path: Controller.current_full_path(conn), + token: Market.add_price(token), + total_token_transfers: Chain.count_token_transfers_from_token_hash_and_token_id(hash, token_id) + ) + else + _ -> + not_found(conn) + end + end + + def index(conn, _) do + not_found(conn) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex new file mode 100644 index 0000000..d7d0a4d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/instance_controller.ex @@ -0,0 +1,26 @@ +defmodule BlockScoutWeb.Tokens.InstanceController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.Controller + alias Explorer.Chain + + def show(conn, %{"token_id" => token_address_hash, "id" => token_id}) do + with {:ok, hash} <- Chain.string_to_address_hash(token_address_hash), + :ok <- Chain.check_token_exists(hash), + :ok <- Chain.check_erc721_or_erc1155_token_instance_exists(token_id, hash) do + token_instance_transfer_path = + conn + |> token_instance_transfer_path(:index, token_address_hash, token_id) + |> Controller.full_path() + + redirect(conn, to: token_instance_transfer_path) + else + _ -> + not_found(conn) + end + end + + def show(conn, _) do + not_found(conn) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex new file mode 100644 index 0000000..5129856 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/inventory_controller.ex @@ -0,0 +1,87 @@ +defmodule BlockScoutWeb.Tokens.InventoryController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.AccessHelpers + alias BlockScoutWeb.Tokens.{HolderController, InventoryView} + alias Explorer.Chain + alias Explorer.Chain.TokenTransfer + alias Phoenix.View + + import BlockScoutWeb.Chain, only: [split_list_by_page: 1, default_paging_options: 0] + + def index(conn, %{"token_id" => address_hash_string, "type" => "JSON"} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, token} <- Chain.token_from_address_hash(address_hash), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + unique_tokens = + Chain.address_to_unique_tokens( + token.contract_address_hash, + unique_tokens_paging_options(params) + ) + + {unique_tokens_paginated, next_page} = split_list_by_page(unique_tokens) + + next_page_path = + case unique_tokens_next_page(next_page, unique_tokens_paginated, params) do + nil -> + nil + + next_page_params -> + token_inventory_path( + conn, + :index, + address_hash_string, + Map.delete(next_page_params, "type") + ) + end + + items = + unique_tokens_paginated + |> Enum.map(fn token_transfer -> + View.render_to_string( + InventoryView, + "_token.html", + token_transfer: token_transfer, + token: token, + conn: conn + ) + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_path + } + ) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + not_found(conn) + + {:error, :not_found} -> + not_found(conn) + end + end + + def index(conn, params) do + HolderController.index(conn, params) + end + + defp unique_tokens_paging_options(%{"unique_token" => token_id}), + do: [paging_options: %{default_paging_options() | key: {token_id}}] + + defp unique_tokens_paging_options(_params), do: [paging_options: default_paging_options()] + + defp unique_tokens_next_page([], _list, _params), do: nil + + defp unique_tokens_next_page(_, list, params) do + Map.merge(params, paging_params(List.last(list))) + end + + defp paging_params(%TokenTransfer{token_id: token_id}) do + %{"unique_token" => Decimal.to_integer(token_id)} + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/token_controller.ex new file mode 100644 index 0000000..6a1ea07 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/token_controller.ex @@ -0,0 +1,55 @@ +defmodule BlockScoutWeb.Tokens.TokenController do + use BlockScoutWeb, :controller + + require Logger + + alias BlockScoutWeb.AccessHelpers + alias Explorer.Chain + alias Explorer.Counters.{TokenHoldersCounter, TokenTransfersCounter} + + def show(conn, %{"id" => address_hash_string}) do + redirect(conn, to: AccessHelpers.get_path(conn, :token_transfer_path, :index, address_hash_string)) + end + + def token_counters(conn, %{"id" => address_hash_string}) do + case Chain.string_to_address_hash(address_hash_string) do + {:ok, address_hash} -> + {transfer_count, token_holder_count} = fetch_token_counters(address_hash, 30_000) + + json(conn, %{transfer_count: transfer_count, token_holder_count: token_holder_count}) + + _ -> + not_found(conn) + end + end + + defp fetch_token_counters(address_hash, timeout) do + total_token_transfers_task = + Task.async(fn -> + TokenTransfersCounter.fetch(address_hash) + end) + + total_token_holders_task = + Task.async(fn -> + TokenHoldersCounter.fetch(address_hash) + end) + + [total_token_transfers_task, total_token_holders_task] + |> Task.yield_many(timeout) + |> Enum.map(fn {_task, res} -> + case res do + {:ok, result} -> + result + + {:exit, reason} -> + Logger.warn("Query fetching token counters terminated: #{inspect(reason)}") + 0 + + nil -> + Logger.warn("Query fetching token counters timed out.") + 0 + end + end) + |> List.to_tuple() + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/tokens_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/tokens_controller.ex new file mode 100644 index 0000000..4e4f289 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/tokens_controller.ex @@ -0,0 +1,74 @@ +defmodule BlockScoutWeb.TokensController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + + alias BlockScoutWeb.{Controller, TokensView} + alias Explorer.Chain + alias Phoenix.View + + def index(conn, %{"type" => "JSON"} = params) do + filter = + if Map.has_key?(params, "filter") do + Map.get(params, "filter") + else + nil + end + + paging_params = + params + |> paging_options() + + tokens = Chain.list_top_tokens(filter, paging_params) + + {tokens_page, next_page} = split_list_by_page(tokens) + + next_page_path = + case next_page_params(next_page, tokens_page, params) do + nil -> + nil + + next_page_params -> + tokens_path( + conn, + :index, + Map.delete(next_page_params, "type") + ) + end + + items_count_str = Map.get(params, "items_count") + + items_count = + if items_count_str do + {items_count, _} = Integer.parse(items_count_str) + items_count + else + 0 + end + + items = + tokens_page + |> Enum.with_index(1) + |> Enum.map(fn {token, index} -> + View.render_to_string( + TokensView, + "_tile.html", + token: token, + index: items_count + index, + conn: conn + ) + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_path + } + ) + end + + def index(conn, _params) do + render(conn, "index.html", current_path: Controller.current_full_path(conn)) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex new file mode 100644 index 0000000..ad7db93 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/tokens/transfer_controller.ex @@ -0,0 +1,91 @@ +defmodule BlockScoutWeb.Tokens.TransferController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, Controller} + alias BlockScoutWeb.Tokens.TransferView + alias Explorer.{Chain, Market} + alias Explorer.Chain.Address + alias Indexer.Fetcher.TokenTotalSupplyOnDemand + alias Phoenix.View + + import BlockScoutWeb.Chain, only: [split_list_by_page: 1, paging_options: 1, next_page_params: 3] + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + def index(conn, %{"token_id" => address_hash_string, "type" => "JSON"} = params) do + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, token} <- Chain.token_from_address_hash(address_hash), + token_transfers <- Chain.fetch_token_transfers_from_token_hash(address_hash, paging_options(params)), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + {token_transfers_paginated, next_page} = split_list_by_page(token_transfers) + + next_page_path = + case next_page_params(next_page, token_transfers_paginated, params) do + nil -> + nil + + next_page_params -> + token_transfer_path( + conn, + :index, + Address.checksum(token.contract_address_hash), + Map.delete(next_page_params, "type") + ) + end + + transfers_json = + Enum.map(token_transfers_paginated, fn transfer -> + View.render_to_string( + TransferView, + "_token_transfer.html", + conn: conn, + token: token, + token_transfer: transfer, + burn_address_hash: @burn_address_hash + ) + end) + + json(conn, %{items: transfers_json, next_page_path: next_page_path}) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + unprocessable_entity(conn) + + {:error, :not_found} -> + not_found(conn) + end + end + + def index(conn, %{"token_id" => address_hash_string} = params) do + options = [necessity_by_association: %{[contract_address: :smart_contract] => :optional}] + + with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), + {:ok, token} <- Chain.token_from_address_hash(address_hash, options), + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do + render( + conn, + "index.html", + counters_path: token_path(conn, :token_counters, %{"id" => Address.checksum(address_hash)}), + current_path: Controller.current_full_path(conn), + token: Market.add_price(token), + token_total_supply_status: TokenTotalSupplyOnDemand.trigger_fetch(address_hash), + tags: get_address_tags(address_hash, current_user(conn)) + ) + else + {:restricted_access, _} -> + not_found(conn) + + :error -> + not_found(conn) + + {:error, :not_found} -> + not_found(conn) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex new file mode 100644 index 0000000..858b7dc --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex @@ -0,0 +1,252 @@ +defmodule BlockScoutWeb.TransactionController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + + import BlockScoutWeb.Chain, + only: [ + fetch_page_number: 1, + paging_options: 1, + next_page_params: 3, + update_page_parameters: 3, + split_list_by_page: 1 + ] + + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + import BlockScoutWeb.Models.GetTransactionTags, only: [get_transaction_with_addresses_tags: 2] + + alias BlockScoutWeb.{ + AccessHelpers, + Controller, + TransactionInternalTransactionController, + TransactionTokenTransferController, + TransactionView + } + + alias Explorer.{Chain, Market} + alias Explorer.Chain.Cache.Transaction, as: TransactionCache + alias Explorer.ExchangeRates.Token + alias Phoenix.View + + @necessity_by_association %{ + :block => :optional, + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [to_address: :smart_contract] => :optional, + :token_transfers => :optional + } + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + @default_options [ + necessity_by_association: %{ + :block => :required, + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + } + ] + + def index(conn, %{"type" => "JSON"} = params) do + options = + @default_options + |> Keyword.merge(paging_options(params)) + + full_options = + options + |> Keyword.put( + :paging_options, + params + |> fetch_page_number() + |> update_page_parameters(Chain.default_page_size(), Keyword.get(options, :paging_options)) + ) + + %{total_transactions_count: transactions_count, transactions: transactions_plus_one} = + Chain.recent_collated_transactions_for_rap(full_options) + + {transactions, next_page} = + if fetch_page_number(params) == 1 do + split_list_by_page(transactions_plus_one) + else + {transactions_plus_one, nil} + end + + next_page_params = + if fetch_page_number(params) == 1 do + page_size = Chain.default_page_size() + + pages_limit = transactions_count |> Kernel./(page_size) |> Float.ceil() |> trunc() + + case next_page_params(next_page, transactions, params) do + nil -> + nil + + next_page_params -> + next_page_params + |> Map.delete("type") + |> Map.delete("items_count") + |> Map.put("pages_limit", pages_limit) + |> Map.put("page_size", page_size) + |> Map.put("page_number", 1) + end + else + Map.delete(params, "type") + end + + json( + conn, + %{ + items: + Enum.map(transactions, fn transaction -> + View.render_to_string( + TransactionView, + "_tile.html", + transaction: transaction, + burn_address_hash: @burn_address_hash, + conn: conn + ) + end), + next_page_params: next_page_params + } + ) + end + + def index(conn, _params) do + transaction_estimated_count = TransactionCache.estimated_count() + + render( + conn, + "index.html", + current_path: Controller.current_full_path(conn), + transaction_estimated_count: transaction_estimated_count + ) + end + + def show(conn, %{"id" => transaction_hash_string, "type" => "JSON"}) do + case Chain.string_to_transaction_hash(transaction_hash_string) do + {:ok, transaction_hash} -> + if Chain.transaction_has_token_transfers?(transaction_hash) do + TransactionTokenTransferController.index(conn, %{ + "transaction_id" => transaction_hash_string, + "type" => "JSON" + }) + else + TransactionInternalTransactionController.index(conn, %{ + "transaction_id" => transaction_hash_string, + "type" => "JSON" + }) + end + + :error -> + set_not_found_view(conn, transaction_hash_string) + end + end + + def show(conn, %{"id" => id} = params) do + with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(id), + :ok <- Chain.check_transaction_exists(transaction_hash) do + if Chain.transaction_has_token_transfers?(transaction_hash) do + with {:ok, transaction} <- + Chain.hash_to_transaction( + transaction_hash, + necessity_by_association: @necessity_by_association + ), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do + render( + conn, + "show_token_transfers.html", + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + block_height: Chain.block_height(), + current_path: Controller.current_full_path(conn), + current_user: current_user(conn), + show_token_transfers: true, + transaction: transaction, + from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), + to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), + tx_tags: + get_transaction_with_addresses_tags( + transaction, + current_user(conn) + ) + ) + else + :not_found -> + set_not_found_view(conn, id) + + :error -> + set_invalid_view(conn, id) + + {:error, :not_found} -> + set_not_found_view(conn, id) + + {:restricted_access, _} -> + set_not_found_view(conn, id) + end + else + with {:ok, transaction} <- + Chain.hash_to_transaction( + transaction_hash, + necessity_by_association: @necessity_by_association + ), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do + render( + conn, + "show_internal_transactions.html", + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + current_path: Controller.current_full_path(conn), + current_user: current_user(conn), + block_height: Chain.block_height(), + show_token_transfers: Chain.transaction_has_token_transfers?(transaction_hash), + transaction: transaction, + from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), + to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), + tx_tags: + get_transaction_with_addresses_tags( + transaction, + current_user(conn) + ) + ) + else + :not_found -> + set_not_found_view(conn, id) + + :error -> + set_invalid_view(conn, id) + + {:error, :not_found} -> + set_not_found_view(conn, id) + + {:restricted_access, _} -> + set_not_found_view(conn, id) + end + end + else + :error -> + set_invalid_view(conn, id) + + :not_found -> + set_not_found_view(conn, id) + end + end + + def set_not_found_view(conn, transaction_hash_string) do + conn + |> put_status(404) + |> put_view(TransactionView) + |> render("not_found.html", transaction_hash: transaction_hash_string) + end + + def set_invalid_view(conn, transaction_hash_string) do + conn + |> put_status(422) + |> put_view(TransactionView) + |> render("invalid.html", transaction_hash: transaction_hash_string) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex new file mode 100644 index 0000000..5cc1af7 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex @@ -0,0 +1,130 @@ +defmodule BlockScoutWeb.TransactionInternalTransactionController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + import BlockScoutWeb.Models.GetTransactionTags, only: [get_transaction_with_addresses_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, Controller, InternalTransactionView, TransactionController} + alias Explorer.{Chain, Market} + alias Explorer.ExchangeRates.Token + alias Phoenix.View + + def index(conn, %{"transaction_id" => transaction_hash_string, "type" => "JSON"} = params) do + with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(transaction_hash_string), + :ok <- Chain.check_transaction_exists(transaction_hash), + {:ok, transaction} <- Chain.hash_to_transaction(transaction_hash, []), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do + full_options = + Keyword.merge( + [ + necessity_by_association: %{ + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [transaction: :block] => :optional, + [created_contract_address: :smart_contract] => :optional, + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional + } + ], + paging_options(params) + ) + + internal_transactions_plus_one = Chain.transaction_to_internal_transactions(transaction_hash, full_options) + + {internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one) + + next_page_path = + case next_page_params(next_page, internal_transactions, params) do + nil -> + nil + + next_page_params -> + transaction_internal_transaction_path( + conn, + :index, + transaction_hash, + Map.delete(next_page_params, "type") + ) + end + + items = + internal_transactions + |> Enum.map(fn internal_transaction -> + View.render_to_string( + InternalTransactionView, + "_tile.html", + internal_transaction: internal_transaction + ) + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_path + } + ) + else + {:restricted_access, _} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + :error -> + TransactionController.set_invalid_view(conn, transaction_hash_string) + + {:error, :not_found} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + :not_found -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + end + end + + def index(conn, %{"transaction_id" => transaction_hash_string} = params) do + with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(transaction_hash_string), + {:ok, transaction} <- + Chain.hash_to_transaction( + transaction_hash, + necessity_by_association: %{ + :block => :optional, + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [to_address: :smart_contract] => :optional, + :token_transfers => :optional + } + ), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do + render( + conn, + "index.html", + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + current_path: Controller.current_full_path(conn), + current_user: current_user(conn), + block_height: Chain.block_height(), + show_token_transfers: Chain.transaction_has_token_transfers?(transaction_hash), + transaction: transaction, + from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), + to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), + tx_tags: + get_transaction_with_addresses_tags( + transaction, + current_user(conn) + ) + ) + else + {:restricted_access, _} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + :error -> + TransactionController.set_invalid_view(conn, transaction_hash_string) + + {:error, :not_found} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex new file mode 100644 index 0000000..bb29f78 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex @@ -0,0 +1,123 @@ +defmodule BlockScoutWeb.TransactionLogController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + import BlockScoutWeb.Models.GetTransactionTags, only: [get_transaction_with_addresses_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, Controller, TransactionController, TransactionLogView} + alias Explorer.{Chain, Market} + alias Explorer.ExchangeRates.Token + alias Phoenix.View + + def index(conn, %{"transaction_id" => transaction_hash_string, "type" => "JSON"} = params) do + with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(transaction_hash_string), + {:ok, transaction} <- + Chain.hash_to_transaction(transaction_hash, + necessity_by_association: %{[to_address: :smart_contract] => :optional} + ), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do + full_options = + Keyword.merge( + [ + necessity_by_association: %{ + [address: :names] => :optional, + [address: :smart_contract] => :optional, + address: :optional + } + ], + paging_options(params) + ) + + from_api = false + logs_plus_one = Chain.transaction_to_logs(transaction_hash, from_api, full_options) + + {logs, next_page} = split_list_by_page(logs_plus_one) + + next_page_url = + case next_page_params(next_page, logs, params) do + nil -> + nil + + next_page_params -> + transaction_log_path(conn, :index, transaction, Map.delete(next_page_params, "type")) + end + + items = + logs + |> Enum.map(fn log -> + View.render_to_string( + TransactionLogView, + "_logs.html", + log: log, + conn: conn, + transaction: transaction + ) + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_url + } + ) + else + {:restricted_access, _} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + :error -> + TransactionController.set_invalid_view(conn, transaction_hash_string) + + {:error, :not_found} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + end + end + + def index(conn, %{"transaction_id" => transaction_hash_string} = params) do + with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(transaction_hash_string), + {:ok, transaction} <- + Chain.hash_to_transaction( + transaction_hash, + necessity_by_association: %{ + :block => :optional, + [created_contract_address: :names] => :optional, + [from_address: :names] => :required, + [to_address: :names] => :optional, + [to_address: :smart_contract] => :optional, + :token_transfers => :optional + } + ), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do + render( + conn, + "index.html", + block_height: Chain.block_height(), + show_token_transfers: Chain.transaction_has_token_transfers?(transaction_hash), + current_path: Controller.current_full_path(conn), + current_user: current_user(conn), + transaction: transaction, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), + to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), + tx_tags: + get_transaction_with_addresses_tags( + transaction, + current_user(conn) + ) + ) + else + {:restricted_access, _} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + :error -> + TransactionController.set_invalid_view(conn, transaction_hash_string) + + {:error, :not_found} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex new file mode 100644 index 0000000..49447c6 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_raw_trace_controller.ex @@ -0,0 +1,111 @@ +defmodule BlockScoutWeb.TransactionRawTraceController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + import BlockScoutWeb.Models.GetTransactionTags, only: [get_transaction_with_addresses_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, TransactionController} + alias EthereumJSONRPC + alias Explorer.{Chain, Market} + alias Explorer.Chain.Import + alias Explorer.Chain.Import.Runner.InternalTransactions + alias Explorer.ExchangeRates.Token + + def index(conn, %{"transaction_id" => hash_string} = params) do + with {:ok, hash} <- Chain.string_to_transaction_hash(hash_string), + {:ok, transaction} <- + Chain.hash_to_transaction( + hash, + necessity_by_association: %{ + :block => :optional, + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [to_address: :smart_contract] => :optional, + :token_transfers => :optional + } + ), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do + if is_nil(transaction.block_number) do + render_raw_trace(conn, [], transaction, hash) + else + internal_transactions = Chain.all_transaction_to_internal_transactions(hash) + + first_trace_exists = + Enum.find_index(internal_transactions, fn trace -> + trace.index == 0 + end) + + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + internal_transactions = + if first_trace_exists do + internal_transactions + else + response = + Chain.fetch_first_trace( + [ + %{ + block_hash: transaction.block_hash, + block_number: transaction.block_number, + hash_data: hash_string, + transaction_index: transaction.index + } + ], + json_rpc_named_arguments + ) + + case response do + {:ok, first_trace_params} -> + InternalTransactions.run_insert_only(first_trace_params, %{ + timeout: :infinity, + timestamps: Import.timestamps(), + internal_transactions: %{params: first_trace_params} + }) + + Chain.all_transaction_to_internal_transactions(hash) + + {:error, _} -> + internal_transactions + + :ignore -> + internal_transactions + end + end + + render_raw_trace(conn, internal_transactions, transaction, hash) + end + else + {:restricted_access, _} -> + TransactionController.set_not_found_view(conn, hash_string) + + :error -> + TransactionController.set_invalid_view(conn, hash_string) + + {:error, :not_found} -> + TransactionController.set_not_found_view(conn, hash_string) + end + end + + defp render_raw_trace(conn, internal_transactions, transaction, hash) do + render( + conn, + "index.html", + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + internal_transactions: internal_transactions, + block_height: Chain.block_height(), + current_user: current_user(conn), + show_token_transfers: Chain.transaction_has_token_transfers?(hash), + transaction: transaction, + from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), + to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), + tx_tags: + get_transaction_with_addresses_tags( + transaction, + current_user(conn) + ) + ) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex new file mode 100644 index 0000000..14b07de --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_state_controller.ex @@ -0,0 +1,464 @@ +defmodule BlockScoutWeb.TransactionStateController do + use BlockScoutWeb, :controller + + alias BlockScoutWeb.{ + AccessHelpers, + Controller, + TransactionController, + TransactionStateView + } + + alias Explorer.{Chain, Chain.Wei, Market, PagingOptions} + alias Explorer.ExchangeRates.Token + alias Phoenix.View + alias Indexer.Fetcher.{CoinBalance, TokenBalance} + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + import BlockScoutWeb.Models.GetTransactionTags, only: [get_transaction_with_addresses_tags: 2] + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + + @burn_address_hash burn_address_hash + + def index(conn, %{"transaction_id" => transaction_hash_string, "type" => "JSON"} = params) do + with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(transaction_hash_string), + :ok <- Chain.check_transaction_exists(transaction_hash), + {:ok, transaction} <- + Chain.hash_to_transaction( + transaction_hash, + necessity_by_association: %{ + [block: :miner] => :required, + from_address: :required, + to_address: :optional + } + ), + {:ok, false} <- + AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- + AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do + full_options = [ + necessity_by_association: %{ + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + from_address: :required, + to_address: :required, + token: :required + }, + # we need to consider all token transfers in block to show whole state change of transaction + paging_options: %PagingOptions{key: nil, page_size: nil} + ] + + token_transfers = Chain.transaction_to_token_transfers(transaction_hash, full_options) + + block = transaction.block + + block_txs = + Chain.block_to_transactions(block.hash, + necessity_by_association: %{}, + paging_options: %PagingOptions{key: nil, page_size: nil} + ) + + {from_before, to_before, miner_before} = coin_balances_before(transaction, block_txs) + + from_hash = transaction.from_address_hash + to_hash = transaction.to_address_hash + miner_hash = block.miner_hash + + from = transaction.from_address + from_after = do_update_coin_balance_from_tx(from_hash, transaction, from_before, block) + + from_coin_entry = + if from_hash not in [to_hash, miner_hash] do + View.render_to_string( + TransactionStateView, + "_state_change.html", + coin_or_token_transfers: :coin, + address: from, + burn_address_hash: @burn_address_hash, + balance_before: from_before, + balance_after: from_after, + balance_diff: Wei.sub(from_after, from_before), + conn: conn + ) + end + + to = transaction.to_address + to_after = do_update_coin_balance_from_tx(to_hash, transaction, to_before, block) + + to_coin_entry = + if to_hash != miner_hash do + View.render_to_string( + TransactionStateView, + "_state_change.html", + coin_or_token_transfers: :coin, + address: to, + burn_address_hash: @burn_address_hash, + balance_before: to_before, + balance_after: to_after, + balance_diff: Wei.sub(to_after, to_before), + conn: conn + ) + end + + miner = block.miner + miner_after = do_update_coin_balance_from_tx(miner_hash, transaction, miner_before, block) + + miner_entry = + View.render_to_string( + TransactionStateView, + "_state_change.html", + coin_or_token_transfers: :coin, + address: miner, + burn_address_hash: @burn_address_hash, + balance_before: miner_before, + balance_after: miner_after, + balance_diff: Wei.sub(miner_after, miner_before), + miner: true, + conn: conn + ) + + token_balances_before = token_balances_before(token_transfers, transaction, block_txs) + + token_balances_after = + do_update_token_balances_from_token_transfers( + token_transfers, + token_balances_before, + :include_transfers + ) + + items = + for {address, balances} <- token_balances_after, + {token_hash, {balance, transfers}} <- balances do + balance_before = token_balances_before[address][token_hash] + + View.render_to_string( + TransactionStateView, + "_state_change.html", + coin_or_token_transfers: transfers, + address: address, + burn_address_hash: @burn_address_hash, + balance_before: balance_before, + balance_after: balance, + balance_diff: Decimal.sub(balance, balance_before), + conn: conn + ) + end + + json(conn, %{items: Enum.sort([from_coin_entry, to_coin_entry, miner_entry | items])}) + else + {:restricted_access, _} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + :error -> + TransactionController.set_invalid_view(conn, transaction_hash_string) + + {:error, :not_found} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + :not_found -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + end + end + + def index(conn, %{"transaction_id" => transaction_hash_string} = params) do + with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(transaction_hash_string), + {:ok, transaction} <- + Chain.hash_to_transaction( + transaction_hash, + necessity_by_association: %{ + :block => :optional, + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [to_address: :smart_contract] => :optional, + :token_transfers => :optional + } + ), + {:ok, false} <- + AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- + AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do + render( + conn, + "index.html", + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + block_height: Chain.block_height(), + current_path: Controller.current_full_path(conn), + show_token_transfers: Chain.transaction_has_token_transfers?(transaction_hash), + transaction: transaction, + from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), + to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), + tx_tags: + get_transaction_with_addresses_tags( + transaction, + current_user(conn) + ), + current_user: current_user(conn) + ) + else + :not_found -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + :error -> + TransactionController.set_invalid_view(conn, transaction_hash_string) + + {:error, :not_found} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + {:restricted_access, _} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + end + end + + def coin_balance(address_hash, _block_number) when is_nil(address_hash) do + %Wei{value: Decimal.new(0)} + end + + def coin_balance(address_hash, block_number) do + case Chain.get_coin_balance(address_hash, block_number) do + %{value: val} when not is_nil(val) -> + val + + _ -> + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + CoinBalance.run([{address_hash.bytes, block_number}], json_rpc_named_arguments) + # after CoinBalance.run balance is fetched and imported, so we can call coin_balance again + coin_balance(address_hash, block_number) + end + end + + def coin_balances_before(tx, block_txs) do + block = tx.block + + from_before = coin_balance(tx.from_address_hash, block.number - 1) + to_before = coin_balance(tx.to_address_hash, block.number - 1) + miner_before = coin_balance(block.miner_hash, block.number - 1) + + block_txs + |> Enum.reduce_while( + {from_before, to_before, miner_before}, + fn block_tx, {block_from, block_to, block_miner} = state -> + if block_tx.index < tx.index do + {:cont, + {do_update_coin_balance_from_tx(tx.from_address_hash, block_tx, block_from, block), + do_update_coin_balance_from_tx(tx.to_address_hash, block_tx, block_to, block), + do_update_coin_balance_from_tx(tx.block.miner_hash, block_tx, block_miner, block)}} + else + # txs ordered by index ascending, so we can halt after facing index greater or equal than index of our tx + {:halt, state} + end + end + ) + end + + defp do_update_coin_balance_from_tx(address_hash, tx, balance, block) do + from = tx.from_address_hash + to = tx.to_address_hash + miner = block.miner_hash + + balance + |> (&if(address_hash == from, do: Wei.sub(&1, from_loss(tx)), else: &1)).() + |> (&if(address_hash == to, do: Wei.sum(&1, to_profit(tx)), else: &1)).() + |> (&if(address_hash == miner, do: Wei.sum(&1, miner_profit(tx, block)), else: &1)).() + end + + def token_balance(@burn_address_hash, _token_transfer, _block_number) do + Decimal.new(0) + end + + def token_balance(address_hash, token_transfer, block_number) do + token = token_transfer.token + token_contract_address_hash = token.contract_address_hash + + case Chain.get_token_balance(address_hash, token_contract_address_hash, block_number) do + %{value: val} when not is_nil(val) -> + val + + # we haven't fetched this balance yet + _ -> + json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) + + token_id_int = + case token_transfer.token_id do + %Decimal{} -> Decimal.to_integer(token_transfer.token_id) + id_int when is_integer(id_int) -> id_int + _ -> token_transfer.token_id + end + + TokenBalance.run( + [ + {address_hash.bytes, token_contract_address_hash.bytes, block_number, token.type, token_id_int, 0} + ], + json_rpc_named_arguments + ) + + # after TokenBalance.run balance is fetched and imported, so we can call token_balance again + token_balance(address_hash, token_transfer, block_number) + end + end + + def token_balances_before(token_transfers, tx, block_txs) do + balances_before = + token_transfers + |> Enum.reduce(%{}, fn transfer, balances_map -> + from = transfer.from_address + to = transfer.to_address + token_hash = transfer.token_contract_address_hash + prev_block = transfer.block_number - 1 + + balances_with_from = + case balances_map do + # from address already in the map + %{^from => %{^token_hash => _}} -> + balances_map + + # we need to add from address into the map + _ -> + put_in( + balances_map, + Enum.map([from, token_hash], &Access.key(&1, %{})), + token_balance(from.hash, transfer, prev_block) + ) + end + + case balances_with_from do + # to address already in the map + %{^to => %{^token_hash => _}} -> + balances_with_from + + # we need to add to address into the map + _ -> + put_in( + balances_with_from, + Enum.map([to, token_hash], &Access.key(&1, %{})), + token_balance(to.hash, transfer, prev_block) + ) + end + end) + + block_txs + |> Enum.reduce_while( + balances_before, + fn block_tx, state -> + if block_tx.index < tx.index do + {:cont, do_update_token_balances_from_token_transfers(block_tx.token_transfers, state)} + else + # txs ordered by index ascending, so we can halt after facing index greater or equal than index of our tx + {:halt, state} + end + end + ) + end + + defp do_update_token_balances_from_token_transfers( + token_transfers, + balances_map, + include_transfers \\ :no + ) do + Enum.reduce( + token_transfers, + balances_map, + &token_transfers_balances_reducer(&1, &2, include_transfers) + ) + end + + defp token_transfers_balances_reducer(transfer, state_balances_map, include_transfers) do + from = transfer.from_address + to = transfer.to_address + token = transfer.token_contract_address_hash + + balances_map_from_included = + case state_balances_map do + # from address is needed to be updated in our map + %{^from => %{^token => val}} -> + put_in( + state_balances_map, + Enum.map([from, token], &Access.key(&1, %{})), + do_update_balance(val, :from, transfer, include_transfers) + ) + + # we are not interested in this address + _ -> + state_balances_map + end + + case balances_map_from_included do + # to address is needed to be updated in our map + %{^to => %{^token => val}} -> + put_in( + balances_map_from_included, + Enum.map([to, token], &Access.key(&1, %{})), + do_update_balance(val, :to, transfer, include_transfers) + ) + + # we are not interested in this address + _ -> + balances_map_from_included + end + end + + # point of this function is to include all transfers for frontend if option :include_transfer is passed + defp do_update_balance(old_val, type, transfer, include_transfers) do + transfer_amount = if is_nil(transfer.amount), do: 1, else: transfer.amount + + case {include_transfers, old_val, type} do + {:include_transfers, {val, transfers}, :from} -> + {Decimal.sub(val, transfer_amount), [{type, transfer} | transfers]} + + {:include_transfers, {val, transfers}, :to} -> + {Decimal.add(val, transfer_amount), [{type, transfer} | transfers]} + + {:include_transfers, val, :from} -> + {Decimal.sub(val, transfer_amount), [{type, transfer}]} + + {:include_transfers, val, :to} -> + {Decimal.add(val, transfer_amount), [{type, transfer}]} + + {_, val, :from} -> + Decimal.sub(val, transfer_amount) + + {_, val, :to} -> + Decimal.add(val, transfer_amount) + end + end + + def from_loss(tx) do + {_, fee} = Chain.fee(tx, :wei) + + if error?(tx) do + %Wei{value: fee} + else + Wei.sum(tx.value, %Wei{value: fee}) + end + end + + def to_profit(tx) do + if error?(tx) do + %Wei{value: 0} + else + tx.value + end + end + + def miner_profit(tx, block) do + base_fee_per_gas = block.base_fee_per_gas || %Wei{value: Decimal.new(0)} + max_priority_fee_per_gas = tx.max_priority_fee_per_gas || tx.gas_price + max_fee_per_gas = tx.max_fee_per_gas || tx.gas_price + + priority_fee_per_gas = + Enum.min_by([max_priority_fee_per_gas, Wei.sub(max_fee_per_gas, base_fee_per_gas)], fn x -> + Wei.to(x, :wei) + end) + + Wei.mult(priority_fee_per_gas, tx.gas_used) + end + + defp error?(tx) do + case Chain.transaction_to_status(tx) do + {:error, _} -> true + _ -> false + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex new file mode 100644 index 0000000..931df3c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex @@ -0,0 +1,137 @@ +defmodule BlockScoutWeb.TransactionTokenTransferController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Account.AuthController, only: [current_user: 1] + import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2] + import BlockScoutWeb.Models.GetTransactionTags, only: [get_transaction_with_addresses_tags: 2] + + alias BlockScoutWeb.{AccessHelpers, Controller, TransactionController, TransactionTokenTransferView} + alias Explorer.{Chain, Market} + alias Explorer.ExchangeRates.Token + alias Phoenix.View + + {:ok, burn_address_hash} = Chain.string_to_address_hash("0x0000000000000000000000000000000000000000") + @burn_address_hash burn_address_hash + + def index(conn, %{"transaction_id" => transaction_hash_string, "type" => "JSON"} = params) do + with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(transaction_hash_string), + :ok <- Chain.check_transaction_exists(transaction_hash), + {:ok, transaction} <- + Chain.hash_to_transaction( + transaction_hash, + [] + ), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do + full_options = + Keyword.merge( + [ + necessity_by_association: %{ + [from_address: :smart_contract] => :optional, + [to_address: :smart_contract] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + from_address: :required, + to_address: :required, + token: :required + } + ], + paging_options(params) + ) + + token_transfers_plus_one = Chain.transaction_to_token_transfers(transaction_hash, full_options) + + {token_transfers, next_page} = split_list_by_page(token_transfers_plus_one) + + next_page_url = + case next_page_params(next_page, token_transfers, params) do + nil -> + nil + + next_page_params -> + transaction_token_transfer_path(conn, :index, transaction_hash, Map.delete(next_page_params, "type")) + end + + items = + token_transfers + |> Enum.map(fn transfer -> + View.render_to_string( + TransactionTokenTransferView, + "_token_transfer.html", + token_transfer: transfer, + burn_address_hash: @burn_address_hash, + conn: conn + ) + end) + + json( + conn, + %{ + items: items, + next_page_path: next_page_url + } + ) + else + {:restricted_access, _} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + :error -> + TransactionController.set_invalid_view(conn, transaction_hash_string) + + {:error, :not_found} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + :not_found -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + end + end + + def index(conn, %{"transaction_id" => transaction_hash_string} = params) do + with {:ok, transaction_hash} <- Chain.string_to_transaction_hash(transaction_hash_string), + {:ok, transaction} <- + Chain.hash_to_transaction( + transaction_hash, + necessity_by_association: %{ + :block => :optional, + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional, + [to_address: :smart_contract] => :optional, + :token_transfers => :optional + } + ), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do + render( + conn, + "index.html", + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), + block_height: Chain.block_height(), + current_path: Controller.current_full_path(conn), + current_user: current_user(conn), + show_token_transfers: true, + transaction: transaction, + from_tags: get_address_tags(transaction.from_address_hash, current_user(conn)), + to_tags: get_address_tags(transaction.to_address_hash, current_user(conn)), + tx_tags: + get_transaction_with_addresses_tags( + transaction, + current_user(conn) + ) + ) + else + :not_found -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + :error -> + TransactionController.set_invalid_view(conn, transaction_hash_string) + + {:error, :not_found} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + + {:restricted_access, _} -> + TransactionController.set_not_found_view(conn, transaction_hash_string) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/verified_contracts_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/verified_contracts_controller.ex new file mode 100644 index 0000000..c75b25e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/verified_contracts_controller.ex @@ -0,0 +1,75 @@ +defmodule BlockScoutWeb.VerifiedContractsController do + use BlockScoutWeb, :controller + + import BlockScoutWeb.Chain, + only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1, fetch_page_number: 1] + + alias BlockScoutWeb.{Controller, VerifiedContractsView} + alias Explorer.{Chain, Market} + alias Explorer.ExchangeRates.Token + alias Phoenix.View + + @necessity_by_association %{[address: :token] => :optional} + + def index(conn, %{"type" => "JSON"} = params) do + full_options = + [necessity_by_association: @necessity_by_association] + |> Keyword.merge(paging_options(params)) + |> Keyword.merge(current_filter(params)) + |> Keyword.merge(search_query(params)) + + verified_contracts_plus_one = Chain.verified_contracts(full_options) + {verified_contracts, next_page} = split_list_by_page(verified_contracts_plus_one) + + items = + for contract <- verified_contracts do + token = + if contract.address.token, + do: Market.get_exchange_rate(contract.address.token.symbol), + else: Token.null() + + View.render_to_string(VerifiedContractsView, "_contract.html", + contract: contract, + token: token + ) + end + + next_page_path = + case next_page_params(next_page, verified_contracts, params) do + nil -> nil + next_page_params -> verified_contracts_path(conn, :index, Map.delete(next_page_params, "type")) + end + + json(conn, %{items: items, next_page_path: next_page_path}) + end + + def index(conn, params) do + render(conn, "index.html", + current_path: Controller.current_full_path(conn), + filter: params["filter"], + page_number: params |> fetch_page_number() |> Integer.to_string(), + contracts_count: Chain.count_contracts_from_cache(), + verified_contracts_count: Chain.count_verified_contracts_from_cache(), + new_contracts_count: Chain.count_new_contracts_from_cache(), + new_verified_contracts_count: Chain.count_new_verified_contracts_from_cache() + ) + end + + defp current_filter(%{"filter" => "solidity"}) do + [filter: :solidity] + end + + defp current_filter(%{"filter" => "vyper"}) do + [filter: :vyper] + end + + defp current_filter(_), do: [] + + defp search_query(%{"search" => ""}), do: [] + + defp search_query(%{"search" => search_string}) do + [search: search_string] + end + + defp search_query(_), do: [] +end diff --git a/apps/block_scout_web/lib/block_scout_web/counters/blocks_indexed_counter.ex b/apps/block_scout_web/lib/block_scout_web/counters/blocks_indexed_counter.ex new file mode 100644 index 0000000..8c4ed18 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/counters/blocks_indexed_counter.ex @@ -0,0 +1,58 @@ +defmodule BlockScoutWeb.Counters.BlocksIndexedCounter do + @moduledoc """ + Module responsible for fetching and consolidating the number blocks indexed. + + It loads the count asynchronously in a time interval. + """ + + use GenServer + + alias BlockScoutWeb.Notifier + alias Explorer.Chain + + # It is undesirable to automatically start the counter in all environments. + # Consider the test environment: if it initiates but does not finish before a + # test ends, that test will fail. + config = Application.compile_env(:block_scout_web, __MODULE__) + @enabled Keyword.get(config, :enabled) + + @doc """ + Starts a process to periodically update the % of blocks indexed. + """ + @spec start_link(term()) :: GenServer.on_start() + def start_link(_) do + GenServer.start_link(__MODULE__, :ok, name: __MODULE__) + end + + @impl true + def init(args) do + if @enabled do + Task.start_link(&calculate_blocks_indexed/0) + + schedule_next_consolidation() + end + + {:ok, args} + end + + def calculate_blocks_indexed do + indexed_ratio_blocks = Chain.indexed_ratio_blocks() + + finished? = Chain.finished_indexing?(indexed_ratio_blocks) + + Notifier.broadcast_blocks_indexed_ratio(indexed_ratio_blocks, finished?) + end + + defp schedule_next_consolidation do + Process.send_after(self(), :calculate_blocks_indexed, :timer.minutes(5)) + end + + @impl true + def handle_info(:calculate_blocks_indexed, state) do + calculate_blocks_indexed() + + schedule_next_consolidation() + + {:noreply, state} + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/counters/internal_transactions_indexed_counter.ex b/apps/block_scout_web/lib/block_scout_web/counters/internal_transactions_indexed_counter.ex new file mode 100644 index 0000000..76085fe --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/counters/internal_transactions_indexed_counter.ex @@ -0,0 +1,58 @@ +defmodule BlockScoutWeb.Counters.InternalTransactionsIndexedCounter do + @moduledoc """ + Module responsible for fetching and consolidating the number pending block operations (internal transactions) indexed. + + It loads the count asynchronously in a time interval. + """ + + use GenServer + + alias BlockScoutWeb.Notifier + alias Explorer.Chain + + # It is undesirable to automatically start the counter in all environments. + # Consider the test environment: if it initiates but does not finish before a + # test ends, that test will fail. + config = Application.compile_env(:block_scout_web, __MODULE__) + @enabled Keyword.get(config, :enabled) + + @doc """ + Starts a process to periodically update the % of internal transactions indexed. + """ + @spec start_link(term()) :: GenServer.on_start() + def start_link(_) do + GenServer.start_link(__MODULE__, :ok, name: __MODULE__) + end + + @impl true + def init(args) do + if @enabled do + Task.start_link(&calculate_internal_transactions_indexed/0) + + schedule_next_consolidation() + end + + {:ok, args} + end + + def calculate_internal_transactions_indexed do + indexed_ratio_internal_transactions = Chain.indexed_ratio_internal_transactions() + + finished? = Chain.finished_indexing?(indexed_ratio_internal_transactions) + + Notifier.broadcast_internal_transactions_indexed_ratio(indexed_ratio_internal_transactions, finished?) + end + + defp schedule_next_consolidation do + Process.send_after(self(), :calculate_internal_transactions_indexed, :timer.minutes(7)) + end + + @impl true + def handle_info(:calculate_internal_transactions_indexed, state) do + calculate_internal_transactions_indexed() + + schedule_next_consolidation() + + {:noreply, state} + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/csp_header.ex b/apps/block_scout_web/lib/block_scout_web/csp_header.ex new file mode 100644 index 0000000..cb81c9b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/csp_header.ex @@ -0,0 +1,30 @@ +defmodule BlockScoutWeb.CSPHeader do + @moduledoc """ + Plug to set content-security-policy with websocket endpoints + """ + + alias Phoenix.Controller + alias Plug.Conn + + def init(opts), do: opts + + def call(conn, _opts) do + Controller.put_secure_browser_headers(conn, %{ + "content-security-policy" => "\ + connect-src 'self' #{websocket_endpoints(conn)} wss://*.bridge.walletconnect.org/ https://request-global.czilladx.com/ https://raw.githubusercontent.com/trustwallet/assets/ https://registry.walletconnect.org/data/wallets.json https://*.poa.network;\ + default-src 'self';\ + script-src 'self' 'unsafe-inline' 'unsafe-eval' https://coinzillatag.com https://www.google.com https://www.gstatic.com;\ + style-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com;\ + img-src 'self' * data:;\ + media-src 'self' * data:;\ + font-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.gstatic.com data:;\ + frame-src 'self' 'unsafe-inline' 'unsafe-eval' https://request-global.czilladx.com/ https://www.google.com;\ + " + }) + end + + defp websocket_endpoints(conn) do + host = Conn.get_req_header(conn, "host") + "ws://#{host} wss://#{host}" + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/endpoint.ex b/apps/block_scout_web/lib/block_scout_web/endpoint.ex new file mode 100644 index 0000000..19c6163 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/endpoint.ex @@ -0,0 +1,90 @@ +defmodule BlockScoutWeb.Endpoint do + use Phoenix.Endpoint, otp_app: :block_scout_web + use Absinthe.Phoenix.Endpoint + + if Application.compile_env(:block_scout_web, :sql_sandbox) do + plug(Phoenix.Ecto.SQL.Sandbox, repo: Explorer.Repo) + end + + socket("/socket", BlockScoutWeb.UserSocket, websocket: [timeout: 45_000]) + socket("/socket/v2", BlockScoutWeb.UserSocketV2, websocket: [timeout: 45_000]) + + # Serve at "/" the static files from "priv/static" directory. + # + # You should set gzip to true if you are running phoenix.digest + # when deploying your static files in production. + plug( + Plug.Static, + at: "/", + from: :block_scout_web, + gzip: true, + only: ~w( + css + fonts + images + js + android-chrome-192x192.png + android-chrome-512x512.png + apple-touch-icon.png + browserconfig.xml + mstile-150x150.png + safari-pinned-tab.svg + robots.txt + ), + only_matching: ~w(manifest) + ) + + # Code reloading can be explicitly enabled under the + # :code_reloader configuration of your endpoint. + if code_reloading? do + socket("/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket) + plug(Phoenix.LiveReloader) + plug(Phoenix.CodeReloader) + end + + plug(Plug.RequestId) + plug(Plug.Logger) + + plug( + Plug.Parsers, + parsers: [:urlencoded, :multipart, :json], + length: 20_000_000, + query_string_length: 1_000_000, + pass: ["*/*"], + json_decoder: Poison + ) + + plug(Plug.MethodOverride) + plug(Plug.Head) + + # The session will be stored in the cookie and signed, + # this means its contents can be read but not tampered with. + # Set :encryption_salt if you would also like to encrypt it. + + plug( + Plug.Session, + store: BlockScoutWeb.Plug.RedisCookie, + key: "_explorer_key", + signing_salt: "iC2ksJHS", + same_site: "Lax", + http_only: false + ) + + use SpandexPhoenix + + plug(BlockScoutWeb.Prometheus.Exporter) + + # 'x-apollo-tracing' header for https://www.graphqlbin.com to work with our GraphQL endpoint + plug(CORSPlug, headers: ["x-apollo-tracing" | CORSPlug.defaults()[:headers]]) + + plug(BlockScoutWeb.Router) + + def init(_key, config) do + if config[:load_from_system_env] do + port = System.get_env("PORT") || raise "expected the PORT environment variable to be set" + {:ok, Keyword.put(config, :http, [:inet6, port: port])} + else + {:ok, config} + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/etherscan.ex b/apps/block_scout_web/lib/block_scout_web/etherscan.ex new file mode 100644 index 0000000..6afc220 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/etherscan.ex @@ -0,0 +1,3011 @@ +defmodule BlockScoutWeb.Etherscan do + @moduledoc """ + Documentation data for Etherscan-compatible API. + """ + + @account_balance_example_value %{ + "status" => "1", + "message" => "OK", + "result" => "663046792267785498951364" + } + + @account_balance_example_value_error %{ + "status" => "0", + "message" => "Invalid address hash", + "result" => nil + } + + @account_balancemulti_example_value %{ + "status" => "1", + "message" => "OK", + "result" => [ + %{ + "account" => "0xddbd2b932c763ba5b1b7ae3b362eac3e8d40121a", + "balance" => "40807168566070000000000", + "stale" => true + }, + %{ + "account" => "0x63a9975ba31b0b9626b34300f7f627147df1f526", + "balance" => "332567136222827062478", + "stale" => false + }, + %{ + "account" => "0x198ef1ec325a96cc354c7266a038be8b5c558f67", + "balance" => "185178830000000000", + "stale" => false + } + ] + } + + @account_pendingtxlist_example_value %{ + "status" => "1", + "message" => "OK", + "result" => [ + %{ + "hash" => "0x98beb27135aa0a25650557005ad962919d6a278c4b3dde7f4f6a3a1e65aa746c", + "nonce" => "0", + "from" => "0x3fb1cd2cd96c6d5c0b5eb3322d807b34482481d4", + "to" => "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae", + "value" => "0", + "gas" => "122261", + "gasPrice" => "50000000000", + "input" => + "0xf00d4b5d000000000000000000000000036c8cecce8d8bbf0831d840d7f29c9e3ddefa63000000000000000000000000c5a96db085dda36ffbe390f455315d30d6d3dc52", + "contractAddress" => "", + "cumulativeGasUsed" => "122207", + "gasUsed" => "122207" + } + ] + } + + @account_txlist_example_value %{ + "status" => "1", + "message" => "OK", + "result" => [ + %{ + "blockNumber" => "65204", + "timeStamp" => "1439232889", + "hash" => "0x98beb27135aa0a25650557005ad962919d6a278c4b3dde7f4f6a3a1e65aa746c", + "nonce" => "0", + "blockHash" => "0x373d339e45a701447367d7b9c7cef84aab79c2b2714271b908cda0ab3ad0849b", + "transactionIndex" => "0", + "from" => "0x3fb1cd2cd96c6d5c0b5eb3322d807b34482481d4", + "to" => "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae", + "value" => "0", + "gas" => "122261", + "gasPrice" => "50000000000", + "isError" => "0", + "txreceipt_status" => "1", + "input" => + "0xf00d4b5d000000000000000000000000036c8cecce8d8bbf0831d840d7f29c9e3ddefa63000000000000000000000000c5a96db085dda36ffbe390f455315d30d6d3dc52", + "contractAddress" => "", + "cumulativeGasUsed" => "122207", + "gasUsed" => "122207", + "confirmations" => "5994246" + } + ] + } + + @account_txlist_example_value_error %{ + "status" => "0", + "message" => "No transactions found", + "result" => [] + } + + @account_txlistinternal_example_value %{ + "status" => "1", + "message" => "OK", + "result" => [ + %{ + "blockNumber" => "6153702", + "timeStamp" => "1534362606", + "from" => "0x2ca1e3f250f56f1761b9a52bc42db53986085eff", + "to" => "", + "value" => "5488334153118633", + "contractAddress" => "0x883103875d905c11f9ac7dacbfc16deb39655361", + "transactionHash" => "0xd65b788c610949704a5f9aac2228c7c777434dfe11c863a12306f57fcbd8cdbb", + "index" => "0", + "input" => "", + "type" => "call", + "callType" => "delegatecall", + "gas" => "814937", + "gasUsed" => "536262", + "isError" => "0", + "errCode" => "" + } + ] + } + + @account_txlistinternal_example_value_error %{ + "status" => "0", + "message" => "No internal transactions found", + "result" => [] + } + + @account_eth_get_balance_example_value %{ + "jsonrpc" => "2.0", + "result" => "0x0234c8a3397aab58", + "id" => 1 + } + + @account_tokentx_example_value %{ + "status" => "1", + "message" => "OK", + "result" => [ + %{ + "blockNumber" => "5997843", + "timeStamp" => "1532086946", + "hash" => "0xd65b788c610949704a5f9aac2228c7c777434dfe11c863a12306f57fcbd8cdbb", + "nonce" => "765", + "blockHash" => "0x6169c5dc05d0051564ba3eae8ebfbdefda640c5f5ffc095846b8aed0b44f64ea", + "from" => "0x4e83362442b8d1bec281594cea3050c8eb01311c", + "contractAddress" => "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2", + "logIndex" => "0", + "to" => "0x21e21ba085289f81a86921de890eed30f1ad2375", + "value" => "10000000000000000000", + "tokenName" => "Maker", + "tokenSymbol" => "MKR", + "tokenDecimal" => "18", + "transactionIndex" => "27", + "gas" => "44758", + "gasPrice" => "7000000000", + "gasUsed" => "37298", + "cumulativeGasUsed" => "1043649", + "input" => + "0xa9059cbb00000000000000000000000021e21ba085289f81a86921de890eed30f1ad23750000000000000000000000000000000000000000000000008ac7230489e80000", + "confirmations" => "199384" + } + ] + } + + @account_tokentx_example_value_error %{ + "status" => "0", + "message" => "No token transfers found", + "result" => [] + } + + @account_tokenbalance_example_value %{ + "status" => "1", + "message" => "OK", + "result" => "135499" + } + + @account_tokenbalance_example_value_error %{ + "status" => "0", + "message" => "Invalid address format", + "result" => nil + } + + @account_tokenlist_example_value %{ + "status" => "1", + "message" => "OK", + "result" => [ + %{ + "balance" => "135499", + "contractAddress" => "0x0000000000000000000000000000000000000000", + "name" => "Example Token", + "decimals" => "18", + "symbol" => "ET", + "type" => "ERC-20" + }, + %{ + "balance" => "1", + "contractAddress" => "0x0000000000000000000000000000000000000001", + "name" => "Example ERC-721 Token", + "decimals" => "18", + "symbol" => "ET7", + "type" => "ERC-721" + } + ] + } + + @account_getminedblocks_example_value %{ + "status" => "1", + "message" => "OK", + "result" => [ + %{ + "blockNumber" => "3462296", + "timeStamp" => "1491118514", + "blockReward" => "5194770940000000000" + } + ] + } + + @account_listaccounts_example_value %{ + "status" => "1", + "message" => "OK", + "result" => [ + %{ + "address" => "0x0000000000000000000000000000000000000000", + "balance" => "135499" + } + ] + } + + @account_getminedblocks_example_value_error %{ + "status" => "0", + "message" => "No blocks found", + "result" => [] + } + + @logs_getlogs_example_value %{ + "status" => "1", + "message" => "OK", + "result" => [ + %{ + "address" => "0x33990122638b9132ca29c723bdf037f1a891a70c", + "topics" => [ + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x000000000000000000000000d9b2f59f3b5c7b3c67047d2f03c3e8052470be92" + ], + "data" => "0x", + "blockNumber" => "0x5c958", + "timeStamp" => "0x561d688c", + "gasPrice" => "0xba43b7400", + "gasUsed" => "0x10682", + "logIndex" => "0x", + "transactionHash" => "0x0b03498648ae2da924f961dda00dc6bb0a8df15519262b7e012b7d67f4bb7e83", + "transactionIndex" => "0x" + } + ] + } + + @logs_getlogs_example_value_error %{ + "status" => "0", + "message" => "Invalid address format", + "result" => nil + } + + @token_gettoken_example_value %{ + "status" => "1", + "message" => "OK", + "result" => %{ + "cataloged" => true, + "contractAddress" => "0x0000000000000000000000000000000000000000", + "decimals" => "18", + "name" => "Example Token", + "symbol" => "ET", + "totalSupply" => "1000000000", + "type" => "ERC-20" + } + } + + @token_gettoken_example_value_error %{ + "status" => "0", + "message" => "Invalid contract address format", + "result" => nil + } + + @token_gettokenholders_example_value %{ + "status" => "1", + "message" => "OK", + "result" => [ + %{ + "address" => "0x0000000000000000000000000000000000000000", + "value" => "965208500001258757122850" + } + ] + } + + @token_gettokenholders_example_value_error %{ + "status" => "0", + "message" => "Invalid contract address format", + "result" => nil + } + + @stats_tokensupply_example_value %{ + "status" => "1", + "message" => "OK", + "result" => "21265524714464" + } + + @stats_ethsupplyexchange_example_value %{ + "status" => "1", + "message" => "OK", + "result" => "101959776311500000000000000" + } + + @stats_ethsupply_example_value %{ + "status" => "1", + "message" => "OK", + "result" => "101959776311500000000000000" + } + + @stats_coinsupply_example_value 101_959_776.3115 + + @stats_coinprice_example_value %{ + "status" => "1", + "message" => "OK", + "result" => %{ + "coin_btc" => "0.03246", + "coin_btc_timestamp" => "1537212510", + "coin_usd" => "204", + "coin_usd_timestamp" => "1537212513" + } + } + + @stats_totalfees_example_value %{ + "status" => "1", + "message" => "OK", + "result" => %{ + "total_fees" => "75411956011480008034" + } + } + + @stats_totalfees_example_value_error %{ + "status" => "0", + "message" => "An incorrect input date provided. It should be in ISO 8601 format (yyyy-mm-dd).", + "result" => nil + } + + @block_getblockreward_example_value %{ + "status" => "1", + "message" => "OK", + "result" => %{ + "blockNumber" => "2165403", + "timeStamp" => "1472533979", + "blockMiner" => "0x13a06d3dfe21e0db5c016c03ea7d2509f7f8d1e3", + "blockReward" => "5314181600000000000", + "uncles" => nil, + "uncleInclusionReward" => nil + } + } + + @block_getblockreward_example_value_error %{ + "status" => "0", + "message" => "Invalid block number", + "result" => nil + } + + @block_getblocknobytime_example_value %{ + "status" => "1", + "message" => "OK", + "result" => %{ + "blockNumber" => "2165403" + } + } + + @block_getblocknobytime_example_value_error %{ + "status" => "0", + "message" => "Invalid params", + "result" => nil + } + + @block_eth_block_number_example_value %{ + "jsonrpc" => "2.0", + "result" => "0xb33bf1", + "id" => 1 + } + + @contract_listcontracts_example_value %{ + "status" => "1", + "message" => "OK", + "result" => [ + %{ + "SourceCode" => """ + pragma solidity >0.4.24; + + contract Test { + constructor() public { b = hex"12345678901234567890123456789012"; } + event Event(uint indexed a, bytes32 b); + event Event2(uint indexed a, bytes32 b); + function foo(uint a) public { emit Event(a, b); } + bytes32 b; + } + """, + "ABI" => """ + [{ + "type":"event", + "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], + "name":"Event" + }, { + "type":"event", + "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], + "name":"Event2" + }, { + "type":"function", + "inputs": [{"name":"a","type":"uint256"}], + "name":"foo", + "outputs": [] + }] + """, + "ContractName" => "Test", + "CompilerVersion" => "v0.2.1-2016-01-30-91a6b35", + "OptimizationUsed" => "1" + } + ] + } + + @contract_getabi_example_value %{ + "status" => "1", + "message" => "OK", + "result" => + ~s([{"constant":false,"inputs":[{"name":"voucher_token","type":"bytes32"}],"name":"burn","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"voucher_token","type":"bytes32"}],"name":"is_expired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"voucher_token","type":"bytes32"}],"name":"is_burnt","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"voucher_token","type":"bytes32"},{"name":"_lifetime","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]) + } + + @contract_getabi_example_value_error %{ + "status" => "0", + "message" => "Contract source code not verified", + "result" => nil + } + + @contract_verify_example_value %{ + "status" => "1", + "message" => "OK", + "result" => %{ + "SourceCode" => """ + pragma solidity >0.4.24; + + contract Test { + constructor() public { b = hex"12345678901234567890123456789012"; } + event Event(uint indexed a, bytes32 b); + event Event2(uint indexed a, bytes32 b); + function foo(uint a) public { emit Event(a, b); } + bytes32 b; + } + """, + "ABI" => """ + [{ + "type":"event", + "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], + "name":"Event" + }, { + "type":"event", + "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], + "name":"Event2" + }, { + "type":"function", + "inputs": [{"name":"a","type":"uint256"}], + "name":"foo", + "outputs": [] + }] + """, + "ContractName" => "Test", + "CompilerVersion" => "v0.2.1-2016-01-30-91a6b35", + "OptimizationUsed" => "1", + "IsProxy" => "true", + "ImplementationAddress" => "0x000000000000000000000000000000000000000e" + } + } + + @contract_verify_example_value_error %{ + "status" => "0", + "message" => "There was an error verifying the contract.", + "result" => nil + } + + @contract_verifysourcecode_example_value %{ + "message" => "OK", + "result" => "b080b96bd06ad1c9341c2afb7e3730311388544961acde94", + "status" => "1" + } + + @contract_checkverifystatus_example_value %{ + "message" => "OK", + "result" => "Pending in queue", + "status" => "1" + } + + @contract_getsourcecode_example_value %{ + "status" => "1", + "message" => "OK", + "result" => %{ + "SourceCode" => """ + pragma solidity >0.4.24; + + contract Test { + constructor() public { b = hex"12345678901234567890123456789012"; } + event Event(uint indexed a, bytes32 b); + event Event2(uint indexed a, bytes32 b); + function foo(uint a) public { emit Event(a, b); } + bytes32 b; + } + """, + "ABI" => """ + [{ + "type":"event", + "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], + "name":"Event" + }, { + "type":"event", + "inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}], + "name":"Event2" + }, { + "type":"function", + "inputs": [{"name":"a","type":"uint256"}], + "name":"foo", + "outputs": [] + }] + """, + "ContractName" => "Test", + "CompilerVersion" => "v0.2.1-2016-01-30-91a6b35", + "OptimizationUsed" => "1", + "FileName" => "{sourcify path or empty}", + "IsProxy" => "true", + "ImplementationAddress" => "0x000000000000000000000000000000000000000e" + } + } + + @contract_getsourcecode_example_value_error %{ + "status" => "0", + "message" => "Invalid address hash", + "result" => nil + } + + @transaction_gettxinfo_example_value %{ + "status" => "1", + "result" => %{ + "blockNumber" => "3", + "confirmations" => "0", + "from" => "0x000000000000000000000000000000000000000c", + "gasLimit" => "91966", + "gasUsed" => "95123", + "gasPrice" => "100000", + "hash" => "0x0000000000000000000000000000000000000000000000000000000000000004", + "input" => "0x04", + "logs" => [ + %{ + "address" => "0x000000000000000000000000000000000000000e", + "data" => "0x00", + "topics" => ["First Topic", "Second Topic", "Third Topic", "Fourth Topic"] + } + ], + "success" => true, + "timeStamp" => "1541018182", + "to" => "0x000000000000000000000000000000000000000d", + "value" => "67612", + revertReason: "No credit of that type" + } + } + + @transaction_gettxreceiptstatus_example_value %{ + "status" => "1", + "message" => "OK", + "result" => %{ + "status" => "1" + } + } + + @transaction_gettxreceiptstatus_example_value_error %{ + "status" => "0", + "message" => "Query parameter txhash is required", + "result" => nil + } + + @transaction_getstatus_example_value %{ + "status" => "1", + "message" => "OK", + "result" => %{ + "isError" => "1", + "errDescription" => "Out of gas" + } + } + + @transaction_getstatus_example_value_error %{ + "status" => "0", + "message" => "Query parameter txhash is required", + "result" => nil + } + + @status_type %{ + type: "status", + enum: ~s(["0", "1"]), + enum_interpretation: %{"0" => "error", "1" => "ok"} + } + + @jsonrpc_version_type %{ + type: "string", + example: ~s("2.0") + } + + @message_type %{ + type: "string", + example: ~s("OK") + } + + @hex_number_type %{ + type: "string", + example: ~s("767969") + } + + @id_type %{ + type: "string", + example: ~s("1") + } + + @wei_type %{ + type: "wei", + definition: &__MODULE__.wei_type_definition/1, + example: ~s("663046792267785498951364") + } + + @gas_type %{ + type: "gas", + definition: "A nonnegative number roughly equivalent to computational steps.", + example: ~s("122261") + } + + @address_hash_type %{ + type: "address hash", + definition: "A 160-bit code used for identifying accounts or contracts.", + example: ~s("0x95426f2bc716022fcf1def006dbc4bb81f5b5164") + } + + @stale_type %{ + type: "boolean", + definition: + "Represents whether or not the balance has not been checked in the last 24 hours, and will be rechecked.", + example: true + } + + @transaction_hash_type %{ + type: "transaction hash", + definition: + "Either a 20-byte address hash or, in the case of being a contract creation transaction, it is the RLP empty byte sequence. Used for identifying transactions.", + example: ~s("0x9c81f44c29ff0226f835cd0a8a2f2a7eca6db52a711f8211b566fd15d3e0e8d4") + } + + @block_number_type %{ + type: "block number", + definition: "A nonnegative number used to identify blocks.", + example: ~s("34092") + } + + @input_type %{ + type: "input", + definition: "Data sent along with the transaction. A variable-byte-length binary.", + example: ~s("0x797af627d02e23b68e085092cd0d47d6cfb54be025f37b5989c0264398f534c08af7dea9") + } + + @confirmation_type %{ + type: "confirmations", + definition: "A number equal to the current block height minus the transaction's block-number.", + example: ~s("6005998") + } + + @transaction_index_type %{ + type: "transaction index", + definition: "Index of the transaction in it's block.", + example: ~s("0") + } + + @token_name_type %{ + type: "string", + definition: "Name of the token.", + example: ~s("Some Token Name") + } + + @token_id_type %{ + type: "integer", + definition: "id of token", + example: ~s("0") + } + + @token_symbol_type %{ + type: "string", + definition: "Trading symbol of the token.", + example: ~s("SYMBOL") + } + + @token_decimal_type %{ + type: "integer", + definition: "Number of decimal places the token can be subdivided to.", + example: ~s("18") + } + + @revert_reason_type %{ + type: "revert_reason", + definition: "Revert reason of transaction.", + example: ~s("No credit of that type") + } + + @logs_details %{ + name: "Log Detail", + fields: %{ + address: @address_hash_type, + topics: %{ + type: "topics", + definition: "An array including the topics for the log.", + example: ~s(["0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"]) + }, + data: %{ + type: "data", + definition: "Non-indexed log parameters.", + example: ~s("0x") + }, + blockNumber: %{ + type: "block number", + definition: "A nonnegative number used to identify blocks.", + example: ~s("0x5c958") + }, + index: %{ + type: "log index", + definition: "A nonnegative number used to identify logs.", + example: ~s("1") + } + } + } + + @token_holder_details %{ + name: "Token holder Detail", + fields: %{ + address: @address_hash_type, + value: %{ + type: "value", + definition: "A nonnegative number used to identify the balance of the target token.", + example: ~s("1000000000000000000") + } + } + } + + @address_balance %{ + name: "AddressBalance", + fields: %{ + address: @address_hash_type, + balance: @wei_type, + stale: @stale_type + } + } + + @transaction %{ + name: "Transaction", + fields: %{ + blockNumber: @block_number_type, + timeStamp: %{ + type: "timestamp", + definition: "The transaction's block-timestamp.", + example: ~s("1439232889") + }, + hash: @transaction_hash_type, + nonce: %{ + type: "nonce", + definition: "A scalar value equal to the number of transactions sent by the sender prior to this transaction.", + example: ~s("0") + }, + blockHash: %{ + type: "block hash", + definition: "A 32-byte hash used for identifying blocks.", + example: ~s("0xd3cabad6adab0b52eb632c386ea194036805713682c62cb589b5abcd76de2159") + }, + transactionIndex: @transaction_index_type, + from: @address_hash_type, + to: @address_hash_type, + value: @wei_type, + gas: @gas_type, + gasPrice: @wei_type, + isError: %{ + type: "error", + enum: ~s(["0", "1"]), + enum_interpretation: %{"0" => "ok", "1" => "error"} + }, + txreceipt_status: @status_type, + input: @input_type, + contractAddress: @address_hash_type, + cumulativeGasUsed: @gas_type, + gasUsed: @gas_type, + confirmations: @confirmation_type + } + } + + @internal_transaction %{ + name: "InternalTransaction", + fields: %{ + blockNumber: @block_number_type, + timeStamp: %{ + type: "timestamp", + definition: "The transaction's block-timestamp.", + example: ~s("1439232889") + }, + from: @address_hash_type, + to: @address_hash_type, + value: @wei_type, + contractAddress: @address_hash_type, + input: @input_type, + type: %{ + type: "type", + definition: ~s(Possible values: "create", "call", "reward", or "selfdestruct"), + example: ~s("create") + }, + callType: %{ + type: "type", + definition: ~s(Possible values: "call", "callcode", "delegatecall", or "staticcall"), + example: ~s("delegatecall") + }, + gas: @gas_type, + gasUsed: @gas_type, + isError: %{ + type: "error", + enum: ~s(["0", "1"]), + enum_interpretation: %{"0" => "ok", "1" => "rejected/cancelled"} + }, + errCode: %{ + type: "string", + definition: "Error message when call type error.", + example: ~s("Out of gas") + } + } + } + + @block_model %{ + name: "Block", + fields: %{ + blockNumber: @block_number_type, + timeStamp: %{ + type: "timestamp", + definition: "When the block was collated.", + example: ~s("1480072029") + } + } + } + + @token_transfer_model %{ + name: "TokenTransfer", + fields: %{ + blockNumber: @block_number_type, + timeStamp: %{ + type: "timestamp", + definition: "The transaction's block-timestamp.", + example: ~s("1439232889") + }, + hash: @transaction_hash_type, + nonce: %{ + type: "nonce", + definition: "A scalar value equal to the number of transactions sent by the sender prior to this transaction.", + example: ~s("0") + }, + blockHash: %{ + type: "block hash", + definition: "A 32-byte hash used for identifying blocks.", + example: ~s("0xd3cabad6adab0b52eb632c386ea194036805713682c62cb589b5abcd76de2159") + }, + from: @address_hash_type, + contractAddress: @address_hash_type, + to: @address_hash_type, + value: %{ + type: "integer", + definition: "The transferred amount.", + example: ~s("663046792267785498951364") + }, + tokenName: @token_name_type, + tokenID: @token_id_type, + tokenSymbol: @token_symbol_type, + tokenDecimal: @token_decimal_type, + transactionIndex: @transaction_index_type, + gas: @gas_type, + gasPrice: @wei_type, + gasUsed: @gas_type, + cumulativeGasUsed: @gas_type, + input: @input_type, + confirmations: @confirmation_type + } + } + + @log %{ + name: "Log", + fields: %{ + address: @address_hash_type, + topics: %{ + type: "topics", + definition: "An array including the topics for the log.", + example: ~s(["0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"]) + }, + data: %{ + type: "data", + definition: "Non-indexed log parameters.", + example: ~s("0x") + }, + blockNumber: %{ + type: "block number", + definition: "A nonnegative number used to identify blocks.", + example: ~s("0x5c958") + }, + timeStamp: %{ + type: "timestamp", + definition: "The transaction's block-timestamp.", + example: ~s("0x561d688c") + }, + gasPrice: %{ + type: "wei", + definition: &__MODULE__.wei_type_definition/1, + example: ~s("0xba43b7400") + }, + gasUsed: %{ + type: "gas", + definition: "A nonnegative number roughly equivalent to computational steps.", + example: ~s("0x10682") + }, + logIndex: %{ + type: "hexadecimal", + example: ~s("0x") + }, + transactionHash: @transaction_hash_type, + transactionIndex: %{ + type: "hexadecimal", + example: ~s("0x") + } + } + } + + @token_model %{ + name: "Token", + fields: %{ + name: @token_name_type, + symbol: @token_symbol_type, + totalSupply: %{ + type: "integer", + definition: "The total supply of the token.", + example: ~s("1000000000") + }, + decimals: @token_decimal_type, + type: %{ + type: "token type", + enum: ~s(["ERC-20", "ERC-721"]), + enum_interpretation: %{"ERC-20" => "ERC-20 token standard", "ERC-721" => "ERC-721 token standard"} + }, + cataloged: %{ + type: "boolean", + definition: "Flag for if token information has been cataloged.", + example: ~s(true) + }, + contractAddress: @address_hash_type + } + } + + @token_balance_model %{ + name: "TokenBalance", + fields: %{ + balance: %{ + type: "integer", + definition: "The token account balance.", + example: ~s("135499") + }, + name: @token_name_type, + symbol: @token_symbol_type, + decimals: @token_decimal_type, + contractAddress: @address_hash_type + } + } + + @block_reward_model %{ + name: "BlockReward", + fields: %{ + blockNumber: @block_number_type, + timeStamp: %{ + type: "timestamp", + definition: "When the block was collated.", + example: ~s("1480072029") + }, + blockMiner: @address_hash_type, + blockReward: %{ + type: "block reward", + definition: "The reward given to the miner of a block.", + example: ~s("5003251945421042780") + }, + uncles: %{type: "null"}, + uncleInclusionReward: %{type: "null"} + } + } + + @block_no_model %{ + name: "BlockNo", + fields: %{ + blockNumber: @block_number_type + } + } + + @account_model %{ + name: "Account", + fields: %{ + "address" => @address_hash_type, + "balance" => @wei_type + } + } + + @contract_model %{ + name: "Contract", + fields: %{ + "Address" => @address_hash_type, + "ABI" => %{ + type: "ABI", + definition: "JSON string for the contract's Application Binary Interface (ABI)", + example: """ + "[{ + \\"type\\":\\"event\\", + \\"inputs\\": [{\\"name\\":\\"a\\",\\"type\\":\\"uint256\\",\\"indexed\\":true},{\\"name\\":\\"b\\",\\"type\\":\\"bytes32\\",\\"indexed\\":false}], + \\"name\\":\\"Event\\" + }, { + \\"type\\":\\"event\\", + \\"inputs\\": [{\\"name\\":\\"a\\",\\"type\\":\\"uint256\\",\\"indexed\\":true},{\\"name\\":\\"b\\",\\"type\\":\\"bytes32\\",\\"indexed\\":false}], + \\"name\\":\\"Event2\\" + }, { + \\"type\\":\\"function\\", + \\"inputs\\": [{\\"name\\":\\"a\\",\\"type\\":\\"uint256\\"}], + \\"name\\":\\"foo\\", + \\"outputs\\": [] + }]" + """ + }, + "ContractName" => %{ + type: "string", + example: ~S("Some name") + }, + "OptimizationUsed" => %{ + type: "optimization used", + enum: ~s(["0", "1"]), + enum_interpretation: %{"0" => "false", "1" => "true"} + } + } + } + + @uid_response_model %{ + name: "UID", + fields: %{ + "UID" => %{ + type: "string", + definition: "Unique identifier of the verification attempt", + example: "b080b96bd06ad1c9341c2afb7e3730311388544961acde94" + } + } + } + + @status_response_model %{ + name: "Status", + fields: %{ + "status" => %{ + type: "string", + definition: "Current status of the verification attempt", + example: "`Pending in queue` | `Pass - Verified` | `Fail - Unable to verify` | `Unknown UID`" + } + } + } + + @contract_source_code_type %{ + type: "contract source code", + definition: "The contract's source code.", + example: """ + "pragma solidity >0.4.24; + + contract Test { + constructor() public { b = hex"12345678901234567890123456789012"; } + event Event(uint indexed a, bytes32 b); + event Event2(uint indexed a, bytes32 b); + function foo(uint a) public { emit Event(a, b); } + bytes32 b; + }" + """ + } + + @contract_decompiled_source_code_type %{ + type: "contract decompiled source code", + definition: "The contract's decompiled source code.", + example: """ + const name() = 'CryptoKitties' + const GEN0_STARTING_PRICE() = 10^16 + const GEN0_AUCTION_DURATION() = 86400 + const GEN0_CREATION_LIMIT() = 45000 + const symbol() = 'CK' + const PROMO_CREATION_LIMIT() = 5000 + def storage: + ceoAddress is addr # mask(160, 0) at storage #0 + cfoAddress is addr # mask(160, 0) at storage #1 + stor1.768 is uint16 => uint256 # mask(256, 768) at storage #1 + cooAddress is addr # mask(160, 0) at storage #2 + stor2.0 is uint256 => uint256 # mask(256, 0) at storage #2 + paused is uint8 # mask(8, 160) at storage #2 + stor2.256 is uint256 => uint256 # mask(256, 256) at storage #2 + stor3 is uint32 # + ... + """ + } + + @contract_decompiler_version_type %{ + type: "decompiler version", + definition: "When decompiled source code is present, the decompiler version with which it was generated.", + example: "decompiler.version" + } + + @contract_with_sourcecode_model @contract_model + |> put_in([:fields, "SourceCode"], @contract_source_code_type) + |> put_in([:fields, "DecompiledSourceCode"], @contract_decompiled_source_code_type) + |> put_in([:fields, "DecompilerVersion"], @contract_decompiler_version_type) + + @transaction_receipt_status_model %{ + name: "TransactionReceiptStatus", + fields: %{ + status: %{ + type: "status", + enum: ~s(["0", "1"]), + enum_interpretation: %{"0" => "fail", "1" => "pass"} + } + } + } + + @transaction_info_model %{ + name: "TransactionInfo", + fields: %{ + hash: @transaction_hash_type, + timeStamp: %{ + type: "timestamp", + definition: "The transaction's block-timestamp.", + example: ~s("1439232889") + }, + blockNumber: @block_number_type, + confirmations: @confirmation_type, + success: %{ + type: "boolean", + definition: "Flag for success during tx execution", + example: ~s(true) + }, + from: @address_hash_type, + to: @address_hash_type, + value: @wei_type, + input: @input_type, + gasLimit: @wei_type, + gasUsed: @gas_type, + gasPrice: @wei_type, + logs: %{ + type: "array", + array_type: @logs_details + }, + revertReason: @revert_reason_type + } + } + + @transaction_status_model %{ + name: "TransactionStatus", + fields: %{ + isError: %{ + type: "isError", + enum: ~s(["0", "1"]), + enum_interpretation: %{"0" => "pass", "1" => "error"} + }, + errDescription: %{ + type: "string", + example: ~s("Out of gas") + } + } + } + + @coin_price_model %{ + name: "CoinPrice", + fields: %{ + coin_btc: %{ + type: "coin_btc", + definition: &__MODULE__.coin_btc_type_definition/1, + example: ~s("0.03161") + }, + coin_btc_timestamp: %{ + type: "timestamp", + definition: "Last updated timestamp.", + example: ~s("1537234460") + }, + coin_usd: %{ + type: "coin_usd", + definition: &__MODULE__.coin_usd_type_definition/1, + example: ~s("197.57") + }, + coin_usd_timestamp: %{ + type: "timestamp", + definition: "Last updated timestamp.", + example: ~s("1537234460") + } + } + } + + @total_fees_model %{ + name: "TotalFees", + fields: %{ + total_fees: %{ + type: "total_fees", + definition: "Total transaction fees in Wei are paid by users to validators per day.", + example: ~s("75411956011480008034") + } + } + } + + @account_eth_get_balance_action %{ + name: "eth_get_balance", + description: + "Mimics Ethereum JSON RPC's eth_getBalance. Returns the balance as of the provided block (defaults to latest)", + required_params: [ + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "The address of the account." + } + ], + optional_params: [ + %{ + key: "block", + placeholder: "block", + type: "string", + description: """ + Either the block number as a string, or one of latest, earliest or pending + + latest will be the latest balance in a *consensus* block. + earliest will be the first recorded balance for the address. + pending will be the latest balance in consensus *or* nonconcensus blocks. + """ + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@account_eth_get_balance_example_value), + model: %{ + name: "Result", + fields: %{ + jsonrpc: @jsonrpc_version_type, + id: @id_type, + result: @hex_number_type + } + } + } + ] + } + + @account_balance_action %{ + name: "balance", + description: """ + Get balance for address. Also available through a GraphQL 'addresses' query. + + If the balance hasn't been updated in a long time, we will double check + with the node to fetch the absolute latest balance. This will not be + reflected in the current request, but once it is updated, subsequent requests + will show the updated balance. If you want to know whether or not we are checking + for another balance, use the `balancemulti` action. That contains a property + called `stale` that will let you know to recheck that balance in the near future. + """, + required_params: [ + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "A 160-bit code used for identifying Accounts." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@account_balance_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: @wei_type + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@account_balance_example_value_error) + } + ] + } + + @account_balancemulti_action %{ + name: "balancemulti", + description: """ + Get balance for multiple addresses. Also available through a GraphQL 'addresses' query. + + If the balance hasn't been updated in a long time, we will double check + with the node to fetch the absolute latest balance. This will not be + reflected in the current request, but once it is updated, subsequent requests + will show the updated balance. You can know that this is taking place via + the `stale` attribute, which is set to `true` if a new balance is being fetched. + """, + required_params: [ + %{ + key: "address", + placeholder: "addressHash1,addressHash2,addressHash3", + type: "string", + description: + "A 160-bit code used for identifying Accounts. Separate addresses by comma. Maximum of 20 addresses." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@account_balancemulti_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @address_balance + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@account_balance_example_value_error) + } + ] + } + + @account_pendingtxlist_action %{ + name: "pendingtxlist", + description: "Get pending transactions by address.", + required_params: [ + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "A 160-bit code used for identifying Accounts." + } + ], + optional_params: [ + %{ + key: "page", + type: "integer", + description: + "A nonnegative integer that represents the page number to be used for pagination. 'offset' must be provided in conjunction." + }, + %{ + key: "offset", + type: "integer", + description: + "A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@account_pendingtxlist_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @transaction + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@account_txlist_example_value_error) + } + ] + } + + @account_txlist_action %{ + name: "txlist", + description: + "Get transactions by address. Up to a maximum of 10,000 transactions. Also available through a GraphQL 'address' query.", + required_params: [ + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "A 160-bit code used for identifying Accounts." + } + ], + optional_params: [ + %{ + key: "sort", + type: "string", + description: + "A string representing the order by block number direction. Defaults to descending order. Available values: asc, desc" + }, + %{ + key: "start_block", + type: "integer", + description: "A nonnegative integer that represents the starting block number." + }, + %{ + key: "end_block", + type: "integer", + description: "A nonnegative integer that represents the ending block number." + }, + %{ + key: "page", + type: "integer", + description: + "A nonnegative integer that represents the page number to be used for pagination. 'offset' must be provided in conjunction." + }, + %{ + key: "offset", + type: "integer", + description: + "A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction." + }, + %{ + key: "filter_by", + type: "string", + description: """ + A string representing the field to filter by. If none is given + it returns transactions that match to, from, or contract address. + Available values: to, from + """ + }, + %{ + key: "start_timestamp", + type: "unix timestamp", + description: "Represents the starting block timestamp." + }, + %{ + key: "end_timestamp", + type: "unix timestamp", + description: "Represents the ending block timestamp." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@account_txlist_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @transaction + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@account_txlist_example_value_error) + } + ] + } + + @account_txlistinternal_action %{ + name: "txlistinternal", + description: + "Get internal transactions by transaction or address hash. Up to a maximum of 10,000 internal transactions. Also available through a GraphQL 'transaction' query.", + required_params: [ + %{ + key: "txhash", + placeholder: "transactionHash", + type: "string", + description: + "Transaction hash. Hash of contents of the transaction. A transcation hash or address hash is required." + } + ], + optional_params: [ + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "A 160-bit code used for identifying accounts. An address hash or transaction hash is required." + }, + %{ + key: "sort", + type: "string", + description: + "A string representing the order by block number direction. Defaults to ascending order. Available values: asc, desc. WARNING: Only available if 'address' is provided." + }, + %{ + key: "start_block", + type: "integer", + description: + "A nonnegative integer that represents the starting block number. WARNING: Only available if 'address' is provided." + }, + %{ + key: "end_block", + type: "integer", + description: + "A nonnegative integer that represents the ending block number. WARNING: Only available if 'address' is provided." + }, + %{ + key: "page", + type: "integer", + description: + "A nonnegative integer that represents the page number to be used for pagination. 'offset' must be provided in conjunction. WARNING: Only available if 'address' is provided." + }, + %{ + key: "offset", + type: "integer", + description: + "A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction. WARNING: Only available if 'address' is provided." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@account_txlistinternal_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @internal_transaction + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@account_txlistinternal_example_value_error) + } + ] + } + + @account_tokentx_action %{ + name: "tokentx", + description: + "Get token transfer events by address. Up to a maximum of 10,000 token transfer events. Also available through a GraphQL 'token_transfers' query.", + required_params: [ + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "A 160-bit code used for identifying accounts." + } + ], + optional_params: [ + %{ + key: "contractaddress", + placeholder: "contractAddressHash", + type: "string", + description: "A 160-bit code used for identifying contracts." + }, + %{ + key: "sort", + type: "string", + description: + "A string representing the order by block number direction. Defaults to ascending order. Available values: asc, desc" + }, + %{ + key: "start_block", + type: "integer", + description: "A nonnegative integer that represents the starting block number." + }, + %{ + key: "end_block", + type: "integer", + description: "A nonnegative integer that represents the ending block number." + }, + %{ + key: "page", + type: "integer", + description: + "A nonnegative integer that represents the page number to be used for pagination. 'offset' must be provided in conjunction." + }, + %{ + key: "offset", + type: "integer", + description: + "A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@account_tokentx_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @token_transfer_model + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@account_tokentx_example_value_error) + } + ] + } + + @account_tokenbalance_action %{ + name: "tokenbalance", + description: "Get token account balance for token contract address.", + required_params: [ + %{ + key: "contractaddress", + placeholder: "contractAddressHash", + type: "string", + description: "A 160-bit code used for identifying contracts." + }, + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "A 160-bit code used for identifying accounts." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@account_tokenbalance_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "integer", + definition: "The token account balance for the contract address.", + example: ~s("135499") + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@account_tokenbalance_example_value_error) + } + ] + } + + @account_tokenlist_action %{ + name: "tokenlist", + description: "Get list of tokens owned by address.", + required_params: [ + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "A 160-bit code used for identifying accounts." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@account_tokenlist_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @token_balance_model + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@account_tokenbalance_example_value_error) + } + ] + } + + @account_getminedblocks_action %{ + name: "getminedblocks", + description: "Get list of blocks mined by address.", + required_params: [ + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "A 160-bit code used for identifying accounts." + } + ], + optional_params: [ + %{ + key: "page", + type: "integer", + description: + "A nonnegative integer that represents the page number to be used for pagination. 'offset' must be provided in conjunction." + }, + %{ + key: "offset", + type: "integer", + description: + "A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@account_getminedblocks_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @block_model + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@account_getminedblocks_example_value_error) + } + ] + } + + @account_listaccounts_action %{ + name: "listaccounts", + description: + "Get a list of accounts and their balances, sorted ascending by the time they were first seen by the explorer.", + required_params: [], + optional_params: [ + %{ + key: "page", + type: "integer", + description: + "A nonnegative integer that represents the page number to be used for pagination. 'offset' must be provided in conjunction." + }, + %{ + key: "offset", + type: "integer", + description: + "A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@account_listaccounts_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @account_model + } + } + } + } + ] + } + + @logs_getlogs_action %{ + name: "getLogs", + description: "Get event logs for an address and/or topics. Up to a maximum of 1,000 event logs.", + required_params: [ + %{ + key: "fromBlock", + placeholder: "blockNumber", + type: "integer", + description: + "A nonnegative integer that represents the starting block number. The use of 'latest' is also supported." + }, + %{ + key: "toBlock", + placeholder: "blockNumber", + type: "integer", + description: + "A nonnegative integer that represents the ending block number. The use of 'latest' is also supported." + }, + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "A 160-bit code used for identifying contracts. An address and/or topic{x} is required." + }, + %{ + key: "topic0", + placeholder: "firstTopic", + type: "string", + description: "A string equal to the first topic. A topic{x} and/or address is required." + } + ], + optional_params: [ + %{ + key: "topic1", + type: "string", + description: "A string equal to the second topic. A topic{x} and/or address is required." + }, + %{ + key: "topic2", + type: "string", + description: "A string equal to the third topic. A topic{x} and/or address is required." + }, + %{ + key: "topic3", + type: "string", + description: "A string equal to the fourth topic. A topic{x} and/or address is required." + }, + %{ + key: "topic0_1_opr", + type: "string", + description: + "A string representing the and|or operator for topic0 and topic1. " <> + "Required if topic0 and topic1 is used. Available values: and, or" + }, + %{ + key: "topic0_2_opr", + type: "string", + description: + "A string representing the and|or operator for topic0 and topic2. " <> + "Required if topic0 and topic2 is used. Available values: and, or" + }, + %{ + key: "topic0_3_opr", + type: "string", + description: + "A string representing the and|or operator for topic0 and topic3. " <> + "Required if topic0 and topic3 is used. Available values: and, or" + }, + %{ + key: "topic1_2_opr", + type: "string", + description: + "A string representing the and|or operator for topic1 and topic2. " <> + "Required if topic1 and topic2 is used. Available values: and, or" + }, + %{ + key: "topic1_3_opr", + type: "string", + description: + "A string representing the and|or operator for topic1 and topic3. " <> + "Required if topic1 and topic3 is used. Available values: and, or" + }, + %{ + key: "topic2_3_opr", + type: "string", + description: + "A string representing the and|or operator for topic2 and topic3. " <> + "Required if topic2 and topic3 is used. Available values: and, or" + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@logs_getlogs_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @log + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@logs_getlogs_example_value_error) + } + ] + } + + @token_gettoken_action %{ + name: "getToken", + description: + "Get ERC-20 " <> + "or ERC-721 token by contract address.", + required_params: [ + %{ + key: "contractaddress", + placeholder: "contractAddressHash", + type: "string", + description: "A 160-bit code used for identifying contracts." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@token_gettoken_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "model", + model: @token_model + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@token_gettoken_example_value_error) + } + ] + } + + @token_gettokenholders_action %{ + name: "getTokenHolders", + description: "Get token holders by contract address.", + required_params: [ + %{ + key: "contractaddress", + placeholder: "contractAddressHash", + type: "string", + description: "A 160-bit code used for identifying contracts." + } + ], + optional_params: [ + %{ + key: "page", + type: "integer", + description: + "A nonnegative integer that represents the page number to be used for pagination. 'offset' must be provided in conjunction." + }, + %{ + key: "offset", + type: "integer", + description: + "A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@token_gettokenholders_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @token_holder_details + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@token_gettokenholders_example_value_error) + } + ] + } + + @stats_tokensupply_action %{ + name: "tokensupply", + description: + "Get ERC-20 or " <> + "ERC-721 " <> + " token total supply by contract address.", + required_params: [ + %{ + key: "contractaddress", + placeholder: "contractAddressHash", + type: "string", + description: "A 160-bit code used for identifying contracts." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@stats_tokensupply_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "integer", + definition: "The total supply of the token.", + example: ~s("1000000000") + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@token_gettoken_example_value_error) + } + ] + } + + @stats_ethsupplyexchange_action %{ + name: "ethsupplyexchange", + description: "Get total supply in Wei from exchange.", + required_params: [], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@stats_ethsupplyexchange_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "integer", + description: "The total supply.", + example: ~s("101959776311500000000000000") + } + } + } + } + ] + } + + @stats_ethsupply_action %{ + name: "ethsupply", + description: "Get total supply in Wei from DB.", + required_params: [], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@stats_ethsupply_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "integer", + description: "The total supply in Wei from DB.", + example: ~s("101959776311500000000000000") + } + } + } + } + ] + } + + @stats_coinsupply_action %{ + name: "coinsupply", + description: "Get total coin supply from DB minus burnt number.", + required_params: [], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@stats_coinsupply_example_value), + model: %{ + name: "Result", + fields: %{ + result: %{ + type: "integer", + description: "The total supply from DB minus burnt number in coin dimension.", + example: 101_959_776.3115 + } + } + } + } + ] + } + + @stats_coinprice_action %{ + name: "coinprice", + description: "Get latest price of native coin in USD and BTC.", + required_params: [], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@stats_coinprice_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "model", + model: @coin_price_model + } + } + } + } + ] + } + + @stats_totalfees_action %{ + name: "totalfees", + description: "Gets total transaction fees in Wei are paid by users to validators per day.", + required_params: [ + %{ + key: "date", + placeholder: "date", + type: "string", + description: "day in ISO 8601 format (yyyy-mm-dd)" + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@stats_totalfees_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "model", + model: @total_fees_model + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@stats_totalfees_example_value_error) + } + ] + } + + @block_eth_block_number_action %{ + name: "eth_block_number", + description: "Mimics Ethereum JSON RPC's eth_blockNumber. Returns the lastest block number", + required_params: [], + optional_params: [ + %{ + key: "id", + placeholder: "request id", + type: "integer", + description: "A nonnegative integer that represents the json rpc request id." + } + ], + responses: [ + %{ + code: "200", + description: "successful request", + example_value: Jason.encode!(@block_eth_block_number_example_value), + model: %{ + name: "Result", + fields: %{ + jsonrpc: @jsonrpc_version_type, + id: @id_type, + result: @hex_number_type + } + } + } + ] + } + + @block_getblockreward_action %{ + name: "getblockreward", + description: "Get block reward by block number.", + required_params: [ + %{ + key: "blockno", + placeholder: "blockNumber", + type: "integer", + description: "A nonnegative integer that represents the block number." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@block_getblockreward_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "model", + model: @block_reward_model + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@block_getblockreward_example_value_error) + } + ] + } + + @block_getblocknobytime_action %{ + name: "getblocknobytime", + description: "Get Block Number by Timestamp.", + required_params: [ + %{ + key: "timestamp", + placeholder: "blockTimestamp", + type: "integer", + description: "A nonnegative integer that represents the block timestamp (Unix timestamp in seconds)." + }, + %{ + key: "closest", + placeholder: "before/after", + type: "string", + description: "Direction to find the closest block number to given timestamp. Available values: before/after." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@block_getblocknobytime_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "model", + model: @block_no_model + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@block_getblocknobytime_example_value_error) + } + ] + } + + @contract_listcontracts_action %{ + name: "listcontracts", + description: """ + Get a list of contracts, sorted ascending by the time they were first seen by the explorer. + + If you provide the filters `not_decompiled`(`4`) or `not_verified(4)` the results will not + be sorted for performance reasons. + """, + required_params: [], + optional_params: [ + %{ + key: "page", + type: "integer", + description: + "A nonnegative integer that represents the page number to be used for pagination. 'offset' must be provided in conjunction." + }, + %{ + key: "offset", + type: "integer", + description: + "A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction." + }, + %{ + key: "filter", + type: "string", + description: + "verified|decompiled|unverified|not_decompiled|empty, or 1|2|3|4|5 respectively. This requests only contracts with that status." + }, + %{ + key: "not_decompiled_with_version", + type: "string", + description: + "Ensures that none of the returned contracts were decompiled with the provided version. Ignored unless filtering for decompiled contracts." + }, + %{ + key: "verified_at_start_timestamp", + type: "unix timestamp", + description: + "Represents the starting timestamp when contracts verified. Taking into account only with `verified` filter." + }, + %{ + key: "verified_at_end_timestamp", + type: "unix timestamp", + description: + "Represents the ending timestamp when contracts verified. Taking into account only with `verified` filter." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@contract_listcontracts_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @contract_model + } + } + } + } + ] + } + + @contract_verify_action %{ + name: "verify", + description: """ + Verify a contract with its source code and contract creation information. +
    +
    +

    curl POST example:

    +
    +
    +
    +
    +
    + curl -d '{"addressHash":"0xc63BB6555C90846afACaC08A0F0Aa5caFCB382a1","compilerVersion":"v0.5.4+commit.9549d8ff", + "contractSourceCode":"pragma solidity ^0.5.4; \ncontract Test {\n}","name":"Test","optimization":false}' + -H "Content-Type: application/json" -X POST "https://blockscout.com/poa/sokol/api?module=contract&action=verify" + +
    +
    +
    + """, + required_params: [ + %{ + key: "addressHash", + placeholder: "addressHash", + type: "string", + description: "The address of the contract." + }, + %{ + key: "name", + placeholder: "name", + type: "string", + description: "The name of the contract." + }, + %{ + key: "compilerVersion", + placeholder: "compilerVersion", + type: "string", + description: "The compiler version for the contract." + }, + %{ + key: "optimization", + placeholder: false, + type: "boolean", + description: "Whether or not compiler optimizations were enabled." + }, + %{ + key: "contractSourceCode", + placeholder: "contractSourceCode", + type: "string", + description: "The source code of the contract." + } + ], + optional_params: [ + %{ + key: "constructorArguments", + type: "string", + description: "The constructor argument data provided." + }, + %{ + key: "autodetectConstructorArguments", + placeholder: false, + type: "boolean", + description: "Whether or not automatically detect constructor argument." + }, + %{ + key: "evmVersion", + placeholder: "evmVersion", + type: "string", + description: "The EVM version for the contract." + }, + %{ + key: "optimizationRuns", + placeholder: "optimizationRuns", + type: "integer", + description: "The number of optimization runs used during compilation" + }, + %{ + key: "library1Name", + type: "string", + description: "The name of the first library used." + }, + %{ + key: "library1Address", + type: "string", + description: "The address of the first library used." + }, + %{ + key: "library2Name", + type: "string", + description: "The name of the second library used." + }, + %{ + key: "library2Address", + type: "string", + description: "The address of the second library used." + }, + %{ + key: "library3Name", + type: "string", + description: "The name of the third library used." + }, + %{ + key: "library3Address", + type: "string", + description: "The address of the third library used." + }, + %{ + key: "library4Name", + type: "string", + description: "The name of the fourth library used." + }, + %{ + key: "library4Address", + type: "string", + description: "The address of the fourth library used." + }, + %{ + key: "library5Name", + type: "string", + description: "The name of the fourth library used." + }, + %{ + key: "library5Address", + type: "string", + description: "The address of the fourth library used." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@contract_verify_example_value), + type: "model", + model: @contract_model + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@contract_verify_example_value_error) + } + ] + } + + @contract_verify_via_sourcify_action %{ + name: "verify_via_sourcify", + description: """ + Verify a contract through Sourcify.
    + a) if smart-contract already verified on Sourcify, it will automatically fetch the data from the repo
    + b) otherwise you have to upload source files and JSON metadata file(s). +
    +
    +

    POST body example:

    +
    +
    +
    +
    +
    + --6e1e4c11657c62dc1e4349d024de9e28
    + Content-Disposition: form-data; name="addressHash"
    +
    + 0xb77b7443e0F32F1FEBf0BE0fBd7124D135d0a525
    +
    + --6e1e4c11657c62dc1e4349d024de9e28
    + Content-Disposition: form-data; name="files[0]"; filename="contract.sol"
    + Content-Type: application/json
    +
    + ...Source code...
    +
    + --6e1e4c11657c62dc1e4349d024de9e28
    + Content-Disposition: form-data; name="files[1]"; filename="metadata.json"
    + Content-Type: application/json
    +
    + ...JSON metadata...
    +
    + --6e1e4c11657c62dc1e4349d024de9e28--
    + +
    +
    +
    + """, + required_params: [ + %{ + key: "addressHash", + placeholder: "addressHash", + type: "string", + description: "The address of the contract." + } + ], + optional_params: [ + %{ + key: "files", + type: "file[]", + description: "Array with sources and metadata files" + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@contract_verify_example_value), + type: "model", + model: @contract_model + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@contract_verify_example_value_error) + } + ] + } + + @contract_verify_vyper_contract_action %{ + name: "verify_vyper_contract", + description: """ + Verify a vyper contract with its source code and contract creation information. +
    +
    +

    curl POST example:

    +
    +
    +
    +
    +
    + curl --location --request POST 'http://localhost:4000/api?module=contract&action=verify_vyper_contract' \ + --form 'contractSourceCode="SOURCE_CODE"' \ + --form 'name="Vyper_contract"' \ + --form 'addressHash="0xE60B1B8bD493569a3E945be50A6c89d29a560Fa1"' \ + --form 'compilerVersion="v0.2.12"' + +
    +
    +
    + """, + required_params: [ + %{ + key: "addressHash", + placeholder: "addressHash", + type: "string", + description: "The address of the contract." + }, + %{ + key: "name", + placeholder: "name", + type: "string", + description: "The name of the contract." + }, + %{ + key: "compilerVersion", + placeholder: "compilerVersion", + type: "string", + description: "The compiler version for the contract." + }, + %{ + key: "contractSourceCode", + placeholder: "contractSourceCode", + type: "string", + description: "The source code of the contract." + } + ], + optional_params: [ + %{ + key: "constructorArguments", + type: "string", + description: "The constructor argument data provided." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@contract_verify_example_value), + type: "model", + model: @contract_model + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@contract_verify_example_value_error) + } + ] + } + + @contract_verifysourcecode_action %{ + name: "verifysourcecode", + description: """ + Verify a contract with Standard input JSON file. Its interface the same as Etherscan's API endpoint +
    +
    + """, + required_params: [ + %{ + name: "solidity-standard-json-input", + key: "codeformat", + placeholder: "solidity-standard-json-input", + type: "string", + description: "Format of sourceCode(supported only \"solidity-standard-json-input\")" + }, + %{ + key: "contractaddress", + placeholder: "contractaddress", + type: "string", + description: "The address of the contract." + }, + %{ + key: "contractname", + placeholder: "contractname", + type: "string", + description: + "The name of the contract. It could be empty string(\"\"), just contract name(\"ContractName\"), or filename and contract name(\"contracts/contract_1.sol:ContractName\")" + }, + %{ + key: "compilerversion", + placeholder: "compilerversion", + type: "string", + description: "The compiler version for the contract." + }, + %{ + key: "sourceCode", + placeholder: "sourceCode", + type: "string", + description: "Standard input json" + } + ], + optional_params: [ + %{ + key: "constructorArguements", + type: "string", + description: "The constructor argument data provided." + }, + %{ + key: "autodetectConstructorArguments", + placeholder: false, + type: "boolean", + description: "Whether or not automatically detect constructor argument." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@contract_verifysourcecode_example_value), + type: "model", + model: @uid_response_model + } + ] + } + + @contract_checkverifystatus_action %{ + name: "checkverifystatus", + description: "Return status of the verification attempt (works in addition to verifysourcecode method)", + required_params: [ + %{ + key: "guid", + placeholder: "identifierString", + type: "string", + description: "A string used for identifying verification attempt" + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@contract_checkverifystatus_example_value), + type: "model", + model: @status_response_model + } + ] + } + + @contract_getabi_action %{ + name: "getabi", + description: "Get ABI for verified contract. Also available through a GraphQL 'addresses' query.", + required_params: [ + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "A 160-bit code used for identifying contracts." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@contract_getabi_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "abi", + definition: "JSON string for the Application Binary Interface (ABI)" + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@contract_getabi_example_value_error) + } + ] + } + + @contract_getsourcecode_action %{ + name: "getsourcecode", + description: "Get contract source code for verified contract. Also available through a GraphQL 'addresses' query.", + required_params: [ + %{ + key: "address", + placeholder: "addressHash", + type: "string", + description: "A 160-bit code used for identifying contracts." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@contract_getsourcecode_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "array", + array_type: @contract_with_sourcecode_model + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@contract_getsourcecode_example_value_error) + } + ] + } + + @transaction_gettxinfo_action %{ + name: "gettxinfo", + description: "Get transaction info.", + required_params: [ + %{ + key: "txhash", + placeholder: "transactionHash", + type: "string", + description: "Transaction hash. Hash of contents of the transaction." + } + ], + optional_params: [ + %{ + key: "index", + type: "integer", + description: "A nonnegative integer that represents the log index to be used for pagination." + } + ], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@transaction_gettxinfo_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "model", + model: @transaction_info_model + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@transaction_gettxreceiptstatus_example_value_error) + } + ] + } + + @transaction_gettxreceiptstatus_action %{ + name: "gettxreceiptstatus", + description: "Get transaction receipt status. Also available through a GraphQL 'transaction' query.", + required_params: [ + %{ + key: "txhash", + placeholder: "transactionHash", + type: "string", + description: "Transaction hash. Hash of contents of the transaction." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@transaction_gettxreceiptstatus_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "model", + model: @transaction_receipt_status_model + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@transaction_gettxreceiptstatus_example_value_error) + } + ] + } + + @transaction_getstatus_action %{ + name: "getstatus", + description: "Get error status and error message. Also available through a GraphQL 'transaction' query.", + required_params: [ + %{ + key: "txhash", + placeholder: "transactionHash", + type: "string", + description: "Transaction hash. Hash of contents of the transaction." + } + ], + optional_params: [], + responses: [ + %{ + code: "200", + description: "successful operation", + example_value: Jason.encode!(@transaction_getstatus_example_value), + model: %{ + name: "Result", + fields: %{ + status: @status_type, + message: @message_type, + result: %{ + type: "model", + model: @transaction_status_model + } + } + } + }, + %{ + code: "200", + description: "error", + example_value: Jason.encode!(@transaction_getstatus_example_value_error) + } + ] + } + + @account_module %{ + name: "account", + actions: [ + @account_eth_get_balance_action, + @account_balance_action, + @account_balancemulti_action, + @account_pendingtxlist_action, + @account_txlist_action, + @account_txlistinternal_action, + @account_tokentx_action, + @account_tokenbalance_action, + @account_tokenlist_action, + @account_getminedblocks_action, + @account_listaccounts_action + ] + } + + @logs_module %{ + name: "logs", + actions: [@logs_getlogs_action] + } + + @token_module %{ + name: "token", + actions: [ + @token_gettoken_action, + @token_gettokenholders_action + ] + } + + @stats_module %{ + name: "stats", + actions: [ + @stats_tokensupply_action, + @stats_ethsupplyexchange_action, + @stats_ethsupply_action, + @stats_coinsupply_action, + @stats_coinprice_action, + @stats_totalfees_action + ] + } + + @block_module %{ + name: "block", + actions: [@block_getblockreward_action, @block_getblocknobytime_action, @block_eth_block_number_action] + } + + @contract_module %{ + name: "contract", + actions: [ + @contract_listcontracts_action, + @contract_getabi_action, + @contract_getsourcecode_action, + @contract_verify_action, + @contract_verify_via_sourcify_action, + @contract_verify_vyper_contract_action, + @contract_verifysourcecode_action, + @contract_checkverifystatus_action + ] + } + + @transaction_module %{ + name: "transaction", + actions: [ + @transaction_gettxinfo_action, + @transaction_gettxreceiptstatus_action, + @transaction_getstatus_action + ] + } + + @documentation [ + @account_module, + @logs_module, + @token_module, + @stats_module, + @block_module, + @contract_module, + @transaction_module + ] + + def get_documentation do + @documentation + end + + def wei_type_definition(coin) do + "The smallest subdenomination of #{coin}, " <> + "and thus the one in which all integer values of the currency are counted, is the Wei. " <> + "One #{coin} is defined as being 1018 Wei." + end + + def coin_btc_type_definition(coin) do + "#{coin} price in Bitcoin." + end + + def coin_usd_type_definition(coin) do + "#{coin} price in US dollars." + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/gettext.ex b/apps/block_scout_web/lib/block_scout_web/gettext.ex new file mode 100644 index 0000000..3d54e80 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/gettext.ex @@ -0,0 +1,24 @@ +defmodule BlockScoutWeb.Gettext do + @moduledoc """ + A module providing Internationalization with a gettext-based API. + + By using [Gettext](https://hexdocs.pm/gettext), + your module gains a set of macros for translations, for example: + + import BlockScoutWeb.Gettext + + # Simple translation + gettext "Here is the string to translate" + + # Plural translation + ngettext "Here is the string to translate", + "Here are the strings to translate", + 3 + + # Domain-based translation + dgettext "errors", "Here is the error message to translate" + + See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. + """ + use Gettext, otp_app: :block_scout_web +end diff --git a/apps/block_scout_web/lib/block_scout_web/models/get_address_tags.ex b/apps/block_scout_web/lib/block_scout_web/models/get_address_tags.ex new file mode 100644 index 0000000..8f0283c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/models/get_address_tags.ex @@ -0,0 +1,74 @@ +defmodule BlockScoutWeb.Models.GetAddressTags do + @moduledoc """ + Get various types of tags associated with the address + """ + + import Ecto.Query, only: [from: 2] + + alias Explorer.Account.{TagAddress, WatchlistAddress} + alias Explorer.Repo + alias Explorer.Tags.{AddressTag, AddressToTag} + + def get_address_tags(nil, nil), + do: %{common_tags: [], personal_tags: [], watchlist_names: []} + + def get_address_tags(address_hash, current_user) when not is_nil(address_hash) do + %{ + common_tags: get_tags_on_address(address_hash), + personal_tags: get_personal_tags(address_hash, current_user), + watchlist_names: get_watchlist_names_on_address(address_hash, current_user) + } + end + + def get_address_tags(_, _), do: %{common_tags: [], personal_tags: [], watchlist_names: []} + + def get_public_tags(address_hash) when not is_nil(address_hash) do + %{ + common_tags: get_tags_on_address(address_hash) + } + end + + def get_tags_on_address(address_hash) when not is_nil(address_hash) do + query = + from( + tt in AddressTag, + left_join: att in AddressToTag, + on: tt.id == att.tag_id, + where: att.address_hash == ^address_hash, + where: tt.label != ^"validator", + select: %{label: tt.label, display_name: tt.display_name, address_hash: att.address_hash} + ) + + Repo.all(query) + end + + def get_tags_on_address(_), do: [] + + def get_personal_tags(address_hash, %{id: id}) when not is_nil(address_hash) do + query = + from( + ta in TagAddress, + where: ta.address_hash_hash == ^address_hash, + where: ta.identity_id == ^id, + select: %{label: ta.name, display_name: ta.name, address_hash: ta.address_hash} + ) + + Repo.account_repo().all(query) + end + + def get_personal_tags(_, _), do: [] + + def get_watchlist_names_on_address(address_hash, %{watchlist_id: watchlist_id}) when not is_nil(address_hash) do + query = + from( + wa in WatchlistAddress, + where: wa.address_hash_hash == ^address_hash, + where: wa.watchlist_id == ^watchlist_id, + select: %{label: wa.name, display_name: wa.name} + ) + + Repo.account_repo().all(query) + end + + def get_watchlist_names_on_address(_, _), do: [] +end diff --git a/apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex b/apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex new file mode 100644 index 0000000..2f3ef3f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/models/get_transaction_tags.ex @@ -0,0 +1,48 @@ +defmodule BlockScoutWeb.Models.GetTransactionTags do + @moduledoc """ + Get various types of tags associated with the transaction + """ + + import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2, get_tags_on_address: 1] + + alias Explorer.Account.TagTransaction + alias Explorer.Chain.Transaction + alias Explorer.Repo + + def get_transaction_with_addresses_tags( + %Transaction{} = transaction, + %{id: identity_id, watchlist_id: watchlist_id} + ) do + tx_tag = get_transaction_tags(transaction.hash, %{id: identity_id}) + addresses_tags = get_addresses_tags_for_transaction(transaction, %{id: identity_id, watchlist_id: watchlist_id}) + Map.put(addresses_tags, :personal_tx_tag, tx_tag) + end + + def get_transaction_with_addresses_tags(%Transaction{} = transaction, _), + do: %{ + common_tags: get_tags_on_address(transaction.to_address_hash), + personal_tags: [], + watchlist_names: [], + personal_tx_tag: nil + } + + def get_transaction_tags(transaction_hash, %{id: identity_id}) do + Repo.account_repo().get_by(TagTransaction, tx_hash_hash: transaction_hash, identity_id: identity_id) + end + + def get_transaction_tags(_, _), do: nil + + def get_addresses_tags_for_transaction( + %Transaction{} = transaction, + %{id: identity_id, watchlist_id: watchlist_id} + ) do + from_tags = get_address_tags(transaction.from_address_hash, %{id: identity_id, watchlist_id: watchlist_id}) + to_tags = get_address_tags(transaction.to_address_hash, %{id: identity_id, watchlist_id: watchlist_id}) + + %{ + common_tags: get_tags_on_address(transaction.to_address_hash), + personal_tags: Enum.dedup(from_tags.personal_tags ++ to_tags.personal_tags), + watchlist_names: Enum.dedup(from_tags.watchlist_names ++ to_tags.watchlist_names) + } + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/models/user_from_auth.ex b/apps/block_scout_web/lib/block_scout_web/models/user_from_auth.ex new file mode 100644 index 0000000..058b160 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/models/user_from_auth.ex @@ -0,0 +1,136 @@ +defmodule BlockScoutWeb.Models.UserFromAuth do + @moduledoc """ + Retrieve the user information from an auth request + """ + require Logger + require Poison + + alias Explorer.Account.Identity + alias Explorer.Repo + alias Ueberauth.Auth + + import Ecto.Query, only: [from: 2] + + def find_or_create(%Auth{} = auth, api_call? \\ false) do + case find_identity(auth) do + [] -> + case create_identity(auth) do + %Identity{} = identity -> + {:ok, return_value(identity, auth, api_call?)} + + {:error, changeset} -> + {:error, changeset} + end + + [%{} = identity | _] -> + update_identity(identity, update_identity_map(auth)) + {:ok, return_value(identity, auth, api_call?)} + end + end + + defp return_value(identity, _auth, true) do + identity + end + + defp return_value(identity, auth, false) do + basic_info(auth, identity) + end + + defp create_identity(auth) do + with {:ok, %Identity{} = identity} <- Repo.account_repo().insert(new_identity(auth)), + {:ok, _watchlist} <- add_watchlist(identity) do + identity + end + end + + defp update_identity(identity, attrs) do + identity + |> Identity.changeset(attrs) + |> Repo.account_repo().update() + end + + defp new_identity(auth) do + %Identity{ + uid: auth.uid, + uid_hash: auth.uid, + email: email_from_auth(auth), + name: name_from_auth(auth), + nickname: nickname_from_auth(auth), + avatar: avatar_from_auth(auth) + } + end + + defp add_watchlist(identity) do + watchlist = Ecto.build_assoc(identity, :watchlists, %{}) + + with {:ok, _} <- Repo.account_repo().insert(watchlist), + do: {:ok, identity} + end + + def find_identity(auth_or_uid) do + Repo.account_repo().all(query_identity(auth_or_uid)) + end + + def query_identity(%Auth{} = auth) do + from(i in Identity, where: i.uid_hash == ^auth.uid) + end + + def query_identity(id) do + from(i in Identity, where: i.id == ^id) + end + + defp basic_info(auth, identity) do + %{watchlists: [watchlist | _]} = Repo.account_repo().preload(identity, :watchlists) + + %{ + id: identity.id, + uid: auth.uid, + email: email_from_auth(auth), + name: name_from_auth(auth), + nickname: nickname_from_auth(auth), + avatar: avatar_from_auth(auth), + watchlist_id: watchlist.id + } + end + + defp update_identity_map(auth) do + %{ + email: email_from_auth(auth), + name: name_from_auth(auth), + nickname: nickname_from_auth(auth), + avatar: avatar_from_auth(auth) + } + end + + # github does it this way + defp avatar_from_auth(%{info: %{urls: %{avatar_url: image}}}), do: image + + # facebook does it this way + defp avatar_from_auth(%{info: %{image: image}}), do: image + + # default case if nothing matches + defp avatar_from_auth(auth) do + Logger.warn(auth.provider <> " needs to find an avatar URL!") + Logger.debug(Poison.encode!(auth)) + nil + end + + defp email_from_auth(%{info: %{email: email}}), do: email + + defp nickname_from_auth(%{info: %{nickname: nickname}}), do: nickname + + defp name_from_auth(%{info: %{name: name}}) + when name != "" and not is_nil(name), + do: name + + defp name_from_auth(%{info: info}) do + [info.first_name, info.last_name, info.nickname] + |> Enum.map(&(&1 |> to_string() |> String.trim())) + |> case do + ["", "", nick] -> nick + ["", lastname, _] -> lastname + [name, "", _] -> name + [name, lastname, _] -> name <> " " <> lastname + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex new file mode 100644 index 0000000..9e0264e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -0,0 +1,401 @@ +defmodule BlockScoutWeb.Notifier do + @moduledoc """ + Responds to events by sending appropriate channel updates to front-end. + """ + + alias Absinthe.Subscription + + alias BlockScoutWeb.{ + AddressContractVerificationViaFlattenedCodeView, + AddressContractVerificationViaJsonView, + AddressContractVerificationViaMultiPartFilesView, + AddressContractVerificationViaStandardJsonInputView, + AddressContractVerificationVyperView, + Endpoint + } + + alias Explorer.{Chain, Market, Repo} + alias Explorer.Chain.{Address, InternalTransaction, TokenTransfer, Transaction} + alias Explorer.Chain.Supply.RSK + alias Explorer.Chain.Transaction.History.TransactionStats + alias Explorer.Counters.{AverageBlockTime, Helper} + alias Explorer.ExchangeRates.Token + alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler} + alias Phoenix.View + + @check_broadcast_sequence_period 500 + + def handle_event({:chain_event, :addresses, type, addresses}) when type in [:realtime, :on_demand] do + Endpoint.broadcast("addresses:new_address", "count", %{count: Chain.address_estimated_count()}) + + addresses + |> Stream.reject(fn %Address{fetched_coin_balance: fetched_coin_balance} -> is_nil(fetched_coin_balance) end) + |> Enum.each(&broadcast_balance/1) + end + + def handle_event({:chain_event, :address_coin_balances, type, address_coin_balances}) + when type in [:realtime, :on_demand] do + Enum.each(address_coin_balances, &broadcast_address_coin_balance/1) + end + + def handle_event({:chain_event, :address_token_balances, type, address_token_balances}) + when type in [:realtime, :on_demand] do + Enum.each(address_token_balances, &broadcast_address_token_balance/1) + end + + def handle_event({:chain_event, :address_current_token_balances, type, address_current_token_balances}) + when type in [:realtime, :on_demand] do + Enum.each(address_current_token_balances, &broadcast_address_token_balance/1) + end + + def handle_event( + {:chain_event, :contract_verification_result, :on_demand, {address_hash, contract_verification_result, conn}} + ) do + %{view: view, compiler: compiler} = select_contract_type_and_form_view(conn.params) + + contract_verification_result = + case contract_verification_result do + {:ok, _} = result -> + result + + {:error, changeset} -> + compiler_versions = fetch_compiler_version(compiler) + + result = + view + |> View.render_to_string("new.html", + changeset: changeset, + compiler_versions: compiler_versions, + evm_versions: CodeCompiler.allowed_evm_versions(), + address_hash: address_hash, + conn: conn, + retrying: true + ) + + {:error, result} + end + + Endpoint.broadcast( + "addresses:#{address_hash}", + "verification_result", + %{ + result: contract_verification_result + } + ) + end + + def handle_event({:chain_event, :block_rewards, :realtime, rewards}) do + if Application.get_env(:block_scout_web, BlockScoutWeb.Chain)[:has_emission_funds] do + broadcast_rewards(rewards) + end + end + + def handle_event({:chain_event, :blocks, :realtime, blocks}) do + last_broadcasted_block_number = Helper.fetch_from_cache(:number, :last_broadcasted_block) + + blocks + |> Enum.sort_by(& &1.number, :asc) + |> Enum.each(fn block -> + broadcast_latest_block?(block, last_broadcasted_block_number) + end) + end + + def handle_event({:chain_event, :exchange_rate}) do + exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + + market_history_data = + case Market.fetch_recent_history() do + [today | the_rest] -> [%{today | closing_price: exchange_rate.usd_value} | the_rest] + data -> data + end + + exchange_rate_with_available_supply = + case Application.get_env(:explorer, :supply) do + RSK -> + %{exchange_rate | available_supply: nil, market_cap_usd: RSK.market_cap(exchange_rate)} + + _ -> + Map.from_struct(exchange_rate) + end + + Endpoint.broadcast("exchange_rate:new_rate", "new_rate", %{ + exchange_rate: exchange_rate_with_available_supply, + market_history_data: Enum.map(market_history_data, fn day -> Map.take(day, [:closing_price, :date]) end) + }) + end + + def handle_event({:chain_event, :internal_transactions, :realtime, internal_transactions}) do + internal_transactions + |> Stream.map( + &(InternalTransaction.where_nonpending_block() + |> Repo.get_by(transaction_hash: &1.transaction_hash, index: &1.index) + |> Repo.preload([:from_address, :to_address, transaction: :block])) + ) + |> Enum.each(&broadcast_internal_transaction/1) + end + + def handle_event({:chain_event, :token_transfers, :realtime, all_token_transfers}) do + transfers_by_token = Enum.group_by(all_token_transfers, fn tt -> to_string(tt.token_contract_address_hash) end) + + for {token_contract_address_hash, token_transfers} <- transfers_by_token do + Subscription.publish( + Endpoint, + token_transfers, + token_transfers: token_contract_address_hash + ) + + token_transfers_full = + token_transfers + |> Stream.map( + &(TokenTransfer + |> Repo.get_by( + block_hash: &1.block_hash, + transaction_hash: &1.transaction_hash, + token_contract_address_hash: &1.token_contract_address_hash, + log_index: &1.log_index + ) + |> Repo.preload([:from_address, :to_address, :token, transaction: :block])) + ) + + token_transfers_full + |> Enum.each(&broadcast_token_transfer/1) + end + end + + def handle_event({:chain_event, :transactions, :realtime, transactions}) do + transactions + |> Enum.map(& &1.hash) + |> Chain.hashes_to_transactions( + necessity_by_association: %{ + :block => :optional, + [created_contract_address: :names] => :optional, + [from_address: :names] => :optional, + [to_address: :names] => :optional + } + ) + |> Enum.map(fn tx -> + # Disable parsing of token transfers from websocket for transaction tab because we display token transfers at a separate tab + Map.put(tx, :token_transfers, []) + end) + |> Enum.each(&broadcast_transaction/1) + end + + def handle_event({:chain_event, :transaction_stats}) do + today = Date.utc_today() + + [{:history_size, history_size}] = + Application.get_env(:block_scout_web, BlockScoutWeb.Chain.TransactionHistoryChartController, {:history_size, 30}) + + x_days_back = Date.add(today, -1 * history_size) + + date_range = TransactionStats.by_date_range(x_days_back, today) + stats = Enum.map(date_range, fn item -> Map.drop(item, [:__meta__]) end) + + Endpoint.broadcast("transactions:stats", "update", %{stats: stats}) + end + + def handle_event(_), do: nil + + def fetch_compiler_version(compiler) do + case CompilerVersion.fetch_versions(compiler) do + {:ok, compiler_versions} -> + compiler_versions + + {:error, _} -> + [] + end + end + + def select_contract_type_and_form_view(params) do + verification_from_metadata_json? = check_verification_type(params, "json:metadata") + + verification_from_standard_json_input? = check_verification_type(params, "json:standard") + + verification_from_vyper? = check_verification_type(params, "vyper") + + verification_from_multi_part_files? = check_verification_type(params, "multi-part-files") + + compiler = if verification_from_vyper?, do: :vyper, else: :solc + + view = + cond do + verification_from_standard_json_input? -> AddressContractVerificationViaStandardJsonInputView + verification_from_metadata_json? -> AddressContractVerificationViaJsonView + verification_from_vyper? -> AddressContractVerificationVyperView + verification_from_multi_part_files? -> AddressContractVerificationViaMultiPartFilesView + true -> AddressContractVerificationViaFlattenedCodeView + end + + %{view: view, compiler: compiler} + end + + defp check_verification_type(params, type), + do: Map.has_key?(params, "verification_type") && Map.get(params, "verification_type") == type + + @doc """ + Broadcast the percentage of blocks indexed so far. + """ + def broadcast_blocks_indexed_ratio(ratio, finished?) do + Endpoint.broadcast("blocks:indexing", "index_status", %{ + ratio: Decimal.to_string(ratio), + finished: finished? + }) + end + + @doc """ + Broadcast the percentage of pending block operations indexed so far. + """ + def broadcast_internal_transactions_indexed_ratio(ratio, finished?) do + Endpoint.broadcast("blocks:indexing_internal_transactions", "index_status", %{ + ratio: Decimal.to_string(ratio), + finished: finished? + }) + end + + defp broadcast_latest_block?(block, last_broadcasted_block_number) do + cond do + last_broadcasted_block_number == 0 || last_broadcasted_block_number == block.number - 1 || + last_broadcasted_block_number < block.number - 4 -> + broadcast_block(block) + :ets.insert(:last_broadcasted_block, {:number, block.number}) + + last_broadcasted_block_number > block.number - 1 -> + broadcast_block(block) + + true -> + Task.start_link(fn -> + schedule_broadcasting(block) + end) + end + end + + defp schedule_broadcasting(block) do + :timer.sleep(@check_broadcast_sequence_period) + last_broadcasted_block_number = Helper.fetch_from_cache(:number, :last_broadcasted_block) + + if last_broadcasted_block_number == block.number - 1 do + broadcast_block(block) + :ets.insert(:last_broadcasted_block, {:number, block.number}) + else + schedule_broadcasting(block) + end + end + + defp broadcast_address_coin_balance(%{address_hash: address_hash, block_number: block_number}) do + Endpoint.broadcast("addresses:#{address_hash}", "coin_balance", %{ + block_number: block_number + }) + end + + defp broadcast_address_token_balance(%{address_hash: address_hash, block_number: block_number}) do + Endpoint.broadcast("addresses:#{address_hash}", "token_balance", %{ + block_number: block_number + }) + end + + defp broadcast_balance(%Address{hash: address_hash} = address) do + Endpoint.broadcast( + "addresses:#{address_hash}", + "balance_update", + %{ + address: address, + exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null() + } + ) + end + + defp broadcast_block(block) do + preloaded_block = Repo.preload(block, [[miner: :names], :transactions, :rewards]) + average_block_time = AverageBlockTime.average_block_time() + + Endpoint.broadcast("blocks:new_block", "new_block", %{ + block: preloaded_block, + average_block_time: average_block_time + }) + + Endpoint.broadcast("blocks:#{to_string(block.miner_hash)}", "new_block", %{ + block: preloaded_block, + average_block_time: average_block_time + }) + end + + defp broadcast_rewards(rewards) do + preloaded_rewards = Repo.preload(rewards, [:address, :block]) + emission_reward = Enum.find(preloaded_rewards, fn reward -> reward.address_type == :emission_funds end) + + preloaded_rewards_except_emission = + Enum.reject(preloaded_rewards, fn reward -> reward.address_type == :emission_funds end) + + Enum.each(preloaded_rewards_except_emission, fn reward -> + Endpoint.broadcast("rewards:#{to_string(reward.address_hash)}", "new_reward", %{ + emission_funds: emission_reward, + validator: reward + }) + end) + end + + defp broadcast_internal_transaction(internal_transaction) do + Endpoint.broadcast("addresses:#{internal_transaction.from_address_hash}", "internal_transaction", %{ + address: internal_transaction.from_address, + internal_transaction: internal_transaction + }) + + if internal_transaction.to_address_hash != internal_transaction.from_address_hash do + Endpoint.broadcast("addresses:#{internal_transaction.to_address_hash}", "internal_transaction", %{ + address: internal_transaction.to_address, + internal_transaction: internal_transaction + }) + end + end + + defp broadcast_transaction(%Transaction{block_number: nil} = pending) do + broadcast_transaction(pending, "transactions:new_pending_transaction", "pending_transaction") + end + + defp broadcast_transaction(transaction) do + broadcast_transaction(transaction, "transactions:new_transaction", "transaction") + end + + defp broadcast_transaction(transaction, transaction_channel, event) do + Endpoint.broadcast("transactions:#{transaction.hash}", "collated", %{}) + + Endpoint.broadcast(transaction_channel, event, %{ + transaction: transaction + }) + + Endpoint.broadcast("addresses:#{transaction.from_address_hash}", event, %{ + address: transaction.from_address, + transaction: transaction + }) + + if transaction.to_address_hash != transaction.from_address_hash do + Endpoint.broadcast("addresses:#{transaction.to_address_hash}", event, %{ + address: transaction.to_address, + transaction: transaction + }) + end + end + + defp broadcast_token_transfer(token_transfer) do + broadcast_token_transfer(token_transfer, "token_transfer") + end + + defp broadcast_token_transfer(token_transfer, event) do + Endpoint.broadcast("addresses:#{token_transfer.from_address_hash}", event, %{ + address: token_transfer.from_address, + token_transfer: token_transfer + }) + + Endpoint.broadcast("tokens:#{token_transfer.token_contract_address_hash}", event, %{ + address: token_transfer.token_contract_address_hash, + token_transfer: token_transfer + }) + + if token_transfer.to_address_hash != token_transfer.from_address_hash do + Endpoint.broadcast("addresses:#{token_transfer.to_address_hash}", event, %{ + address: token_transfer.to_address, + token_transfer: token_transfer + }) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/paging_helper.ex b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex new file mode 100644 index 0000000..3f44c6d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/paging_helper.ex @@ -0,0 +1,117 @@ +defmodule BlockScoutWeb.PagingHelper do + @moduledoc """ + Helper for fetching filters and other url query paramters + """ + import Explorer.Chain, only: [string_to_transaction_hash: 1] + alias Explorer.PagingOptions + + @page_size 50 + @default_paging_options %PagingOptions{page_size: @page_size + 1} + @allowed_filter_labels ["validated", "pending"] + @allowed_type_labels ["coin_transfer", "contract_call", "contract_creation", "token_transfer", "token_creation"] + + def paging_options(%{"block_number" => block_number_string, "index" => index_string}, [:validated | _]) do + with {block_number, ""} <- Integer.parse(block_number_string), + {index, ""} <- Integer.parse(index_string) do + [paging_options: %{@default_paging_options | key: {block_number, index}}] + else + _ -> + [paging_options: @default_paging_options] + end + end + + def paging_options(%{"inserted_at" => inserted_at_string, "hash" => hash_string}, [:pending | _]) do + with {:ok, inserted_at, _} <- DateTime.from_iso8601(inserted_at_string), + {:ok, hash} <- string_to_transaction_hash(hash_string) do + [paging_options: %{@default_paging_options | key: {inserted_at, hash}, is_pending_tx: true}] + else + _ -> + [paging_options: @default_paging_options] + end + end + + def paging_options(_params, _filter), do: [paging_options: @default_paging_options] + + def filter_options(%{"filter" => filter}) do + parse_filter(filter, @allowed_filter_labels) + end + + def filter_options(_params), do: [] + + def type_filter_options(%{"type" => type}) do + parse_filter(type, @allowed_type_labels) + end + + def type_filter_options(_params), do: [] + + def method_filter_options(%{"method" => method}) do + parse_method_filter(method) + end + + def method_filter_options(_params), do: [] + + def parse_filter("[" <> filter, allowed_labels) do + filter + |> String.trim_trailing("]") + |> parse_filter(allowed_labels) + end + + # sobelow_skip ["DOS.StringToAtom"] + def parse_filter(filter, allowed_labels) when is_binary(filter) do + filter + |> String.split(",") + |> Enum.filter(fn label -> Enum.member?(allowed_labels, label) end) + |> Enum.uniq() + |> Enum.map(&String.to_atom/1) + end + + def parse_method_filter("[" <> filter) do + filter + |> String.trim_trailing("]") + |> parse_method_filter() + end + + def parse_method_filter(filter) do + filter + |> String.split(",") + |> Enum.uniq() + end + + def select_block_type(%{"type" => type}) do + case String.downcase(type) do + "uncle" -> + [ + necessity_by_association: %{ + :transactions => :optional, + [miner: :names] => :optional, + :nephews => :required, + :rewards => :optional + }, + block_type: "Uncle" + ] + + "reorg" -> + [ + necessity_by_association: %{ + :transactions => :optional, + [miner: :names] => :optional, + :rewards => :optional + }, + block_type: "Reorg" + ] + + _ -> + select_block_type(nil) + end + end + + def select_block_type(_), + do: [ + necessity_by_association: %{ + :transactions => :optional, + [miner: :names] => :optional, + :rewards => :optional + }, + block_type: "Block" + ] +end diff --git a/apps/block_scout_web/lib/block_scout_web/plug/admin/check_owner_registered.ex b/apps/block_scout_web/lib/block_scout_web/plug/admin/check_owner_registered.ex new file mode 100644 index 0000000..b15fd16 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/plug/admin/check_owner_registered.ex @@ -0,0 +1,29 @@ +defmodule BlockScoutWeb.Plug.Admin.CheckOwnerRegistered do + @moduledoc """ + Checks that an admin owner has registered. + + If the admin owner, the user is redirected to a page + with instructions of how to continue setup. + """ + + import Phoenix.Controller, only: [redirect: 2] + import Plug.Conn + + alias BlockScoutWeb.AdminRouter.Helpers, as: AdminRoutes + alias Explorer.Admin + alias Plug.Conn + + def init(opts), do: opts + + def call(%Conn{} = conn, _opts) do + case Admin.owner() do + {:ok, _} -> + conn + + {:error, :not_found} -> + conn + |> redirect(to: AdminRoutes.setup_path(conn, :configure)) + |> halt() + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/plug/admin/require_admin_role.ex b/apps/block_scout_web/lib/block_scout_web/plug/admin/require_admin_role.ex new file mode 100644 index 0000000..bd11cd5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/plug/admin/require_admin_role.ex @@ -0,0 +1,26 @@ +defmodule BlockScoutWeb.Plug.Admin.RequireAdminRole do + @moduledoc """ + Authorization plug requiring a user to be authenticated and have an admin role. + """ + + import Plug.Conn + + import Phoenix.Controller, only: [redirect: 2] + + alias BlockScoutWeb.AdminRouter.Helpers, as: AdminRoutes + alias Explorer.Admin + + def init(opts), do: opts + + def call(conn, _) do + with user when not is_nil(user) <- conn.assigns[:user], + {:ok, admin} <- Admin.from_user(user) do + assign(conn, :admin, admin) + else + _ -> + conn + |> redirect(to: AdminRoutes.session_path(conn, :new)) + |> halt() + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/plug/allow_iframe.ex b/apps/block_scout_web/lib/block_scout_web/plug/allow_iframe.ex new file mode 100644 index 0000000..ee20311 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/plug/allow_iframe.ex @@ -0,0 +1,14 @@ +defmodule BlockScoutWeb.Plug.AllowIframe do + @moduledoc """ + Allows for iframes by deleting the + [`X-Frame-Options` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) + """ + + alias Plug.Conn + + def init(opts), do: opts + + def call(conn, _opts) do + Conn.delete_resp_header(conn, "x-frame-options") + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/plug/check_account_api.ex b/apps/block_scout_web/lib/block_scout_web/plug/check_account_api.ex new file mode 100644 index 0000000..80c5301 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/plug/check_account_api.ex @@ -0,0 +1,21 @@ +defmodule BlockScoutWeb.Plug.CheckAccountAPI do + @moduledoc """ + Checks if the Account functionality enabled for API level. + """ + import Plug.Conn + + alias Explorer.Account + + def init(opts), do: opts + + def call(conn, _opts) do + if Account.enabled?() do + conn + else + conn + |> put_resp_content_type("application/json") + |> send_resp(404, Jason.encode!(%{message: "Account functionality is disabled"})) + |> halt() + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/plug/check_account_web.ex b/apps/block_scout_web/lib/block_scout_web/plug/check_account_web.ex new file mode 100644 index 0000000..00a3af4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/plug/check_account_web.ex @@ -0,0 +1,31 @@ +defmodule BlockScoutWeb.Plug.CheckAccountWeb do + @moduledoc """ + Checks if the Account functionality enabled for web interface. + """ + import Phoenix.Controller + alias Phoenix.View + import Plug.Conn + + alias Explorer.Account + + def init(opts), do: opts + + def call(conn, _opts) do + if Account.enabled?() do + conn + else + inner_view = + View.render( + BlockScoutWeb.PageNotFoundView, + "index.html", + token: nil + ) + + conn + |> put_status(404) + |> put_view(BlockScoutWeb.LayoutView) + |> render(:app, inner_content: inner_view) + |> halt() + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/plug/check_api_v2.ex b/apps/block_scout_web/lib/block_scout_web/plug/check_api_v2.ex new file mode 100644 index 0000000..95269a2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/plug/check_api_v2.ex @@ -0,0 +1,21 @@ +defmodule BlockScoutWeb.Plug.CheckApiV2 do + @moduledoc """ + Checks if the API V2 enabled. + """ + import Plug.Conn + + alias BlockScoutWeb.API.V2, as: API_V2 + + def init(opts), do: opts + + def call(conn, _opts) do + if API_V2.enabled?() do + conn + else + conn + |> put_resp_content_type("application/json") + |> send_resp(404, Jason.encode!(%{message: "API V2 is disabled"})) + |> halt() + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/plug/fetch_user_from_session.ex b/apps/block_scout_web/lib/block_scout_web/plug/fetch_user_from_session.ex new file mode 100644 index 0000000..963beac --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/plug/fetch_user_from_session.ex @@ -0,0 +1,20 @@ +defmodule BlockScoutWeb.Plug.FetchUserFromSession do + @moduledoc """ + Fetches a `t:Explorer.Accounts.User.t/0` record if a user id is found in the session. + """ + + import Plug.Conn + + alias Explorer.Accounts + + def init(opts), do: opts + + def call(conn, _opts) do + with user_id when not is_nil(user_id) <- get_session(conn, :user_id), + {:ok, user} <- Accounts.fetch_user(user_id) do + assign(conn, :user, user) + else + _ -> conn + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/plug/graphql.ex b/apps/block_scout_web/lib/block_scout_web/plug/graphql.ex new file mode 100644 index 0000000..e46cbed --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/plug/graphql.ex @@ -0,0 +1,12 @@ +defmodule BlockScoutWeb.Plug.GraphQL do + @moduledoc """ + Default query for GraphiQL interface. + """ + @default_transaction_hash "0x69e3923eef50eada197c3336d546936d0c994211492c9f947a24c02827568f9f" + + def default_query do + transaction_hash = System.get_env("GRAPHIQL_TRANSACTION") || @default_transaction_hash + + "{transaction(hash: \"#{transaction_hash}\") { hash, blockNumber, value, gasUsed }}" + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/plug/redis_cookie.ex b/apps/block_scout_web/lib/block_scout_web/plug/redis_cookie.ex new file mode 100644 index 0000000..ff8d457 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/plug/redis_cookie.ex @@ -0,0 +1,223 @@ +defmodule BlockScoutWeb.Plug.RedisCookie do + @moduledoc """ + Extended version of Plug.Session.COOKIE from https://github.com/elixir-plug/plug/blob/main/lib/plug/session/cookie.ex + Added Redis to have a possibility to invalidate session + """ + + require Logger + @behaviour Plug.Session.Store + + alias Plug.Crypto + alias Plug.Crypto.{KeyGenerator, MessageEncryptor, MessageVerifier} + + @impl true + def init(opts) do + opts + |> build_opts() + |> build_rotating_opts(opts[:rotating_options]) + |> Map.delete(:secret_key_base) + end + + @impl true + def get(conn, raw_cookie, opts) do + opts = Map.put(opts, :secret_key_base, conn.secret_key_base) + + [opts | opts.rotating_options] + |> Enum.find_value(:error, &read_raw_cookie(raw_cookie, &1)) + |> decode(opts.serializer, opts.log) + |> check_in_redis(raw_cookie) + end + + @impl true + def put(conn, _sid, term, opts) do + %{serializer: serializer, key_opts: key_opts, signing_salt: signing_salt} = opts + binary = encode(term, serializer) + + opts + |> case do + %{encryption_salt: nil} -> + MessageVerifier.sign(binary, derive(conn.secret_key_base, signing_salt, key_opts)) + + %{encryption_salt: encryption_salt} -> + MessageEncryptor.encrypt( + binary, + derive(conn.secret_key_base, encryption_salt, key_opts), + derive(conn.secret_key_base, signing_salt, key_opts) + ) + end + |> store_to_redis() + end + + @impl true + def delete(_conn, sid, _opts) do + remove_from_redis(sid) + :ok + end + + defp encode(term, :external_term_format) do + :erlang.term_to_binary(term) + end + + defp encode(term, serializer) do + {:ok, binary} = serializer.encode(term) + binary + end + + defp decode({:ok, binary}, :external_term_format, log) do + {:term, + try do + Crypto.non_executable_binary_to_term(binary) + rescue + e -> + Logger.log( + log, + "Plug.Session could not decode incoming session cookie. Reason: " <> + Exception.message(e) + ) + + %{} + end} + end + + defp decode({:ok, binary}, serializer, _log) do + case serializer.decode(binary) do + {:ok, term} -> {:custom, term} + _ -> {:custom, %{}} + end + end + + defp decode(:error, _serializer, false) do + {nil, %{}} + end + + defp decode(:error, _serializer, log) do + Logger.log( + log, + "Plug.Session could not verify incoming session cookie. " <> + "This may happen when the session settings change or a stale cookie is sent." + ) + + {nil, %{}} + end + + defp prederive(secret_key_base, value, key_opts) + when is_binary(secret_key_base) and is_binary(value) do + {:prederived, derive(secret_key_base, value, Keyword.delete(key_opts, :cache))} + end + + defp prederive(_secret_key_base, value, _key_opts) do + value + end + + defp derive(_secret_key_base, {:prederived, value}, _key_opts) do + value + end + + defp derive(secret_key_base, {module, function, args}, key_opts) do + derive(secret_key_base, apply(module, function, args), key_opts) + end + + defp derive(secret_key_base, key, key_opts) do + secret_key_base + |> validate_secret_key_base() + |> KeyGenerator.generate(key, key_opts) + end + + defp validate_secret_key_base(nil), + do: raise(ArgumentError, "cookie store expects conn.secret_key_base to be set") + + defp validate_secret_key_base(secret_key_base) when byte_size(secret_key_base) < 64, + do: raise(ArgumentError, "cookie store expects conn.secret_key_base to be at least 64 bytes") + + defp validate_secret_key_base(secret_key_base), do: secret_key_base + + defp check_signing_salt(opts) do + case opts[:signing_salt] do + nil -> raise ArgumentError, "cookie store expects :signing_salt as option" + salt -> salt + end + end + + defp check_serializer(serializer) when is_atom(serializer), do: serializer + + defp check_serializer(_), + do: raise(ArgumentError, "cookie store expects :serializer option to be a module") + + defp read_raw_cookie(raw_cookie, opts) do + signing_salt = derive(opts.secret_key_base, opts.signing_salt, opts.key_opts) + + opts + |> case do + %{encryption_salt: nil} -> + MessageVerifier.verify(raw_cookie, signing_salt) + + %{encryption_salt: _} -> + encryption_salt = derive(opts.secret_key_base, opts.encryption_salt, opts.key_opts) + + MessageEncryptor.decrypt(raw_cookie, encryption_salt, signing_salt) + end + |> case do + :error -> nil + result -> result + end + end + + defp build_opts(opts) do + encryption_salt = opts[:encryption_salt] + signing_salt = check_signing_salt(opts) + + iterations = Keyword.get(opts, :key_iterations, 1000) + length = Keyword.get(opts, :key_length, 32) + digest = Keyword.get(opts, :key_digest, :sha256) + log = Keyword.get(opts, :log, :debug) + secret_key_base = Keyword.get(opts, :secret_key_base) + key_opts = [iterations: iterations, length: length, digest: digest, cache: Plug.Keys] + + serializer = check_serializer(opts[:serializer] || :external_term_format) + + %{ + secret_key_base: secret_key_base, + encryption_salt: prederive(secret_key_base, encryption_salt, key_opts), + signing_salt: prederive(secret_key_base, signing_salt, key_opts), + key_opts: key_opts, + serializer: serializer, + log: log + } + end + + defp build_rotating_opts(opts, rotating_opts) when is_list(rotating_opts) do + Map.put(opts, :rotating_options, Enum.map(rotating_opts, &build_opts/1)) + end + + defp build_rotating_opts(opts, _), do: Map.put(opts, :rotating_options, []) + + defp store_to_redis(cookie) do + Redix.command(:redix, ["SET", hash(cookie), 1]) + + cookie + end + + defp remove_from_redis(sid) do + Redix.command(:redix, ["DEL", sid]) + end + + defp check_in_redis({sid, map}, _cookie) when is_nil(sid) or map == %{}, do: {nil, %{}} + + defp check_in_redis({_sid, session}, cookie) do + hash = hash(cookie) + + case Redix.command(:redix, ["GET", hash]) do + {:ok, one} when one in [1, "1"] -> + {hash, session} + + _ -> + {nil, %{}} + end + end + + defp hash(cookie) do + :sha256 + |> :crypto.hash(cookie) + |> Base.encode16() + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/prometheus/exporter.ex b/apps/block_scout_web/lib/block_scout_web/prometheus/exporter.ex new file mode 100644 index 0000000..c5febc5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/prometheus/exporter.ex @@ -0,0 +1,9 @@ +defmodule BlockScoutWeb.Prometheus.Exporter do + @moduledoc """ + Exports `Prometheus` metrics at `/metrics` + """ + + @dialyzer :no_match + + use Prometheus.PlugExporter +end diff --git a/apps/block_scout_web/lib/block_scout_web/prometheus/instrumenter.ex b/apps/block_scout_web/lib/block_scout_web/prometheus/instrumenter.ex new file mode 100644 index 0000000..a782d43 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/prometheus/instrumenter.ex @@ -0,0 +1,16 @@ +defmodule BlockScoutWeb.Prometheus.Instrumenter do + @moduledoc """ + Phoenix request metrics for `Prometheus`. + """ + + @dialyzer {:no_match, + [ + phoenix_channel_join: 3, + phoenix_channel_receive: 3, + phoenix_controller_call: 3, + phoenix_controller_render: 3, + setup: 0 + ]} + + use Prometheus.PhoenixInstrumenter +end diff --git a/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex new file mode 100644 index 0000000..148bbbc --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/realtime_event_handler.ex @@ -0,0 +1,41 @@ +defmodule BlockScoutWeb.RealtimeEventHandler do + @moduledoc """ + Subscribing process for broadcast events from realtime. + """ + + use GenServer + + alias BlockScoutWeb.Notifier + alias Explorer.Chain.Events.Subscriber + alias Explorer.Counters.Helper + + def start_link(_) do + GenServer.start_link(__MODULE__, [], name: __MODULE__) + end + + @impl true + def init([]) do + Helper.create_cache_table(:last_broadcasted_block) + Subscriber.to(:address_coin_balances, :realtime) + Subscriber.to(:addresses, :realtime) + Subscriber.to(:block_rewards, :realtime) + Subscriber.to(:blocks, :realtime) + Subscriber.to(:internal_transactions, :realtime) + Subscriber.to(:token_transfers, :realtime) + Subscriber.to(:transactions, :realtime) + Subscriber.to(:addresses, :on_demand) + Subscriber.to(:address_coin_balances, :on_demand) + Subscriber.to(:address_token_balances, :on_demand) + Subscriber.to(:contract_verification_result, :on_demand) + # Does not come from the indexer + Subscriber.to(:exchange_rate) + Subscriber.to(:transaction_stats) + {:ok, []} + end + + @impl true + def handle_info(event, state) do + Notifier.handle_event(event) + {:noreply, state} + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/resolvers/address.ex b/apps/block_scout_web/lib/block_scout_web/resolvers/address.ex new file mode 100644 index 0000000..dda5aaf --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/resolvers/address.ex @@ -0,0 +1,19 @@ +defmodule BlockScoutWeb.Resolvers.Address do + @moduledoc false + + alias Explorer.Chain + + def get_by(_, %{hashes: hashes}, _) do + case Chain.hashes_to_addresses(hashes) do + [] -> {:error, "Addresses not found."} + result -> {:ok, result} + end + end + + def get_by(_, %{hash: hash}, _) do + case Chain.hash_to_address(hash) do + {:error, :not_found} -> {:error, "Address not found."} + {:ok, _} = result -> result + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/resolvers/block.ex b/apps/block_scout_web/lib/block_scout_web/resolvers/block.ex new file mode 100644 index 0000000..a970f7c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/resolvers/block.ex @@ -0,0 +1,12 @@ +defmodule BlockScoutWeb.Resolvers.Block do + @moduledoc false + + alias Explorer.Chain + + def get_by(_, %{number: number}, _) do + case Chain.number_to_block(number) do + {:ok, _} = result -> result + {:error, :not_found} -> {:error, "Block number #{number} was not found."} + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/resolvers/internal_transaction.ex b/apps/block_scout_web/lib/block_scout_web/resolvers/internal_transaction.ex new file mode 100644 index 0000000..08f3ca4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/resolvers/internal_transaction.ex @@ -0,0 +1,23 @@ +defmodule BlockScoutWeb.Resolvers.InternalTransaction do + @moduledoc false + + alias Absinthe.Relay.Connection + alias Explorer.Chain.Transaction + alias Explorer.{GraphQL, Repo} + + def get_by(%{transaction_hash: _, index: _} = args) do + GraphQL.get_internal_transaction(args) + end + + def get_by(%Transaction{} = transaction, args, _) do + transaction + |> GraphQL.transaction_to_internal_transactions_query() + |> Connection.from_query(&Repo.all/1, args, options(args)) + end + + defp options(%{before: _}), do: [] + + defp options(%{count: count}), do: [count: count] + + defp options(_), do: [] +end diff --git a/apps/block_scout_web/lib/block_scout_web/resolvers/token_transfer.ex b/apps/block_scout_web/lib/block_scout_web/resolvers/token_transfer.ex new file mode 100644 index 0000000..38b8b3f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/resolvers/token_transfer.ex @@ -0,0 +1,24 @@ +defmodule BlockScoutWeb.Resolvers.TokenTransfer do + @moduledoc false + + alias Absinthe.Relay.Connection + alias Explorer.{GraphQL, Repo} + + def get_by(%{transaction_hash: _, log_index: _} = args) do + GraphQL.get_token_transfer(args) + end + + def get_by(_, %{token_contract_address_hash: token_contract_address_hash} = args, _) do + connection_args = Map.take(args, [:after, :before, :first, :last]) + + token_contract_address_hash + |> GraphQL.list_token_transfers_query() + |> Connection.from_query(&Repo.all/1, connection_args, options(args)) + end + + defp options(%{before: _}), do: [] + + defp options(%{count: count}), do: [count: count] + + defp options(_), do: [] +end diff --git a/apps/block_scout_web/lib/block_scout_web/resolvers/transaction.ex b/apps/block_scout_web/lib/block_scout_web/resolvers/transaction.ex new file mode 100644 index 0000000..adfe1b7 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/resolvers/transaction.ex @@ -0,0 +1,28 @@ +defmodule BlockScoutWeb.Resolvers.Transaction do + @moduledoc false + + alias Absinthe.Relay.Connection + alias Explorer.{Chain, GraphQL, Repo} + alias Explorer.Chain.Address + + def get_by(_, %{hash: hash}, _) do + case Chain.hash_to_transaction(hash) do + {:ok, transaction} -> {:ok, transaction} + {:error, :not_found} -> {:error, "Transaction not found."} + end + end + + def get_by(%Address{hash: address_hash}, args, _) do + connection_args = Map.take(args, [:after, :before, :first, :last]) + + address_hash + |> GraphQL.address_to_transactions_query(args.order) + |> Connection.from_query(&Repo.all/1, connection_args, options(args)) + end + + defp options(%{before: _}), do: [] + + defp options(%{count: count}), do: [count: count] + + defp options(_), do: [] +end diff --git a/apps/block_scout_web/lib/block_scout_web/router.ex b/apps/block_scout_web/lib/block_scout_web/router.ex new file mode 100644 index 0000000..543dc4a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/router.ex @@ -0,0 +1,89 @@ +defmodule BlockScoutWeb.Router do + use BlockScoutWeb, :router + + alias BlockScoutWeb.Plug.GraphQL + alias BlockScoutWeb.{ApiRouter, WebRouter} + + if Application.compile_env(:block_scout_web, ApiRouter)[:wobserver_enabled] do + forward("/wobserver", Wobserver.Web.Router) + end + + if Application.compile_env(:block_scout_web, :admin_panel_enabled) do + forward("/admin", BlockScoutWeb.AdminRouter) + end + + pipeline :browser do + plug(:accepts, ["html"]) + plug(:fetch_session) + plug(:fetch_flash) + plug(:protect_from_forgery) + plug(BlockScoutWeb.CSPHeader) + end + + pipeline :api do + plug(:accepts, ["json"]) + end + + forward("/api", ApiRouter) + + if Application.compile_env(:block_scout_web, ApiRouter)[:reading_enabled] do + # Needs to be 200 to support the schema introspection for graphiql + @max_complexity 200 + + forward("/graphql", Absinthe.Plug, + schema: BlockScoutWeb.Schema, + analyze_complexity: true, + max_complexity: @max_complexity + ) + + forward("/graphiql", Absinthe.Plug.GraphiQL, + schema: BlockScoutWeb.Schema, + interface: :advanced, + default_query: GraphQL.default_query(), + socket: BlockScoutWeb.UserSocket, + analyze_complexity: true, + max_complexity: @max_complexity + ) + else + scope "/", BlockScoutWeb do + pipe_through(:browser) + get("/api-docs", PageNotFoundController, :index) + get("/eth-rpc-api-docs", PageNotFoundController, :index) + end + end + + scope "/", BlockScoutWeb do + pipe_through(:browser) + + get("/api-docs", APIDocsController, :index) + get("/eth-rpc-api-docs", APIDocsController, :eth_rpc) + end + + url_params = Application.compile_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url] + api_path = url_params[:api_path] + path = url_params[:path] + + if path != api_path do + scope to_string(api_path) <> "verify_smart_contract" do + pipe_through(:api) + + post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create) + end + else + scope "/verify_smart_contract" do + pipe_through(:api) + + post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create) + end + end + + if Application.compile_env(:block_scout_web, WebRouter)[:enabled] do + forward("/", BlockScoutWeb.WebRouter) + else + scope "/", BlockScoutWeb do + pipe_through(:browser) + + forward("/", APIDocsController, :index) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/schema.ex b/apps/block_scout_web/lib/block_scout_web/schema.ex new file mode 100644 index 0000000..42d644c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/schema.ex @@ -0,0 +1,124 @@ +defmodule BlockScoutWeb.Schema do + @moduledoc false + + use Absinthe.Schema + use Absinthe.Relay.Schema, :modern + + alias Absinthe.Middleware.Dataloader, as: AbsintheMiddlewareDataloader + alias Absinthe.Plugin, as: AbsinthePlugin + + alias BlockScoutWeb.Resolvers.{ + Address, + Block, + InternalTransaction, + TokenTransfer, + Transaction + } + + alias Explorer.Chain + alias Explorer.Chain.InternalTransaction, as: ExplorerChainInternalTransaction + alias Explorer.Chain.TokenTransfer, as: ExplorerChainTokenTransfer + alias Explorer.Chain.Transaction, as: ExplorerChainTransaction + + import_types(BlockScoutWeb.Schema.Types) + + node interface do + resolve_type(fn + %ExplorerChainInternalTransaction{}, _ -> + :internal_transaction + + %ExplorerChainTokenTransfer{}, _ -> + :token_transfer + + %ExplorerChainTransaction{}, _ -> + :transaction + + _, _ -> + nil + end) + end + + query do + node field do + resolve(fn + %{type: :internal_transaction, id: id}, _ -> + %{"transaction_hash" => transaction_hash_string, "index" => index} = Jason.decode!(id) + {:ok, transaction_hash} = Chain.string_to_transaction_hash(transaction_hash_string) + InternalTransaction.get_by(%{transaction_hash: transaction_hash, index: index}) + + %{type: :token_transfer, id: id}, _ -> + %{"transaction_hash" => transaction_hash_string, "log_index" => log_index} = Jason.decode!(id) + {:ok, transaction_hash} = Chain.string_to_transaction_hash(transaction_hash_string) + TokenTransfer.get_by(%{transaction_hash: transaction_hash, log_index: log_index}) + + %{type: :transaction, id: transaction_hash_string}, _ -> + {:ok, hash} = Chain.string_to_transaction_hash(transaction_hash_string) + Transaction.get_by(%{}, %{hash: hash}, %{}) + + _, _ -> + {:error, "Unknown node"} + end) + end + + @desc "Gets an address by hash." + field :address, :address do + arg(:hash, non_null(:address_hash)) + resolve(&Address.get_by/3) + end + + @desc "Gets addresses by address hash." + field :addresses, list_of(:address) do + arg(:hashes, non_null(list_of(non_null(:address_hash)))) + resolve(&Address.get_by/3) + complexity(fn %{hashes: hashes}, child_complexity -> length(hashes) * child_complexity end) + end + + @desc "Gets a block by number." + field :block, :block do + arg(:number, non_null(:integer)) + resolve(&Block.get_by/3) + end + + @desc "Gets token transfers by token contract address hash." + connection field(:token_transfers, node_type: :token_transfer) do + arg(:token_contract_address_hash, non_null(:address_hash)) + arg(:count, :integer) + + resolve(&TokenTransfer.get_by/3) + + complexity(fn + %{first: first}, child_complexity -> + first * child_complexity + + %{last: last}, child_complexity -> + last * child_complexity + end) + end + + @desc "Gets a transaction by hash." + field :transaction, :transaction do + arg(:hash, non_null(:full_hash)) + resolve(&Transaction.get_by/3) + end + end + + subscription do + field :token_transfers, list_of(:token_transfer) do + arg(:token_contract_address_hash, non_null(:address_hash)) + + config(fn args, _info -> + {:ok, topic: to_string(args.token_contract_address_hash)} + end) + end + end + + def context(context) do + loader = Dataloader.add_source(Dataloader.new(), :db, Chain.data()) + + Map.put(context, :loader, loader) + end + + def plugins do + [AbsintheMiddlewareDataloader] ++ AbsinthePlugin.defaults() + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/schema/scalars.ex b/apps/block_scout_web/lib/block_scout_web/schema/scalars.ex new file mode 100644 index 0000000..51ebae0 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/schema/scalars.ex @@ -0,0 +1,121 @@ +defmodule BlockScoutWeb.Schema.Scalars do + @moduledoc false + + use Absinthe.Schema.Notation + + alias Explorer.Chain.{Data, Hash, Wei} + alias Explorer.Chain.Hash.{Address, Full, Nonce} + + import_types(BlockScoutWeb.Schema.Scalars.JSON) + + @desc """ + The address (40 (hex) characters / 160 bits / 20 bytes) is derived from the public key (128 (hex) characters / + 512 bits / 64 bytes) which is derived from the private key (64 (hex) characters / 256 bits / 32 bytes). + + The address is actually the last 40 characters of the keccak-256 hash of the public key with `0x` appended. + """ + scalar :address_hash do + parse(fn + %Absinthe.Blueprint.Input.String{value: value} -> + Hash.cast(Address, value) + + _ -> + :error + end) + + serialize(&to_string/1) + end + + @desc """ + An unpadded hexadecimal number with 0 or more digits. Each pair of digits + maps directly to a byte in the underlying binary representation. When + interpreted as a number, it should be treated as big-endian. + """ + scalar :data do + parse(fn + %Absinthe.Blueprint.Input.String{value: value} -> + Data.cast(value) + + _ -> + :error + end) + + serialize(&to_string/1) + end + + @desc """ + A 32-byte [KECCAK-256](https://en.wikipedia.org/wiki/SHA-3) hash. + """ + scalar :full_hash do + parse(fn + %Absinthe.Blueprint.Input.String{value: value} -> + Hash.cast(Full, value) + + _ -> + :error + end) + + serialize(&to_string/1) + end + + @desc """ + The nonce (16 (hex) characters / 128 bits / 8 bytes) is derived from the Proof-of-Work. + """ + scalar :nonce_hash do + parse(fn + %Absinthe.Blueprint.Input.String{value: value} -> + Hash.cast(Nonce, value) + + _ -> + :error + end) + + serialize(&to_string/1) + end + + @desc """ + The smallest fractional unit of Ether. Using wei instead of ether allows code to do integer match instead of using + floats. + + See [Ethereum Homestead Documentation](http://ethdocs.org/en/latest/ether.html) for examples of various denominations of wei. + + Etymology of "wei" comes from [Wei Dai (戴維)](https://en.wikipedia.org/wiki/Wei_Dai), a + [cypherpunk](https://en.wikipedia.org/wiki/Cypherpunk) who came up with b-money, which outlined modern + cryptocurrencies. + """ + scalar :wei do + parse(fn + %Absinthe.Blueprint.Input.String{value: value} -> + Wei.cast(value) + + _ -> + :error + end) + + serialize(&to_string(&1.value)) + end + + enum :status do + value(:ok) + value(:error) + end + + enum :call_type do + value(:call) + value(:callcode) + value(:delegatecall) + value(:staticcall) + end + + enum :type do + value(:call) + value(:create) + value(:reward) + value(:selfdestruct) + end + + enum :sort_order do + value(:asc) + value(:desc) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/schema/scalars/JSON.ex b/apps/block_scout_web/lib/block_scout_web/schema/scalars/JSON.ex new file mode 100644 index 0000000..8b4ef28 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/schema/scalars/JSON.ex @@ -0,0 +1,33 @@ +defmodule BlockScoutWeb.Schema.Scalars.JSON do + @moduledoc """ + The JSON scalar type allows arbitrary JSON values to be passed in and out. + """ + use Absinthe.Schema.Notation + + @desc """ + The `JSON` scalar type represents arbitrary JSON string data, represented as UTF-8 + character sequences. The JSON type is most often used to represent a free-form + human-readable JSON string. + """ + scalar :json do + parse(&decode/1) + serialize(&encode/1) + end + + defp decode(%Absinthe.Blueprint.Input.String{value: value}) do + case Jason.decode(value) do + {:ok, result} -> {:ok, result} + _ -> :error + end + end + + defp decode(%Absinthe.Blueprint.Input.Null{}) do + {:ok, nil} + end + + defp decode(_) do + :error + end + + defp encode(value), do: Jason.encode!(value) +end diff --git a/apps/block_scout_web/lib/block_scout_web/schema/types.ex b/apps/block_scout_web/lib/block_scout_web/schema/types.ex new file mode 100644 index 0000000..bc77abf --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/schema/types.ex @@ -0,0 +1,176 @@ +defmodule BlockScoutWeb.Schema.Types do + @moduledoc false + + use Absinthe.Schema.Notation + use Absinthe.Relay.Schema.Notation, :modern + + import Absinthe.Resolution.Helpers + + alias BlockScoutWeb.Resolvers.{ + InternalTransaction, + Transaction + } + + import_types(Absinthe.Type.Custom) + import_types(BlockScoutWeb.Schema.Scalars) + + connection(node_type: :transaction) + connection(node_type: :internal_transaction) + connection(node_type: :token_transfer) + + @desc """ + A stored representation of a Web3 address. + """ + object :address do + field(:hash, :address_hash) + field(:fetched_coin_balance, :wei) + field(:fetched_coin_balance_block_number, :integer) + field(:contract_code, :data) + + field :smart_contract, :smart_contract do + resolve(dataloader(:db, :smart_contract)) + end + + connection field(:transactions, node_type: :transaction) do + arg(:count, :integer) + arg(:order, type: :sort_order, default_value: :desc) + resolve(&Transaction.get_by/3) + + complexity(fn + %{first: first}, child_complexity -> + first * child_complexity + + %{last: last}, child_complexity -> + last * child_complexity + + %{}, _child_complexity -> + 0 + end) + end + end + + @desc """ + A package of data that contains zero or more transactions, the hash of the previous block ("parent"), and optionally + other data. Because each block (except for the initial "genesis block") points to the previous block, the data + structure that they form is called a "blockchain". + """ + object :block do + field(:hash, :full_hash) + field(:consensus, :boolean) + field(:difficulty, :decimal) + field(:gas_limit, :decimal) + field(:gas_used, :decimal) + field(:nonce, :nonce_hash) + field(:number, :integer) + field(:size, :integer) + field(:timestamp, :datetime) + field(:total_difficulty, :decimal) + field(:miner_hash, :address_hash) + field(:parent_hash, :full_hash) + end + + @desc """ + Models internal transactions. + """ + node object(:internal_transaction, id_fetcher: &internal_transaction_id_fetcher/2) do + field(:call_type, :call_type) + field(:created_contract_code, :data) + field(:error, :string) + field(:gas, :decimal) + field(:gas_used, :decimal) + field(:index, :integer) + field(:init, :data) + field(:input, :data) + field(:output, :data) + field(:trace_address, :json) + field(:type, :type) + field(:value, :wei) + field(:block_number, :integer) + field(:transaction_index, :integer) + field(:created_contract_address_hash, :address_hash) + field(:from_address_hash, :address_hash) + field(:to_address_hash, :address_hash) + field(:transaction_hash, :full_hash) + end + + @desc """ + The representation of a verified Smart Contract. + + "A contract in the sense of Solidity is a collection of code (its functions) + and data (its state) that resides at a specific address on the Ethereum + blockchain." + http://solidity.readthedocs.io/en/v0.4.24/introduction-to-smart-contracts.html + """ + object :smart_contract do + field(:name, :string) + field(:compiler_version, :string) + field(:optimization, :boolean) + field(:contract_source_code, :string) + field(:abi, :json) + field(:address_hash, :address_hash) + end + + @desc """ + Represents a token transfer between addresses. + """ + node object(:token_transfer, id_fetcher: &token_transfer_id_fetcher/2) do + field(:amount, :decimal) + field(:block_number, :integer) + field(:log_index, :integer) + field(:token_id, :decimal) + field(:from_address_hash, :address_hash) + field(:to_address_hash, :address_hash) + field(:token_contract_address_hash, :address_hash) + field(:transaction_hash, :full_hash) + end + + @desc """ + Models a Web3 transaction. + """ + node object(:transaction, id_fetcher: &transaction_id_fetcher/2) do + field(:hash, :full_hash) + field(:block_number, :integer) + field(:cumulative_gas_used, :decimal) + field(:error, :string) + field(:gas, :decimal) + field(:gas_price, :wei) + field(:gas_used, :decimal) + field(:index, :integer) + field(:input, :string) + field(:nonce, :nonce_hash) + field(:r, :decimal) + field(:s, :decimal) + field(:status, :status) + field(:v, :decimal) + field(:value, :wei) + field(:from_address_hash, :address_hash) + field(:to_address_hash, :address_hash) + field(:created_contract_address_hash, :address_hash) + + connection field(:internal_transactions, node_type: :internal_transaction) do + arg(:count, :integer) + resolve(&InternalTransaction.get_by/3) + + complexity(fn + %{first: first}, child_complexity -> + first * child_complexity + + %{last: last}, child_complexity -> + last * child_complexity + + %{}, _child_complexity -> + 0 + end) + end + end + + def token_transfer_id_fetcher(%{transaction_hash: transaction_hash, log_index: log_index}, _) do + Jason.encode!(%{transaction_hash: to_string(transaction_hash), log_index: log_index}) + end + + def transaction_id_fetcher(%{hash: hash}, _), do: to_string(hash) + + def internal_transaction_id_fetcher(%{transaction_hash: transaction_hash, index: index}, _) do + Jason.encode!(%{transaction_hash: to_string(transaction_hash), index: index}) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/social_media.ex b/apps/block_scout_web/lib/block_scout_web/social_media.ex new file mode 100644 index 0000000..7c13c7a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/social_media.ex @@ -0,0 +1,25 @@ +defmodule BlockScoutWeb.SocialMedia do + @moduledoc """ + This module provides social media links + """ + + @services %{ + facebook: "https://www.facebook.com/", + instagram: "https://www.instagram.com/", + twitter: "https://www.twitter.com/", + telegram: "https://t.me/" + } + + def links do + :block_scout_web + |> Application.get_env(__MODULE__, []) + |> Enum.reverse() + |> filter_and_build_links() + end + + defp filter_and_build_links(configured_services) do + for {name, account} <- configured_services, Map.has_key?(@services, name) do + {name, @services[name] <> account} + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/api_key/form.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/api_key/form.html.eex new file mode 100644 index 0000000..a425a07 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/api_key/form.html.eex @@ -0,0 +1,34 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :api_keys %> +
    +
    +
    +

    <%=if @method == :update, do: gettext("Update"), else: gettext("Add") %> <%= gettext "API key"%>

    + +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/api_key/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/api_key/index.html.eex new file mode 100644 index 0000000..72ebbe8 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/api_key/index.html.eex @@ -0,0 +1,51 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :api_keys %> +
    +
    +
    +

    <%= gettext "API keys" %>

    +
    + <%= if Enum.count(@api_keys) < Key.get_max_api_keys_count() do %> +
    +
    + <%= gettext "Create an API key to use with your RPC и EthRPC API requests." %> <%= gettext "Learn more" %> +
    +
    + <% else %> +
    +
    + <%= gettext "You can create 3 API keys per account." %> <%= gettext "Learn more" %> +
    +
    + <% end %> +
    +
    + <%= if @api_keys != [] do %> + + + + + + + + + + + <%= Enum.map(@api_keys, fn key -> + render("row.html", api_key: key, conn: @conn) + end) %> + +
    <%= gettext "Name" %><%= gettext "API key" %>
    + <% end %> +
    +
    + <%= if Enum.count(@api_keys) < Key.get_max_api_keys_count() do %> + <%= gettext "Add API key" %> + <% end %> +
    +
    +
    +
    + +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/api_key/row.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/api_key/row.html.eex new file mode 100644 index 0000000..1d83433 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/api_key/row.html.eex @@ -0,0 +1,18 @@ + + <%= @api_key.name %> + + <%= @api_key.value %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], clipboard_text: @api_key.value, aria_label: gettext("Copy API key"), title: gettext("Copy API key"), style: "display: inline-block; vertical-align: text-bottom; position: initial; margin-top: 1px;" %> + + +
    + + +
    + <%= gettext("Remove") %> + + + <%= link gettext("Edit"), to: api_key_path(@conn, :edit, @api_key.value) %> + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/auth/profile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/auth/profile.html.eex new file mode 100644 index 0000000..618c877 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/auth/profile.html.eex @@ -0,0 +1,36 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :profile %> +
    +
    +
    +

    Profile

    +
    +
    + <%= @user.nickname %> +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/common/_nav.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/common/_nav.html.eex new file mode 100644 index 0000000..cf28ad5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/common/_nav.html.eex @@ -0,0 +1,25 @@ + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/form.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/form.html.eex new file mode 100644 index 0000000..c54477c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/form.html.eex @@ -0,0 +1,39 @@ +<% abi = format_abi(@custom_abi) %> +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :custom_abis %> +
    +
    +
    +

    <%=if @method == :update, do: gettext("Update"), else: gettext("Add") %> <%= gettext "Custom ABI"%>

    + +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/index.html.eex new file mode 100644 index 0000000..409a4ff --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/index.html.eex @@ -0,0 +1,51 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :custom_abis %> +
    +
    +
    +

    <%= gettext "Custom ABI" %>

    +
    + <%= if Enum.count(@custom_abis) < CustomABI.get_max_custom_abis_count() do %> +
    +
    + <%= gettext "Create a Custom ABI to interact with contracts." %> +
    +
    + <% else %> +
    +
    + <%= gettext "You can create up to 15 Custom ABIs per account." %> +
    +
    + <% end %> +
    +
    + <%= if @custom_abis != [] do %> + + + + + + + + + + + <%= Enum.map(@custom_abis, fn key -> + render("row.html", custom_abi: key, conn: @conn) + end) %> + +
    <%= gettext "Name" %><%= gettext "Contract Address" %>
    + <% end %> +
    +
    + <%= if Enum.count(@custom_abis) < CustomABI.get_max_custom_abis_count() do %> + <%= gettext "Add Custom ABI" %> + <% end %> +
    +
    +
    +
    + +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/row.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/row.html.eex new file mode 100644 index 0000000..36c2740 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/custom_abi/row.html.eex @@ -0,0 +1,18 @@ + + <%= @custom_abi.name %> + + <%= link(@custom_abi.address_hash, to: address_contract_path(@conn, :index, @custom_abi.address_hash)) %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], clipboard_text: @custom_abi.address_hash, aria_label: gettext("Copy Contract Address"), title: gettext("Copy Contract Address"), style: "display: inline-block; vertical-align: text-bottom; position: initial; margin-top: 1px;" %> + + +
    + + +
    + <%= gettext("Remove") %> + + + <%= link gettext("Edit"), to: custom_abi_path(@conn, :edit, @custom_abi.id) %> + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/address_field.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/address_field.html.eex new file mode 100644 index 0000000..a0b78f3 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/address_field.html.eex @@ -0,0 +1,8 @@ +
    + <%= label @f, :addresses, gettext("Address*"), class: "control-label", style: "font-size: 14px" %> +
    + <%= array_input @f, :addresses, maxlength: 42, size: 70, placeholder: gettext "Smart contract / Address (0x...)" %> + <%= array_add_button @f, :addresses, maxlength: 42, size: 70, placeholder: gettext "Smart contract / Address (0x...)" %> +
    + <%= error_tag @f, :addresses, class: "text-danger form-error pt-0" %> +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/form.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/form.html.eex new file mode 100644 index 0000000..86cfd1d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/form.html.eex @@ -0,0 +1,72 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :public_tags %> +
    +
    +
    +

    <%=if @method == :update, do: gettext("Request to edit a public tag/label"), else: gettext("Request a public tag/label") %>

    + +
    +
    +
    +
    + +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/index.html.eex new file mode 100644 index 0000000..bd94519 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/index.html.eex @@ -0,0 +1,44 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :public_tags %> +
    +
    +
    +

    <%= gettext "Public tags" %>

    +
    +
    +
    + <%= gettext "You can request a public category tag which is displayed to all Blockscout users. Public tags may be added to contract or external addresses, and any associated transactions will inherit that tag. Clicking a tag opens a page with related information and helps provide context and data organization. Requests are sent to a moderator for review and approval. This process can take several days." %> +
    +
    +
    +
    + <%= if @public_tags_requests != [] do %> + + + + + + + + + + + + <%= Enum.map(@public_tags_requests, fn x -> + render("row.html", public_tags_request: x, conn: @conn) + end) %> + +
    <%= gettext "Public tag" %><%= gettext "Smart contract / Address" %><%= gettext "Submission date" %>
    + <% end %> +
    +
    + <%= if Enum.count(@public_tags_requests) < PublicTagsRequest.get_max_public_tags_request_count() do %> + <%= gettext "Request to add public tag" %> + <% end %> +
    +
    +
    +
    + +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/row.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/row.html.eex new file mode 100644 index 0000000..f7d2772 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/public_tags_request/row.html.eex @@ -0,0 +1,20 @@ + + + <%= for tag <- String.split(@public_tags_request.tags, ";") do %> +
    <%= tag %>
    + <% end %> + + <%= Enum.join(@public_tags_request.addresses, "\n") %> + <%= Calendar.strftime(@public_tags_request.inserted_at, "%b %d, %Y") %> + + <%= link (render BlockScoutWeb.CommonComponentsView, "_svg_pen.html"), to: public_tags_request_path(@conn, :edit, @public_tags_request.id) %> + + +
    + + + +
    + <%= (render BlockScoutWeb.CommonComponentsView, "_svg_trash.html") %> + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/form.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/form.html.eex new file mode 100644 index 0000000..ec4d930 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/form.html.eex @@ -0,0 +1,33 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :address_tags %> +
    +
    +
    +

    <%= gettext "Add address tag"%>

    + +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/index.html.eex new file mode 100644 index 0000000..511405c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/index.html.eex @@ -0,0 +1,41 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :address_tags %> +
    +
    +
    +

    <%= gettext "Address Tags" %>

    +
    +
    +
    + <%= if @address_tags == [] do %> +
    +
    + <%= gettext "You don't have address tags yet" %> +
    +
    +

    + <% else %> + + + + + + + + + + <%= Enum.map(@address_tags, fn at -> + render("row.html", address_tag: at, conn: @conn) + end) %> + +
    <%= gettext "Name" %><%= gettext "Address" %><%= gettext "Action" %>
    + <% end %> +
    +
    + <%= if Enum.count(@address_tags) < TagAddress.get_max_tags_count() do %> + <%= gettext "Add address tag" %> + <% end %> +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/row.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/row.html.eex new file mode 100644 index 0000000..5fd4445 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_address/row.html.eex @@ -0,0 +1,15 @@ +<%= if @address_tag.address_hash do %> + + <%= @address_tag.name %> + +
    + <%= link(trimmed_hash(@address_tag.address_hash), to: address_path(@conn, :show, @address_tag.address_hash)) %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], clipboard_text: @address_tag.address_hash, aria_label: gettext("Copy Address"), title: gettext("Copy Address"), style: "display: inline-block; vertical-align: text-bottom; position: initial; margin-top: 1px;" %> + + + <%= link "Remove Tag", to: tag_address_path(@conn, :delete, @address_tag.id), method: :delete %> +
    + + +<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/form.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/form.html.eex new file mode 100644 index 0000000..40d9c03 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/form.html.eex @@ -0,0 +1,33 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :transaction_tags %> +
    +
    +
    +

    <%= gettext "Add transaction tag"%>

    + +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/index.html.eex new file mode 100644 index 0000000..c1cca8a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/index.html.eex @@ -0,0 +1,41 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :transaction_tags %> +
    +
    +
    +

    <%= gettext "Transaction Tags" %>

    +
    +
    +
    + <%= if @tx_tags == [] do %> +
    +
    + <%= gettext "You don't have transaction tags yet" %> +
    +
    +

    + <% else %> + + + + + + + + + + <%= Enum.map(@tx_tags, fn at -> + render("row.html", tx_tag: at, conn: @conn) + end) %> + +
    <%= gettext "Name" %><%= gettext "Transaction" %><%= gettext "Action" %>
    + <% end %> +
    +
    + <%= if Enum.count(@tx_tags) < TagTransaction.get_max_tags_count() do %> + <%= gettext "Add transaction tag" %> + <% end %> +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex new file mode 100644 index 0000000..55151ae --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/tag_transaction/row.html.eex @@ -0,0 +1,18 @@ +<%= if @tx_tag.tx_hash do %> + + <%= @tx_tag.name %> + +
    + <%= link(@tx_tag.tx_hash, + to: transaction_path(BlockScoutWeb.Endpoint, :show, @tx_tag.tx_hash), + "data-test": "transaction_hash_link", + class: "text-truncate") %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], clipboard_text: @tx_tag.tx_hash, aria_label: gettext("Copy Address"), title: gettext("Copy Address"), style: "display: inline-block; vertical-align: text-bottom; position: initial; margin-top: 1px;" %> +
    + + + <%= link "Remove Tag", to: tag_transaction_path(@conn, :delete, @tx_tag.id), method: :delete %> + + +<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/watchlist/show.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/watchlist/show.html.eex new file mode 100644 index 0000000..592532f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/watchlist/show.html.eex @@ -0,0 +1,42 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :watchlist %> +
    +
    +
    +

    <%= gettext "Watch list" %>

    +
    +
    +
    + <%= if @watchlist.watchlist_addresses == [] do %> +
    +
    + <%= gettext "You don't have addresses on you watchlist yet" %> +
    +
    +

    + <% else %> + + + + + + + + + + + <%= Enum.map(@watchlist.watchlist_addresses, fn wa -> + render(WatchlistAddressView, "row.html", watchlist_address: wa, exchange_rate: exchange_rate(), conn: @conn) + end) %> + +
    <%= gettext "Name" %><%= gettext "Address" %><%= gettext "Balance" %><%= gettext "Actions" %>
    + <% end %> +
    +
    + <%= if Enum.count(@watchlist.watchlist_addresses) < WatchlistAddress.get_max_watchlist_addresses_count() do %> + <%= gettext "Add address" %> + <% end %> +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/watchlist_address/form.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/watchlist_address/form.html.eex new file mode 100644 index 0000000..c2a6dce --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/watchlist_address/form.html.eex @@ -0,0 +1,91 @@ +
    +
    + <%= render BlockScoutWeb.Account.CommonView, "_nav.html", conn: @conn, active_item: :watchlist %> +
    +
    +
    +

    <%=if @method == :update, do: gettext("Edit Watch list address"), else: gettext "Add address to the Watch list" %>

    + +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/account/watchlist_address/row.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/account/watchlist_address/row.html.eex new file mode 100644 index 0000000..95c92d9 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/account/watchlist_address/row.html.eex @@ -0,0 +1,29 @@ + + <%= @watchlist_address.name %> + +
    + <%= link(trimmed_hash(@watchlist_address.address_hash), to: address_path(@conn, :show, @watchlist_address.address_hash)) %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], clipboard_text: @watchlist_address.address_hash, aria_label: gettext("Copy From Address"), title: gettext("Copy Address"), style: "display: inline-block; vertical-align: text-bottom; position: initial; margin-top: 1px;" %> +
    + + + <%= balance_ether(@watchlist_address.fetched_coin_balance) %> +
    + + + ( + ) + + + + <%= link(gettext("Edit"), to: watchlist_address_path(@conn, :edit, @watchlist_address.id)) %> + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_balance_dropdown.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_balance_dropdown.html.eex new file mode 100644 index 0000000..e590b9f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_balance_dropdown.html.eex @@ -0,0 +1,12 @@ +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Fetching tokens...") %> + + +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_block_link.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_block_link.html.eex new file mode 100644 index 0000000..935d6e7 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_block_link.html.eex @@ -0,0 +1 @@ +<%= link(@block_number, to: block_path(@conn, :show, @block_number), class: "tile-title-lg") %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_current_coin_balance.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_current_coin_balance.html.eex new file mode 100644 index 0000000..405f7e6 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_current_coin_balance.html.eex @@ -0,0 +1,15 @@ +<%= format_wei_value(@coin_balance, :ether) %> +<%= if !empty_exchange_rate?(@exchange_rate) do %> + <% usd_value = to_string(@exchange_rate.usd_value) %> + + ( + ) + +<% end %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_custom_view_df_title.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_custom_view_df_title.html.eex new file mode 100644 index 0000000..fd99f0a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_custom_view_df_title.html.eex @@ -0,0 +1,19 @@ +
    + +
    + + <%= @title %> + + + + + + +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_labels.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_labels.html.eex new file mode 100644 index 0000000..dbfb5ba --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_labels.html.eex @@ -0,0 +1,19 @@ +<%= for common_tag <- @tags.common_tags do %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: common_tag.display_name, additional_classes: [tag_name_to_label(common_tag.label), "ml-1"] %> +<% end %> +<%= for personal_tag <- @tags.personal_tags do %> + <%= if personal_tag.address_hash do %> + <%= if personal_tag.label =~ "dark forest" do %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: personal_tag.display_name, additional_classes: ["df", "ml-1"] %> + <% else %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: personal_tag.display_name, additional_classes: [tag_name_to_label(personal_tag.label), "ml-1"] %> + <% end %> + <% end %> +<% end %> +<%= for watchlist_name <- @tags.watchlist_names do %> + <%= if watchlist_name.label =~ "dark forest" do %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: watchlist_name.display_name, additional_classes: ["df", "ml-1"] %> + <% else %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: watchlist_name.display_name, additional_classes: [tag_name_to_label(watchlist_name.label), "ml-1"] %> + <% end %> +<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_link.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_link.html.eex new file mode 100644 index 0000000..275a8f0 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_link.html.eex @@ -0,0 +1,15 @@ +<%= if @address do %> + <%= if assigns[:show_full_hash] do %> + <%= if name = if assigns[:ignore_implementation_name], do: primary_name(@address), else: implementation_name(@address) || primary_name(@address) do %> + <%= name %> | + <% end %> + <%= link to: address_path(BlockScoutWeb.Endpoint, :show, @address), "data-test": "address_hash_link", class: assigns[:class] do %> + <%= @address %> + <% end %> + + <% else %> + <%= link to: address_path(BlockScoutWeb.Endpoint, :show, @address), "data-test": "address_hash_link", class: assigns[:class] do %> + <%= render BlockScoutWeb.AddressView, "_responsive_hash.html", address: @address, contract: @contract, truncate: assigns[:truncate], use_custom_tooltip: @use_custom_tooltip, no_tooltip: assigns[:no_tooltip], custom_classes_tooltip: assigns[:custom_classes_tooltip], ignore_implementation_name: assigns[:ignore_implementation_name] %> + <% end %> + <% end %> +<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_metatags.html.eex new file mode 100644 index 0000000..4e18247 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_metatags.html.eex @@ -0,0 +1,15 @@ +<%= if assigns[:address] do %> + + <%= gettext( + "%{address} - %{subnetwork} Explorer", + address: BlockScoutWeb.AddressView.address_page_title(@address), + subnetwork: LayoutView.subnetwork_title() + ) %> + + "> + Explorer.coin() <> ", "<> LayoutView.network_title() %>"> +<% else %> + + <%= gettext "Top Accounts - %{subnetwork} Explorer", subnetwork: LayoutView.subnetwork_title() %> + +<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_responsive_hash.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_responsive_hash.html.eex new file mode 100644 index 0000000..c3dc9af --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_responsive_hash.html.eex @@ -0,0 +1,30 @@ + + <%= if name = if assigns[:ignore_implementation_name], do: primary_name(@address), else: implementation_name(@address) || primary_name(@address) do %> + <%= if assigns[:no_tooltip] do %> + <%= if @use_custom_tooltip == true do %> + <%= name %> (<%= short_hash(@address) %>...) + <% else %> + <%= short_contract_name(name, 30) %> + <%= short_contract_name(name, 10) %> + (<%= BlockScoutWeb.AddressView.trimmed_hash(@address.hash) %>) + <% end %> + <% else %> + <%= if @use_custom_tooltip == true do %> + <%= name %> (<%= short_hash(@address) %>...) + <% else %> + "> + <%= short_contract_name(name, 30) %> + <%= short_contract_name(name, 10) %> + (<%= BlockScoutWeb.AddressView.trimmed_hash(@address.hash) %>) + + <% end %> + <% end %> + <% else %> + <%= if assigns[:truncate] do %> + <%= BlockScoutWeb.AddressView.trimmed_hash(@address.hash) %> + <% else %> + <%= @address %> + <%= BlockScoutWeb.AddressView.trimmed_hash(@address.hash) %> + <% end %> + <% end %> + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_show_address_transactions.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_show_address_transactions.html.eex new file mode 100644 index 0000000..0ce6401 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_show_address_transactions.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressTransactionView, "index.html", assigns %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex new file mode 100644 index 0000000..ce533de --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex @@ -0,0 +1,107 @@ +<% dark_forest_addresses_list_0_4 = CustomContractsHelpers.get_custom_addresses_list(:dark_forest_addresses) %> +<% dark_forest_addresses_list_0_5 = CustomContractsHelpers.get_custom_addresses_list(:dark_forest_addresses_v_0_5) %> +<% dark_forest_addresses_list = dark_forest_addresses_list_0_4 ++ dark_forest_addresses_list_0_5 %> +<% current_address = "0x" <> Base.encode16(@address.hash.bytes, case: :lower) %> +
    + <%= link( + gettext("Transactions"), + class: "card-tab #{tab_status("transactions", @conn.request_path)}", + to: AccessHelpers.get_path(@conn, :address_transaction_path, :index, @address.hash) + ) %> + <%= if Chain.check_if_token_transfers_at_address(@address.hash) do %> + <%= link( + gettext("Token Transfers"), + class: "card-tab #{tab_status("token-transfers", @conn.request_path)}", + "data-test": "token_transfers_tab_link", + to: AccessHelpers.get_path(@conn, :address_token_transfers_path, :index, @address.hash) + ) %> + <% end %> + <%= if Chain.check_if_tokens_at_address(@address.hash) do %> + <%= link( + gettext("Tokens"), + class: "card-tab #{tab_status("tokens", @conn.request_path)}", + to: AccessHelpers.get_path(@conn, :address_token_path, :index, @address.hash), + "data-test": "tokens_tab_link" + ) %> + <% end %> + <%= link( + gettext("Internal Transactions"), + class: "card-tab #{tab_status("internal-transactions", @conn.request_path)}", + "data-test": "internal_transactions_tab_link", + to: AccessHelpers.get_path(@conn, :address_internal_transaction_path, :index, @address.hash) + ) %> + <%= link( + gettext("Coin Balance History"), + class: "card-tab #{tab_status("coin-balances", @conn.request_path)}", + "data-test": "coin_balance_tab_link", + to: AccessHelpers.get_path(@conn, :address_coin_balance_path, :index, @address.hash) + ) %> + <%= if Chain.check_if_logs_at_address(@address.hash) do %> + <%= link( + gettext("Logs"), + class: "card-tab #{tab_status("logs", @conn.request_path)}", + to: AccessHelpers.get_path(@conn, :address_logs_path, :index, @address.hash) + ) %> + <% end %> + <%= if Chain.check_if_validated_blocks_at_address(@address.hash) do %> + <%= link( + gettext("Blocks Validated"), + class: "card-tab #{tab_status("validations", @conn.request_path)}", + "data-test": "validations_tab_link", + to: AccessHelpers.get_path(@conn, :address_validation_path, :index, @address.hash) + ) %> + <% end %> + <%= if contract?(@address) do %> + <%= link( + to: AccessHelpers.get_path(@conn, :address_contract_path, :index, @address.hash), + class: "card-tab #{tab_status("contracts", @conn.request_path)}") do %> + <%= gettext("Code") %> + <%= if smart_contract_verified?(@address) do %> + <%= cond do %> + <% Enum.member?(dark_forest_addresses_list, current_address) -> %> + + <%= render BlockScoutWeb.IconsView, "_check_dark_forest_icon.html" %> + + <% true -> %> + + <% end %> + <% end %> + <% end %> + <% end %> + <%= if has_decompiled_code?(@address) do %> + <%= link( + to: AccessHelpers.get_path(@conn, :address_decompiled_contract_path, :index, @address.hash), + class: "card-tab #{tab_status("decompiled-contracts", @conn.request_path)}") do %> + <%= gettext("Decompiled code") %> + + <% end %> + <% end %> + <%= if smart_contract_with_read_only_functions?(@address) || has_address_custom_abi_with_read_functions?(@conn, @address.hash) do %> + <%= link( + gettext("Read Contract"), + to: AccessHelpers.get_path(@conn, :address_read_contract_path, :index, @address.hash), + class: "card-tab #{tab_status("read-contract", @conn.request_path)}") + %> + <% end %> + <%= if @is_proxy do %> + <%= link( + gettext("Read Proxy"), + to: AccessHelpers.get_path(@conn, :address_read_proxy_path, :index, @address.hash), + class: "card-tab #{tab_status("read-proxy", @conn.request_path)}") + %> + <% end %> + <%= if smart_contract_with_write_functions?(@address) || has_address_custom_abi_with_write_functions?(@conn, @address.hash) do %> + <%= link( + gettext("Write Contract"), + to: AccessHelpers.get_path(@conn, :address_write_contract_path, :index, @address.hash), + class: "card-tab #{tab_status("write-contract", @conn.request_path)}") + %> + <% end %> + <%= if smart_contract_with_write_functions?(@address) && @is_proxy do %> + <%= link( + gettext("Write Proxy"), + to: AccessHelpers.get_path(@conn, :address_write_proxy_path, :index, @address.hash), + class: "card-tab #{tab_status("write-proxy", @conn.request_path)}") + %> + <% end %> +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_tile.html.eex new file mode 100644 index 0000000..9e91a5a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_tile.html.eex @@ -0,0 +1,34 @@ + + + + + <%= @index %> + + + + <%= @address |> BlockScoutWeb.AddressView.address_partial_selector(nil, nil) |> BlockScoutWeb.RenderHelpers.render_partial() %> + + + <%= balance(@address) %> + + + data-usd-exchange-rate="<%= @exchange_rate.usd_value %>"> + <% end %> + + + <%= if balance_percentage_enabled?(@total_supply) do %> + + + <%= balance_percentage(@address, @total_supply) %> + + <% end %> + + + + <%= @tx_count %> + <%= gettext "Transactions sent" %> + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex new file mode 100644 index 0000000..60a022f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex @@ -0,0 +1,41 @@ + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer.html.eex new file mode 100644 index 0000000..24a31bb --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer.html.eex @@ -0,0 +1,15 @@ +<%= if @type=="address" do %> + +<% else %> + +<% end %> + +
    +

    <%= @header %>

    + <%= if @type=="address" do %> +
    <%= address_link_to_other_explorer(@address_link, @hash ,true) %>
    + <% else %> +
    <%= address_link_to_other_explorer(@tx_link, @hash ,true) %>
    + <% end %> +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer_modal.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer_modal.html.eex new file mode 100644 index 0000000..69ffb70 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorer_modal.html.eex @@ -0,0 +1,22 @@ +
    +
    + +
    +
    + + <%= if @type=="address" do %> + <%= link( + address_link_to_other_explorer(@address_link, @hash, false), + to: address_link_to_other_explorer(@address_link, @hash ,true) + ) %> + <% else %> + <%= link( + address_link_to_other_explorer(@tx_link, @hash, false), + to: address_link_to_other_explorer(@tx_link, @hash ,true) + ) %> + <% end %> + +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorers.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorers.html.eex new file mode 100644 index 0000000..21afb57 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/_verify_other_explorers.html.eex @@ -0,0 +1,38 @@ +
    + +
    +

    Verify with other Explorers:

    +
    + <%= render "_verify_other_explorer.html", hash: @hash, type: @type, header: "Etherscan.io", class: "etherscan", address_link: "https://etherscan.io/address/", tx_link: "https://etherscan.io/tx/" %> + <%= render "_verify_other_explorer.html", hash: @hash, type: @type, header: "Blockchair.com", class: "blockchair", address_link: "https://blockchair.com/ethereum/address/", tx_link: "https://blockchair.com/ethereum/transaction/" %> + <%= render "_verify_other_explorer.html", hash: @hash, type: @type, header: "Etherchain.org", class: "etherchain", address_link: "https://www.etherchain.org/account/", tx_link: "https://www.etherchain.org/tx/" %> + + + + + + +
    +
    + + + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex new file mode 100644 index 0000000..57dba32 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex @@ -0,0 +1,58 @@ +
    + <%= render BlockScoutWeb.Advertisement.TextAdView, "index.html", conn: @conn %> +
    +
    +

    <%= Explorer.coin_name() %> <%= gettext "Addresses" %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + +
    +
    + + + + + + + <%= if balance_percentage_enabled?(@total_supply) do %> + + <% end %> + + + + + <% columns_num = if balance_percentage_enabled?(@total_supply), do: 5, else: 4 %> + <%= render BlockScoutWeb.CommonComponentsView, "_table-loader.html", columns_num: columns_num %> + +
    +
    +   +
    +
    +
    + Address +
    +
    +
    + Balance +
    +
    +
    + Percentage +
    +
    +
    + Txn Count +
    +
    +
    +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    +
    + + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex new file mode 100644 index 0000000..34059b2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex @@ -0,0 +1,292 @@ +
    + <%= render BlockScoutWeb.Advertisement.TextAdView, "index.html", conn: @conn %> + <% dark_forest_addresses_list_0_4 = CustomContractsHelpers.get_custom_addresses_list(:dark_forest_addresses) %> + <% dark_forest_addresses_list_0_5 = CustomContractsHelpers.get_custom_addresses_list(:dark_forest_addresses_v_0_5) %> + <% circles_addresses_list = CustomContractsHelpers.get_custom_addresses_list(:circles_addresses) %> + <% current_address = "0x" <> Base.encode16(@address.hash.bytes, case: :lower) %> + <% created_from_address_hash = if from_address_hash(@address), do: "0x" <> Base.encode16(from_address_hash(@address).bytes, case: :lower), else: nil %> +
    + +
    +
    +
    + <%= cond do %> + <% Enum.member?(dark_forest_addresses_list_0_4, current_address) -> %> + <%= render BlockScoutWeb.AddressView, "_custom_view_df_title.html", title: "zkSnark space warfare (v0.4)" %> + <% Enum.member?(dark_forest_addresses_list_0_5, current_address) -> %> + <%= render BlockScoutWeb.AddressView, "_custom_view_df_title.html", title: "zkSnark space warfare (v0.5)" %> + <% Enum.member?(circles_addresses_list, current_address) -> %> +
    + +
    + <% Enum.member?(circles_addresses_list, created_from_address_hash) -> %> +
    + +
    + <% true -> %> + <%= nil %> + <% end %> +

    +
    <%= address_title(@address) %> <%= gettext "Details" %>
    + <%= render BlockScoutWeb.AddressView, "_labels.html", address_hash: @address.hash, tags: @tags %> + + + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + id: "tx-raw-input", + additional_classes: ["overview-title-item"], + clipboard_text: @address, + aria_label: gettext("Copy Address"), + title: gettext("Copy Address") %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_qr_code.html" %> + +

    +

    <%= @address %>

    + + + <% address_name = primary_name(@address) %> + <%= cond do %> + <% @address.token -> %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Token name and symbol.") %> + <%= gettext("Token") %> +
    +
    + <%= link( + token_title(@address.token), + to: token_path(@conn, + :show, + @address.hash), + "data-test": + "token_hash_link" + ) + %> +
    +
    + <% address_name -> %> + <%= if contract?(@address) do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("The name found in the source code of the Contract.") %> + <%= gettext("Contract Name") %> +
    +
    + <%= short_contract_name(address_name, 30) %> +
    +
    + <% else %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("The name of the validator.") %> + <%= gettext("Validator Name") %> +
    +
    + <%= short_contract_name(address_name, 30) %> +
    +
    + <% end %> + <% true -> %> + <% end %> + + <% from_address_hash = from_address_hash(@address) %> + <%= if contract?(@address) do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Transactions and address of creation.") %> + <%= gettext("Creator") %> +
    +
    + <%= if from_address_hash do %> + <%= link( + trimmed_hash(from_address_hash(@address)), + to: address_path(@conn, :show, from_address_hash(@address)) + ) %> + + <%= gettext "at" %> + + <%= link( + trimmed_hash(transaction_hash(@address)), + to: transaction_path(@conn, :show, transaction_hash(@address)), + "data-test": "transaction_hash_link" + ) %> + <% else %> + + <% end %> +
    +
    + <% end %> + + <%= if @is_proxy do %> + <% {implementation_address, name} = Chain.get_implementation_address_hash(@address.hash, @address.smart_contract.abi) || "0x0000000000000000000000000000000000000000" %> + <%= if implementation_address do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Implementation address of the proxy contract.") %> + <%= gettext("Implementation") %> +
    +
    + <%= link( + (if name, do: name <> " | " <> implementation_address, else: implementation_address), + to: address_path(@conn, :show, implementation_address), + class: "contract-address" + ) + %> +
    +
    + <% end %> + <% end %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Address balance in") <> " " <> Explorer.coin_name() <> " " <> gettext("doesn't include ERC20, ERC721, ERC1155 tokens).") %> + <%= gettext("Balance") %> +
    +
    + <%= balance(@address) %> + <%= if !match?({:pending, _}, @coin_balance_status) && !empty_exchange_rate?(@exchange_rate) do %> + <% usd_value = to_string(@exchange_rate.usd_value) %> + + ( + ) + + <% end %> +
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("All tokens in the account and total value.") %> + <%= gettext("Tokens") %> +
    +
    + <%= render BlockScoutWeb.AddressView, "_balance_dropdown.html", conn: @conn, address: @address %> +
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Number of transactions related to this address.") %> + <%= gettext("Transactions") %> +
    +
    + <%= if @conn.request_path |> String.contains?("/transactions") do %> + + <%= if @address.transactions_count do %> + <%= Number.Delimit.number_to_delimited(@address.transactions_count, precision: 0) %> <%= gettext("Transactions") %> + <% else %> + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Fetching transactions...") %> + <% end %> + + <% else %> + + <%= if @address.token_transfers_count do %> + <%= Number.Delimit.number_to_delimited(@address.transactions_count, precision: 0) %> <%= gettext("Transactions") %> + <% else %> + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Fetching transactions...") %> + <% end %> + + <% end %> +
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Number of transfers to/from this address.") %> + <%= gettext("Transfers") %> +
    +
    + <%= if @conn.request_path |> String.contains?("/token-transfers") do %> + + <%= if @address.token_transfers_count do %> + <%= Number.Delimit.number_to_delimited(@address.token_transfers_count, precision: 0) %> <%= gettext("Transfers") %> + <% else %> + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Fetching transfers...") %> + <% end %> + + <% else %> + + <%= if @address.token_transfers_count do %> + <%= Number.Delimit.number_to_delimited(@address.token_transfers_count, precision: 0) %> <%= gettext("Transfers") %> + <% else %> + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Fetching transfers...") %> + <% end %> + + <% end %> +
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Gas used by the address.") %> + <%= gettext("Gas Used") %> +
    +
    + + <%= if @address.gas_used do %> + <%= Number.Delimit.number_to_delimited(@address.gas_used, precision: 0) %> + <% else %> + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Fetching gas used...") %> + <% end %> + +
    +
    + + <%= if @address.fetched_coin_balance_block_number do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Block number in which the address was updated.") %> + <%= gettext("Last Balance Update") %> +
    +
    + <%= link( + @address.fetched_coin_balance_block_number, + to: block_path(@conn, :show, @address.fetched_coin_balance_block_number), + class: "tile-title-lg" + ) %> +
    +
    + <% end %> +
    + + +
    +
    +
    +
    +
    + + +<%= render BlockScoutWeb.CommonComponentsView, "_modal_qr_code.html", qr_code: qr_code(@address), title: @address %> + +<%= render BlockScoutWeb.Advertisement.BannersAdView, "_banner_728.html", conn: @conn %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/_coin_balances.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/_coin_balances.html.eex new file mode 100644 index 0000000..74e974b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/_coin_balances.html.eex @@ -0,0 +1,31 @@ +
    +
    +
    + <%= link( + to: block_path(@conn, :show, @coin_balance.block_number), + class: "tile-title-lg" + ) do %> + <%= gettext "Block" %> <%= @coin_balance.block_number %> + <% end %> + <%= if @coin_balance.transaction_hash do %> + <%= link( + to: transaction_path(@conn, :show, @coin_balance.transaction_hash) + ) do %> + <%= @coin_balance.transaction_hash %> + <% end %> + <% end %> + +
    +
    + + <%= delta_arrow(@coin_balance.delta) %> + <%= format_delta(@coin_balance.delta) %> + +
    +
    + + <%= format(@coin_balance.value) %> + +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex new file mode 100644 index 0000000..9292206 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_coin_balance/index.html.eex @@ -0,0 +1,48 @@ +
    + + + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> + +
    +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost, click to load newer blocks") %> + +

    <%= gettext "Balances" %>

    + + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading chart...") %> +
    + + + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + + + +
    +
    + <%= gettext "There is no coin history for this address." %> +
    +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    + +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/_metatags.html.eex new file mode 100644 index 0000000..3ef2a67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex new file mode 100644 index 0000000..f3b11b6 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex @@ -0,0 +1,224 @@ +<% contract_creation_code = contract_creation_code(@address) %> +<% minimal_proxy_template = Chain.get_minimal_proxy_template(@address.hash) %> +<% metadata_for_verification = minimal_proxy_template || Chain.get_address_verified_twin_contract(@address.hash).verified_contract %> +<% smart_contract_verified = BlockScoutWeb.AddressView.smart_contract_verified?(@address) %> +<% additional_sources_from_twin = Chain.get_address_verified_twin_contract(@address.hash).additional_sources %> +<% fully_verified = Chain.smart_contract_fully_verified?(@address.hash)%> +<% additional_sources = if smart_contract_verified, do: @address.smart_contract_additional_sources, else: additional_sources_from_twin %> +
    + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> + +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> +
    + <%= unless smart_contract_verified do %> + <%= if minimal_proxy_template do %> + <%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: metadata_for_verification.address_hash, conn: @conn %> + <% else %> + <%= if metadata_for_verification do %> + <% path = address_verify_contract_path(@conn, :new, @address.hash) %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_info.html" %> + <%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link( + metadata_for_verification.address_hash, + to: address_contract_path(@conn, :index, metadata_for_verification.address_hash)) %>.
    <%= gettext("All metadata displayed below is from that contract. In order to verify current contract, click") %> <%= gettext("Verify & Publish") %> <%= gettext("button") %>
    +
    + <%= link(gettext("Verify & Publish"), to: path, class: "button button-primary button-sm float-right ml-3", "data-test": "verify_and_publish") %> +
    + <% end %> + <% end %> + <% end %> + <%= if smart_contract_verified && @address.smart_contract.is_changed_bytecode do %> + <%= render BlockScoutWeb.CommonComponentsView, "_changed_bytecode_warning.html" %> + <% end %> + <%= if smart_contract_verified || (!smart_contract_verified && metadata_for_verification) do %> + <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: metadata_for_verification %> + <%= if @address.smart_contract.partially_verified && smart_contract_verified do %> +
    + <%= gettext("This contract has been partially verified via Sourcify.") %> + <% else %> + <%= if @address.smart_contract.verified_via_sourcify && smart_contract_verified do %> +
    + <%= gettext("This contract has been verified via Sourcify.") %> + <% end %> + <% end %> + <%= if @address.smart_contract.verified_via_sourcify && smart_contract_verified do %> + target="_blank"> + View contract in Sourcify repository <%= render BlockScoutWeb.IconsView, "_external_link.html" %> + +
    + + <% end %> +
    +
    +
    <%= gettext "Contract name:" %>
    +
    <%= target_contract.name %>
    +


    +


    +
    <%= gettext "Optimization enabled" %>
    +
    <%= if target_contract.is_vyper_contract, do: "N/A", else: format_optimization_text(target_contract.optimization) %>
    +
    +
    +
    <%= gettext "Compiler version" %>
    +
    <%= target_contract.compiler_version %>
    +


    +


    + <%= if target_contract.optimization && target_contract.optimization_runs do %> +
    <%= gettext "Optimization runs" %>
    +
    <%= target_contract.optimization_runs %>
    + <% end %> +
    +
    + <%= if smart_contract_verified && target_contract.evm_version do %> +
    <%= gettext "EVM Version" %>
    +
    <%= target_contract.evm_version %>
    +


    +


    + <% end %> + <%= if target_contract.inserted_at do %> +
    <%= gettext "Verified at" %>
    +
    <%= target_contract.inserted_at %>
    + <% end %> +
    +
    + <%= if smart_contract_verified && target_contract.constructor_arguments do %> +
    +
    +

    <%= gettext "Constructor Arguments" %>

    +
    +
    +
    <%= raw(format_constructor_arguments(target_contract, @conn)) %>
    +              
    +
    +
    + <% end %> +
    +
    +

    <%= target_contract.file_path || gettext "Contract source code" %>

    + +
    +
    ><%= target_contract.contract_source_code %>
    +        
    + + <%= additional_sources |> Enum.with_index() |> Enum.map(fn {additional_source, index} -> %> +
    +
    +

    <%= additional_source.file_name %>

    + +
    +
    <%= additional_source.contract_source_code %>
    +          
    + <% end)%> + +
    +
    +

    <%= gettext "Contract ABI" %>

    + +
    +
    +
    <%= format_smart_contract_abi(target_contract.abi) %>
    +            
    +
    +
    + + <% end %> +
    + <%= case contract_creation_code do %> + <% {:selfdestructed, transaction_init} -> %> +
    +

    <%= gettext "Contract Creation Code" %>

    + +
    +
    +

    <%= gettext "Contracts that self destruct in their constructors have no contract code published and cannot be verified." %>

    +

    <%= gettext "Displaying the init data provided of the creating transaction." %>

    +
    +
    +
    <%= transaction_init %>
    +
    + <% {:ok, contract_code} -> %> + <%= if creation_code(@address) do %> +
    +

    <%= gettext "Contract Creation Code" %>

    +
    + + <%= if !fully_verified do %> + <% path = address_verify_contract_path(@conn, :new, @address.hash) %> + <%= link( + gettext("Verify & Publish"), + to: path, + class: "button button-primary button-sm float-right ml-3", + "data-test": "verify_and_publish" + ) %> + <% end %> +
    +
    +
    +
    <%= creation_code(@address) %>
    +
    + <% end %> + <%= if fully_verified do %> +
    +

    <%= gettext "Deployed ByteCode" %>

    + +
    + <% else %> +
    +
    +

    <%= gettext "Deployed ByteCode" %>

    +
    +
    + + <%= if !fully_verified and !creation_code(@address) do %> + <% path = address_verify_contract_path(@conn, :new, @address.hash) %> + <%= link( + gettext("Verify & Publish"), + to: path, + class: "button button-primary button-sm float-right ml-3", + "data-test": "verify_and_publish" + ) %> + <% end %> +
    +
    + <% end %> +
    +
    <%= contract_code %>
    +
    + <% end %> +
    + + <%= if smart_contract_verified || (!smart_contract_verified && metadata_for_verification) do %> + <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: metadata_for_verification %> + <%= if target_contract.external_libraries && target_contract.external_libraries != [] do %> +
    +
    +

    <%= gettext "External libraries" %>

    +
    +
    +
    <%= raw(format_external_libraries(target_contract.external_libraries, @conn)) %>
    +              
    +
    +
    + <% end %> + <% end %> +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex new file mode 100644 index 0000000..0a9f89f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex @@ -0,0 +1,129 @@ +
    + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost") %> + +
    +

    <%= gettext "New Smart Contract Verification" %>

    + + <%= form_for @changeset, + address_contract_verification_path(@conn, :create), + [], + fn f -> %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", f: f %> + +
    +
    + <%= label f, "Verify" %> +
    +
    +
    + <%= radio_button f, :verify_via, true, checked: true, class: "form-check-input verify-via-flattened-code", "aria-describedby": "verify_via-help-block" %> +
    + <%= label :verify_via, :true, gettext("Via flattened source code"), class: "radio-text" %> +
    +
    + <%= radio_button f, :verify_via, false, class: "form-check-input verify-via-standard-json-input" %> +
    + <%= label :verify_via, :false, gettext("Via Standard Input JSON"), class: "radio-text" %> +
    + <%= if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do %> +
    + <%= radio_button f, :verify_via, false, class: "form-check-input verify-via-sourcify" %> +
    + <%= label :verify_via, :false, gettext("Via Sourcify: Sources and metadata JSON file"), class: "radio-text" %> +
    + <% end %> + <%= if RustVerifierInterface.enabled?() do %> +
    + <%= radio_button f, :verify_via, false, class: "form-check-input verify-via-multi-part-files" %> +
    + <%= label :verify_via, :false, gettext("Via multi-part files"), class: "radio-text" %> +
    + <% end %> +
    + <%= radio_button f, :verify_via, false, class: "form-check-input verify-vyper-contract" %> +
    + <%= label :verify_via, :false, gettext("Vyper contract"), class: "radio-text" %> +
    +
    + <%= error_tag f, :verify_via, id: "verify_via-help-block", class: "text-danger form-error" %> +
    +
    Choose a smart-contract verification method. Currently, Blockscout supports 2 methods:
    + 1. Verification through flattened source code. +
    + 2. Verification using Standard input JSON file.
    + 3. Verification through Sourcify.
    + a) if smart-contract already verified on Sourcify, it will automatically fetch the data from the repo
    + b) otherwise you will be asked to upload source files and JSON metadata file(s).
    + 4. Verification of Vyper contract. +
    + +
    +
    + +
    + + <%= link( + gettext("Next"), + to: address_verify_contract_via_flattened_code_path(@conn, :new, @address_hash), + id: "verify_via_flattened_code_button", + class: "btn-full-primary mr-2", + "data-button-loading": "animation" + ) %> + <%= link( + gettext("Next"), + to: address_verify_contract_via_json_path(@conn, :new, @address_hash), + id: "verify_via_sourcify_button", + class: "btn-full-primary mr-2", + style: "display: none;", + "data-button-loading": "animation" + ) %> + <%= link( + gettext("Next"), + to: address_verify_vyper_contract_path(@conn, :new, @address_hash), + id: "verify_vyper_contract_button", + class: "btn-full-primary mr-2", + style: "display: none;", + "data-button-loading": "animation" + ) %> + <%= link( + gettext("Next"), + to: address_verify_contract_via_standard_json_input_path(@conn, :new, @address_hash), + id: "verify_via_standard_json_input_button", + class: "btn-full-primary mr-2", + style: "display: none;", + "data-button-loading": "animation" + ) %> + <%= link( + gettext("Next"), + to: address_verify_contract_via_multi_part_files_path(@conn, :new, @address_hash), + id: "verify_via_multi_part_files_button", + class: "btn-full-primary mr-2", + style: "display: none;", + "data-button-loading": "animation" + ) %> + <%= + link( + gettext("Cancel"), + class: "btn-no-border", + to: address_contract_path(@conn, :index, @address_hash) + ) + %> +
    + <% end %> +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_compiler_field.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_compiler_field.html.eex new file mode 100644 index 0000000..fd98aaf --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_compiler_field.html.eex @@ -0,0 +1,10 @@ +
    +
    + <%= label @f, :compiler_version, gettext("Compiler") %> +
    + <%= select @f, :compiler_version, @compiler_versions, class: "form-control border-rounded", "aria-describedby": "compiler-help-block", id: "smart_contract_compiler_version" %> + <%= error_tag @f, :compiler_version, id: "compiler-help-block", class: "text-danger form-error" %> +
    +
    <%= raw @tooltip %>
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_constructor_args.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_constructor_args.html.eex new file mode 100644 index 0000000..5fa7785 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_constructor_args.html.eex @@ -0,0 +1,10 @@ +
    +
    + <%= label @f, :constructor_arguments, gettext("ABI-encoded Constructor Arguments (if required by the contract)") %> +
    + <%= textarea @f, :constructor_arguments, class: "form-control border-rounded monospace", rows: 3, "aria-describedby": "contract-constructor-arguments-help-block" %> + <%= error_tag @f, :constructor_arguments, id: "contract-constructor-arguments-help-block", class: "text-danger form-error", "data-test": "contract-constructor-arguments-error" %> +
    +
    Add arguments in ABI hex encoded form. Constructor arguments are written right to left, and will be found at the end of the input created bytecode. They may also be parsed here.
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex new file mode 100644 index 0000000..6084b3b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex @@ -0,0 +1,10 @@ +
    +
    + <%= label @f, :address_hash, gettext("Contract Address") %> +
    + <%= text_input @f, :address_hash, class: "form-control border-rounded", id: "smart_contract_address_hash", "aria-describedby": "contract-address-help-block", readonly: true %> + <%= error_tag @f, :address_hash, id: "contract-address-help-block", class: "text-danger form-error" %> +
    +
    The 0x address supplied on contract creation.
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex new file mode 100644 index 0000000..f4c39dd --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex @@ -0,0 +1,10 @@ +
    +
    + <%= label @f, :name, gettext("Contract Name") %> +
    + <%= text_input @f, :name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block", "data-test": "contract_name" %> + <%= error_tag @f, :name, id: "contract-name-help-block", class: "text-danger form-error" %> +
    +
    <%= raw @tooltip %>
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex new file mode 100644 index 0000000..fcc2109 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex @@ -0,0 +1,20 @@ +
    +
    + <%= label @f, "Try to fetch constructor arguments automatically" %> +
    +
    +
    + <%= radio_button @f, :autodetect_constructor_args, false, class: "form-check-input autodetectfalse" %> +
    + <%= label :autodetect_constructor_args, :false, gettext("No"), class: "radio-text" %> +
    +
    + <%= radio_button @f, :autodetect_constructor_args, true, class: "form-check-input autodetecttrue", "aria-describedby": "autodetect_constructor_args-help-block" %> +
    + <%= label :autodetect_constructor_args, :true, gettext("Yes"), class: "radio-text" %> +
    +
    + <%= error_tag @f, :autodetect_constructor_args, id: "autodetect_constructor_args-help-block", class: "text-danger form-error" %> +
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex new file mode 100644 index 0000000..275de35 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex @@ -0,0 +1,21 @@ +
    +
    + <%= label @f, "Include nightly builds" %> +
    +
    +
    + <%= radio_button @f, :nightly_builds, false, checked: true, class: "form-check-input nightly-builds-false" %> +
    + <%= label :nightly_builds, :false, gettext("No"), class: "radio-text" %> +
    +
    + <%= radio_button @f, :nightly_builds, true, class: "form-check-input nightly-builds-true", "aria-describedby": "nightly_builds-help-block" %> +
    + <%= label :nightly_builds, :true, gettext("Yes"), class: "radio-text" %> +
    +
    + <%= error_tag @f, :nightly_builds, id: "nightly_builds-help-block", class: "text-danger form-error" %> +
    +
    Select yes if you want to show nightly builds.
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_libraries_other.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_libraries_other.html.eex new file mode 100644 index 0000000..7c6af84 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_libraries_other.html.eex @@ -0,0 +1,8 @@ +<%= for library_index <- 2..Application.get_env(:block_scout_web, :verification_max_libraries) do %> + <% library = "library" <> to_string(library_index) |> String.to_atom() %> +
    + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_library_name.html", library: library, index: library_index %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_library_address.html", library: library, index: library_index %> +
    +<% end %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_address.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_address.html.eex new file mode 100644 index 0000000..e86bb67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_address.html.eex @@ -0,0 +1,10 @@ +<% library_address = "library" <> to_string(@index) <> "_address" |> String.to_atom() %> +
    +
    + <%= label :external_libraries, @library, gettext("Library") <> " " <> to_string(@index) <> " " <> gettext("Address") %> +
    + <%= text_input :external_libraries, library_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
    +
    <%= if assigns[:tooltip_text] do @tooltip_text end %>
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_first.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_first.html.eex new file mode 100644 index 0000000..995d86c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_first.html.eex @@ -0,0 +1,13 @@ +
    + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_library_name.html", + library: :library1, + index: 1, + tooltip_text: gettext("A library name called in the .sol file. Multiple libraries (up to ") <> to_string(Application.get_env(:block_scout_web, :verification_max_libraries)) <> gettext(") may be added for each contract. Click the Add Library button to add an additional one.") + %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_library_address.html", + library: :library1, + index: 1, + tooltip_text: gettext "The 0x library address. This can be found in the generated json file or Truffle output (if using truffle)." + %> +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_name.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_name.html.eex new file mode 100644 index 0000000..b1e0a7a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_common_fields/_library_name.html.eex @@ -0,0 +1,10 @@ +<% library_name = "library" <> to_string(@index) <> "_name" |> String.to_atom() %> +
    +
    + <%= label :external_libraries, @library, gettext("Library") <> " " <> to_string(@index) <> " " <> gettext("Name") %> +
    + <%= text_input :external_libraries, library_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
    +
    <%= if assigns[:tooltip_text] do @tooltip_text end %>
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex new file mode 100644 index 0000000..7728537 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex @@ -0,0 +1,119 @@ +<% metadata_for_verification = if assigns[:retrying], do: nil, else: Chain.get_address_verified_twin_contract(@address_hash).verified_contract %> +<% changeset = (if assigns[:retrying], do: @changeset, else: SmartContract.merge_twin_contract_with_changeset(metadata_for_verification, @changeset)) |> SmartContract.address_to_checksum_address() %> +<% fetch_constructor_arguments_automatically = if metadata_for_verification, do: true, else: changeset.changes.autodetect_constructor_args %> +<% display_constructor_arguments_text_area = if fetch_constructor_arguments_automatically, do: "none", else: "block" %> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost") %> + +
    +

    <%= gettext "New Solidity Smart Contract Verification" %>

    + + <%= form_for changeset, + address_contract_verification_path(@conn, :create), + [], + fn f -> %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", f: f %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_name_field.html", f: f, tooltip: gettext "Must match the name specified in the code. For example, in contract MyContract {..} MyContract is the contract name." %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_include_nightly_builds_field.html", f: f %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_compiler_field.html", f: f, compiler_versions: @compiler_versions, tooltip: gettext "The compiler version is specified in pragma solidity X.X.X. Use the compiler version rather than the nightly build. If using the Solidity compiler, run solc —version to check." %> + +
    +
    + <%= label :evm_version, :evm_version, gettext("EVM Version") %> +
    + <%= select f, :evm_version, @evm_versions, class: "form-control border-rounded", "aria-describedby": "evm-version-help-block" %> +
    +
    <%= gettext "The EVM version the contract is written for. If the bytecode does not match the version, we try to verify using the latest EVM version." %> <%= gettext "EVM version details" %>.
    +
    +
    + +
    +
    + <%= label f, "Optimization" %> +
    +
    +
    + <%= radio_button f, :optimization, false, class: "form-check-input optimization-false" %> +
    + <%= label :smart_contract_optimization, :false, gettext("No"), class: "radio-text" %> +
    +
    + <%= radio_button f, :optimization, true, class: "form-check-input optimization-true", "aria-describedby": "optimization-help-block" %> +
    + <%= label :smart_contract_optimization, :true, gettext("Yes"), class: "radio-text" %> +
    +
    + <%= error_tag f, :optimization, id: "optimization-help-block", class: "text-danger form-error" %> +
    +
    <%= gettext "If you enabled optimization during compilation, select yes." %>
    +
    +
    + +
    "> +
    + <%= label f, :name, gettext("Optimization runs") %> +
    + <%= text_input f, :optimization_runs, class: "form-control border-rounded", "aria-describedby": "optimization-runs-help-block", "data-test": "optimization-runs" %> +
    +
    +
    +
    + +
    +
    + <%= label f, :contract_source_code, gettext("Enter the Solidity Contract Code") %> +
    + <%= textarea f, :contract_source_code, class: "form-control border-rounded monospace", rows: 3, "aria-describedby": "contract-source-code-help-block" %> + <%= error_tag f, :contract_source_code, id: "contract-source-code-help-block", class: "text-danger form-error", "data-test": "contract-source-code-error" %> +
    +
    <%= gettext "We recommend using flattened code. This is necessary if your code utilizes a library or inherits dependencies. Use the" %> <%= gettext "POA solidity flattener or the" %> <%= gettext "truffle flattener" %>.
    +
    +
    + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_fetch_constructor_args.html", f: f %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_constructor_args.html", f: f, display_constructor_arguments_text_area: display_constructor_arguments_text_area %> + +
    + <%= gettext "Add Contract Libraries" %> +
    + +
    +

    <%= gettext "Contract Libraries" %>

    + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_library_first.html" %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_libraries_other.html" %> + +
    + <%= gettext "Add Library" %> +
    +
    + +
    + + <%= submit gettext("Verify & publish"), class: "btn-full-primary mr-2", "data-button-loading": "animation", "data-submit-button": "" %> + <%= reset gettext("Reset"), class: "btn-line mr-2 js-smart-contract-form-reset" %> + <%= + link( + gettext("Cancel"), + class: "btn-no-border", + to: address_contract_path(@conn, :index, @address_hash) + ) + %> +
    + <% end %> +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex new file mode 100644 index 0000000..f2bdbff --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex @@ -0,0 +1,49 @@ +
    + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost") %> + +
    +

    <%= gettext "New Smart Contract Verification" %>

    + <%= form_for @changeset, + address_contract_verification_path(@conn, :create), + [id: "metadata-json-dropzone-form"], + fn f -> %> + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", f: f %> + +
    +
    + +
    +
    +
    + <%= gettext("Drop sources and metadata JSON file or click here") %> + <%= error_tag f, :file, id: "file-help-block", class: "text-danger form-error", style: "max-width: 600px;" %> +
    +
    +
    +
    Drop all Solidity contract source files and JSON metadata file(s) created during contract compilation into the drop zone.
    +
    +
    + +
    + + + <%= reset gettext("Reset"), class: "btn-line mr-2 js-smart-contract-form-reset" %> + <%= + link( + gettext("Cancel"), + class: "btn-no-border", + to: address_contract_path(@conn, :index, @address_hash) + ) + %> +
    + <% end %> +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex new file mode 100644 index 0000000..1cee4b5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex @@ -0,0 +1,115 @@ +<% metadata_for_verification = if assigns[:retrying], do: nil, else: Chain.get_address_verified_twin_contract(@address_hash).verified_contract %> +<% changeset = (if assigns[:retrying], do: @changeset, else: SmartContract.merge_twin_contract_with_changeset(metadata_for_verification, @changeset)) |> SmartContract.address_to_checksum_address() %> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost") %> + +
    +

    <%= gettext "New Solidity Smart Contract Verification" %>

    + + <%= form_for changeset, + address_contract_verification_path(@conn, :create), + [id: "multi-part-dropzone-form"], + fn f -> %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", f: f %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_include_nightly_builds_field.html", f: f %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_compiler_field.html", f: f, compiler_versions: @compiler_versions, tooltip: gettext "The compiler version is specified in pragma solidity X.X.X. Use the compiler version rather than the nightly build. If using the Solidity compiler, run solc —version to check." %> + +
    +
    + <%= label :evm_version, :evm_version, gettext("EVM Version") %> +
    + <%= select f, :evm_version, @evm_versions, class: "form-control border-rounded", "aria-describedby": "evm-version-help-block" %> +
    +
    <%= gettext "The EVM version the contract is written for. If the bytecode does not match the version, we try to verify using the latest EVM version." %> <%= gettext "EVM version details" %>.
    +
    +
    + +
    +
    + <%= label f, "Optimization" %> +
    +
    +
    + <%= radio_button f, :optimization, false, class: "form-check-input optimization-false" %> +
    + <%= label :smart_contract_optimization, :false, gettext("No"), class: "radio-text" %> +
    +
    + <%= radio_button f, :optimization, true, class: "form-check-input optimization-true", "aria-describedby": "optimization-help-block" %> +
    + <%= label :smart_contract_optimization, :true, gettext("Yes"), class: "radio-text" %> +
    +
    + <%= error_tag f, :optimization, id: "optimization-help-block", class: "text-danger form-error" %> +
    +
    <%= gettext "If you enabled optimization during compilation, select yes." %>
    +
    +
    + +
    "> +
    + <%= label f, :name, gettext("Optimization runs") %> +
    + <%= text_input f, :optimization_runs, class: "form-control border-rounded", "aria-describedby": "optimization-runs-help-block", "data-test": "optimization-runs" %> +
    +
    +
    +
    + +
    + +
    +
    +
    + <%= gettext("Drop sources or click here") %> + <%= error_tag f, :file, id: "file-help-block", class: "text-danger form-error", style: "max-width: 600px;" %> +
    +
    +
    +
    <%= gettext "Drop all Solidity contract source files into the drop zone." %>
    +
    + +
    + <%= gettext "Add Contract Libraries" %> +
    + +
    +

    <%= gettext "Contract Libraries" %>

    + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_library_first.html" %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_libraries_other.html" %> + +
    + <%= gettext "Add Library" %> +
    +
    + +
    + + <%#= submit gettext("Verify & publish"), class: "btn-full-primary mr-2", "data-button-loading": "animation", "data-submit-button": "" %> + <%# verify-via-multi-part-files-submit %> + + <%= reset gettext("Reset"), class: "btn-line mr-2 js-smart-contract-form-reset" %> + <%= + link( + gettext("Cancel"), + class: "btn-no-border", + to: address_contract_path(@conn, :index, @address_hash) + ) + %> +
    + <% end %> +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex new file mode 100644 index 0000000..e86c158 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex @@ -0,0 +1,63 @@ +<% metadata_for_verification = Chain.get_address_verified_twin_contract(@address_hash).verified_contract %> +<% changeset = (if assigns[:retrying], do: @changeset, else: SmartContract.merge_twin_contract_with_changeset(metadata_for_verification, @changeset)) |> SmartContract.address_to_checksum_address() %> +<% fetch_constructor_arguments_automatically = if metadata_for_verification, do: true, else: changeset.changes.autodetect_constructor_args %> +<% display_constructor_arguments_text_area = if fetch_constructor_arguments_automatically, do: "none", else: "block" %> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost") %> + +
    +

    <%= gettext "New Smart Contract Verification" %>

    + <%= form_for changeset, + address_contract_verification_path(@conn, :create), + [id: "standard-json-dropzone-form"], + fn f -> %> + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", f: f %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_name_field.html", f: f, tooltip: "Must match the name specified in the code. For example, in contract MyContract {..} MyContract is the contract name. Also contract name could be: path/to/file.sol:MyContract" %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_include_nightly_builds_field.html", f: f %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_compiler_field.html", f: f, compiler_versions: @compiler_versions, tooltip: "The compiler version is specified in pragma solidity X.X.X. Use the compiler version rather than the nightly build. If using the Solidity compiler, run solc —version to check." %> + +
    +
    + +
    +
    +
    + <%= gettext("Drop the standard input JSON file or click here") %> + <%= error_tag f, :file, id: "file-help-block", class: "text-danger form-error", style: "max-width: 600px;" %> +
    +
    +
    +
    Drop the standard input JSON file created during contract compilation into the drop zone.
    +
    +
    + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_fetch_constructor_args.html", f: f %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_constructor_args.html", f: f, display_constructor_arguments_text_area: display_constructor_arguments_text_area %> + +
    + + + <%= reset gettext("Reset"), class: "btn-line mr-2 js-smart-contract-form-reset" %> + <%= + link( + gettext("Cancel"), + class: "btn-no-border", + to: address_contract_path(@conn, :index, @address_hash) + ) + %> +
    + <% end %> +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex new file mode 100644 index 0000000..4b07896 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex @@ -0,0 +1,59 @@ +<% metadata_for_verification = Chain.get_address_verified_twin_contract(@address_hash).verified_contract %> +<% changeset = (if assigns[:retrying], do: @changeset, else: SmartContract.merge_twin_vyper_contract_with_changeset(metadata_for_verification, @changeset)) |> SmartContract.address_to_checksum_address() %> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost") %> + +
    +

    <%= gettext "New Vyper Smart Contract Verification" %>

    + + <%= form_for changeset, + address_contract_verification_path(@conn, :create), + [], + fn f -> %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_address_field.html", f: f %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_contract_name_field.html", f: f, tooltip: "Must match the name specified in the code." %> + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_compiler_field.html", f: f, compiler_versions: @compiler_versions, tooltip: "" %> + +
    +
    + <%= label f, :contract_source_code, gettext("Enter the Vyper Contract Code") %> +
    + <%= textarea f, :contract_source_code, class: "form-control border-rounded monospace", rows: 3, "aria-describedby": "contract-source-code-help-block" %> + <%= error_tag f, :contract_source_code, id: "contract-source-code-help-block", class: "text-danger form-error", "data-test": "contract-source-code-error" %> +
    +
    +
    +
    + + <%= render BlockScoutWeb.AddressContractVerificationCommonFieldsView, "_constructor_args.html", f: f, display_constructor_arguments_text_area: "block" %> + +
    + +
    + +
    + + <%= submit gettext("Verify & publish"), class: "btn-full-primary mr-2", "data-button-loading": "animation", "data-submit-button": "" %> + <%= reset gettext("Reset"), class: "btn-line mr-2 js-smart-contract-form-reset" %> + <%= + link( + gettext("Cancel"), + class: "btn-no-border", + to: address_contract_path(@conn, :index, @address_hash) + ) + %> +
    + <% end %> +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/_metatags.html.eex new file mode 100644 index 0000000..3ef2a67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex new file mode 100644 index 0000000..18abbbc --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex @@ -0,0 +1,33 @@ +
    + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> + <% contract = last_decompiled_contract_version(@address.decompiled_smart_contracts) %> + <%= if contract do %> +
    +

    <%= gettext "Decompiler version" %>

    +
    +
    <%= contract.decompiler_version %>
    +
    +
    +
    +
    +

    <%= gettext "Decompiled contract code" %>

    + +
    +
    +
    <%= raw(highlight_decompiled_code(contract.decompiled_source_code)) %>
    +
    +
    +
    + <% else %> +
    + <%= gettext "There is no decompilded contracts for this address." %> +
    + <% end %> +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/_metatags.html.eex new file mode 100644 index 0000000..3ef2a67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex new file mode 100644 index 0000000..feb7011 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex @@ -0,0 +1,72 @@ +
    + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> + +
    +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost, click to load newer internal transactions") %> +
    +

    <%= gettext "Internal Transactions" %>

    +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    +
    + + +
    +
    + <%= gettext "There are no internal transactions for this address." %> +
    +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_csv_export_button.html", address: Address.checksum(@address.hash), type: "internal-transactions", conn: @conn %> + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + +
    +
    + + +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex new file mode 100644 index 0000000..eaa2d7d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex @@ -0,0 +1,111 @@ +
    "> + <% decoded_result = decode(@log, @log.transaction) %> + <%= case decoded_result do %> + <% {:error, :contract_not_verified, _cadidates} -> %> +
    + <%= gettext "To see accurate decoded input data, the contract must be verified." %> + <%= case @log.transaction do %> + <% %{to_address: %{hash: hash}} -> %> + <% path = address_verify_contract_path(@conn, :new, hash) %> + <%= gettext "Verify the contract " %><%= gettext "here" %> + <% _ -> %> + <%= nil %> + <% end %> +
    + <% _ -> %> + <%= nil %> + <% end %> +
    +
    <%= gettext "Transaction" %>
    +
    +

    + <%= link( + @log.transaction, + to: transaction_path(@conn, :show, @log.transaction), + "data-test": "log_address_link", + "data-address-hash": @log.transaction + ) %> +

    +
    + <%= case decoded_result do %> + <% {:error, :could_not_decode} -> %> +
    <%= gettext "Decoded" %>
    +
    +
    + <%= gettext "Failed to decode log data." %> +
    + <% {:ok, method_id, text, mapping} -> %> +
    <%= gettext "Decoded" %>
    +
    + + + + + + + + + +
    Method Id0x<%= method_id %>
    Call<%= text %>
    + <%= render BlockScoutWeb.LogView, "_data_decoded_view.html", mapping: mapping %> + <% {:error, :contract_not_verified, results} -> %> + <%= for {:ok, method_id, text, mapping} <- results do %> +
    <%= gettext "Decoded" %>
    +
    + + + + + + + + + +
    Method Id0x<%= method_id %>
    Call<%= text %>
    + <%= render BlockScoutWeb.LogView, "_data_decoded_view.html", mapping: mapping %> +
    + <% end %> + <% _ -> %> + <%= nil %> + <% end %> +
    <%= gettext "Topics" %>
    +
    +
    + <%= unless is_nil(@log.first_topic) do %> +
    + [0] + <%= @log.first_topic %> +
    + <% end %> + <%= unless is_nil(@log.second_topic) do %> +
    + [1] + <%= @log.second_topic %> +
    + <% end %> + <%= unless is_nil(@log.third_topic) do %> +
    + [2] + <%= @log.third_topic %> +
    + <% end %> + <%= unless is_nil(@log.fourth_topic) do %> +
    + [3] + <%= @log.fourth_topic %> +
    + <% end %> +
    +
    +
    + <%= gettext "Data" %> +
    +
    + <%= unless is_nil(@log.data) do %> +
    + <%= @log.data %> +
    + <% end %> +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex new file mode 100644 index 0000000..3dc20d3 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex @@ -0,0 +1,46 @@ +
    + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> +
    +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> + +
    +

    <%= gettext "Logs" %>

    +
    + + + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + + + +
    +
    + <%= gettext "There are no logs for this address." %> +
    +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_csv_export_button.html", address: Address.checksum(@address.hash), type: "logs", conn: @conn %> + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + +
    + +
    + +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_metatags.html.eex new file mode 100644 index 0000000..3ef2a67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex new file mode 100644 index 0000000..58ff9c9 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex @@ -0,0 +1,58 @@ +
    + + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> + +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> + <%= if @need_wallet do %> +
    + <%= render BlockScoutWeb.SmartContractView, "_connect_container.html" %> +
    + <% end %> + <%= if @non_custom_abi && assigns[:custom_abi] do %> + + <% else %> + <%= if assigns[:custom_abi] do %> +

    <%= gettext "Custom ABI from account" %>

    + <% end %> + <% end %> + <%= + for status <- ["error", "warning", "success", "question"] do + render BlockScoutWeb.CommonComponentsView, "_modal_status.html", status: status + end + %> + <%= render BlockScoutWeb.SmartContractView, "_pending_contract_write.html" %> + <%= if @non_custom_abi && assigns[:custom_abi] do %> +
    + <% end %> + <%= if @non_custom_abi do %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    + <% end %> + <%= if assigns[:custom_abi] do %> + +
    " id="custom" role="tabpanel" aria-labelledby="custom-tab" data-smart-contract-functions-custom data-hash="<%= to_string(@address.hash) %>" data-type="<%= @type %>" data-action="<%= @action %>" data-url="<%= smart_contract_path(@conn, :index) %>"> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    + <% end %> + <%= if @non_custom_abi && assigns[:custom_abi] do %> +
    + <% end %> +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/_metatags.html.eex new file mode 100644 index 0000000..3ef2a67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex new file mode 100644 index 0000000..a3577a7 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_read_proxy/index.html.eex @@ -0,0 +1,17 @@ +
    + + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> + +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token/_metatags.html.eex new file mode 100644 index 0000000..3ef2a67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token/_tokens.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token/_tokens.html.eex new file mode 100644 index 0000000..7691bb3 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token/_tokens.html.eex @@ -0,0 +1,58 @@ + + + + + <%= if System.get_env("DISPLAY_TOKEN_ICONS") === "true" do %> + <% chain_id_for_token_icon = Application.get_env(:block_scout_web, :chain_id) %> + <% address_hash = @token.contract_address_hash %> + <%= + render BlockScoutWeb.TokensView, + "_token_icon.html", + chain_id: chain_id_for_token_icon, + address: Address.checksum(address_hash) + %> + <% end %> + + + <%= link( + to: address_token_transfers_path(@conn, :index, to_string(@address.hash), to_string(@token.contract_address_hash)), + class: "tile-title-lg", + "data-test": "token_transfers_#{@token.contract_address_hash}" + ) do %> + <%= token_name(@token) %> + <% end %> + + + <%= @token.type %> + + + <%= format_according_to_decimals(@token_balance.value, @token.decimals) %> + + + <%= @token.symbol %> + + +

    + <% token_price = if @token.usd_value, do: @token.usd_value, else: nil %> + <%= ChainView.format_currency_value(token_price, "@") %> +

    + + + <%= if @token.usd_value do %> +

    + <%= ChainView.format_usd_value(Chain.balance_in_usd(@token_balance, @token)) %> +

    + <% end %> + + + <%= with {:ok, address} <- Chain.hash_to_address(@token.contract_address_hash) do + render BlockScoutWeb.AddressView, + "_link.html", + address: address, + contract: false, + use_custom_tooltip: false, + no_tooltip: true + end + %> + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex new file mode 100644 index 0000000..1fd8115 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex @@ -0,0 +1,75 @@ +
    + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> + +
    +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> +
    + <%= render BlockScoutWeb.AddressTokenView, + "overview.html", + address: @address, + exchange_rate: @exchange_rate + %> + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + +
    + + + + + + + + + + + + + + + + <%= render BlockScoutWeb.CommonComponentsView, "_table-loader.html", columns_num: 9 %> + +
    +
     
    +
    +
     
    +
    +
    Asset
    +
    +
    Type
    +
    +
    Amount
    +
    +
    Symbol
    +
    +
    Price
    +
    +
    Value
    +
    +
    Contract Address
    +
    +
    + + + +
    +
    + <%= gettext "There are no tokens for this address." %> +
    +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    +
    + +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview.html.eex new file mode 100644 index 0000000..6f92237 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview.html.eex @@ -0,0 +1,73 @@ +<% native_coin = Explorer.coin_name() %> + +<% wei_value = if @address.fetched_coin_balance, do: @address.fetched_coin_balance.value %> +<% raw_usd_value = + case @exchange_rate.usd_value do + %Decimal{} -> + if wei_value do + %Wei{value: Decimal.new(wei_value)} + |> Wei.to(:ether) + |> Decimal.mult(@exchange_rate.usd_value) + else + Decimal.new(0) + end + _ -> Decimal.new(0) + end +%> +<% data_usd_exchange_rate = + unless AddressView.empty_exchange_rate?(@exchange_rate) do + "data-usd-exchange-rate='#{@exchange_rate.usd_value}' data-raw-usd-value='#{raw_usd_value}'" + end +%> +<% native_coin_balance_token = AddressView.balance(@address) +%> +<% native_coin_balance_usd = + if AddressView.empty_exchange_rate?(@exchange_rate) do + nil + else + " + " + end +%> +<% native_coin_balance = + if native_coin_balance_usd do + native_coin_balance_usd <> " | " <> native_coin_balance_token + else + native_coin_balance_token + end +%> +
    + <%= render BlockScoutWeb.AddressTokenView, "overview_item.html", + title: gettext("Net Worth"), + tooltip: gettext("Shows total assets held in the address"), + value: "N/A", + data_test: "address-tokens-panel-net-worth", + classes: ["fs-14"] + %> + <%= render BlockScoutWeb.AddressTokenView, "overview_item.html", + title: "#{native_coin} #{gettext("Balance")}", + tooltip: "#{gettext("Shows the current")} #{native_coin} #{gettext("balance of the address")}", + value: raw(native_coin_balance), + data_test: "address-tokens-panel-native-worth", + classes: ["fs-14"] + %> + <%= render BlockScoutWeb.AddressTokenView, "overview_item.html", + title: gettext("Tokens"), + tooltip: gettext("Shows the tokens held in the address (includes ERC-20, ERC-721 and ERC-1155)."), + value: "N/A", + data_test: "address-tokens-panel-tokens-worth", + classes: ["fs-14"] + %> + <%= render BlockScoutWeb.AddressTokenView, "overview_item.html", + title: gettext("CRC Worth"), + tooltip: gettext("Shows the total CRC balance in the address."), + value: "0 CRC", + data_test: "address-tokens-panel-crc-total-worth", + data_test_container: "address-tokens-panel-crc-total-worth-container", + classes: ["fs-14"], + container_classes: ["d-none"] + %> +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview_item.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview_item.html.eex new file mode 100644 index 0000000..c038254 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token/overview_item.html.eex @@ -0,0 +1,14 @@ +
    " data-test="<%= if assigns[:data_test_container], do: @data_test_container %>" style="padding: 10px;"> +
    +
    +

    <%= @title %>

    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip.html", + text: @tooltip, + additional_classes: ["ml-2"] + %> +
    +
    ""> + <%= @value %> +
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex new file mode 100644 index 0000000..9b3f6a7 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex @@ -0,0 +1,78 @@ +
    + <%= if Enum.any?(@token_balances) do %> + +
    + (>) +
    + <%= if @conn.request_path |> String.contains?("/tokens") do %> + + + + + + <% else %> + + + + + + <% end %> + <% else %> + <%= tokens_count_title(@token_balances) %> + <% end %> + + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_tokens.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_tokens.html.eex new file mode 100644 index 0000000..ceaf905 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_tokens.html.eex @@ -0,0 +1,65 @@ +
    + + + <%= for {token_balance, token} <- sort_by_usd_value_and_name(@token_balances) do %> +
    + <% path = cond do + token_balance.token_type == "ERC-721" && !is_nil(token_balance.token_id) -> token_instance_path(@conn, :show, token.contract_address_hash, to_string(token_balance.token_id)) + token_balance.token_type == "ERC-1155" && !is_nil(token_balance.token_id) -> token_instance_path(@conn, :show, token.contract_address_hash, to_string(token_balance.token_id)) + true -> token_path(@conn, :show, to_string(token.contract_address_hash)) + end + %> + <%= link( + to: path, + class: "dropdown-item" + ) do %> + + + + <% end %> +
    + <% end %> +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/_metatags.html.eex new file mode 100644 index 0000000..3ef2a67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex new file mode 100644 index 0000000..94f202d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex @@ -0,0 +1,76 @@ +
    + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> + +
    +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> +
    + + <%= if assigns[:token] do %> +

    + <%= gettext "Tokens" %> / <%= token_name(@token) %> +

    + <% end %> + + <%= if !assigns[:token] do %> +
    +

    <%= gettext "Token Transfers" %>

    +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    +
    + <% end %> + + + + + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_csv_export_button.html", address: Address.checksum(@address.hash), type: "token-transfers", conn: @conn %> + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + +
    +
    + + +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/_metatags.html.eex new file mode 100644 index 0000000..3ef2a67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex new file mode 100644 index 0000000..a5b5786 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex @@ -0,0 +1,72 @@ +
    + + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> + +
    +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost, click to load newer transactions") %> +
    +

    <%= gettext "Transactions" %>

    +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    +
    + + + +
    +
    + <%= gettext "There are no transactions for this address." %> +
    +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_csv_export_button.html", address: Address.checksum(@address.hash), type: "transactions", conn: @conn %> + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + +
    +
    + +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_validation/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/_metatags.html.eex new file mode 100644 index 0000000..3ef2a67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex new file mode 100644 index 0000000..e435195 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex @@ -0,0 +1,33 @@ +
    + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> + +
    +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost, click to load newer validations") %> +

    <%=gettext("Blocks Validated")%>

    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + + + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    +
    + +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_metatags.html.eex new file mode 100644 index 0000000..3ef2a67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex new file mode 100644 index 0000000..7febeee --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_write_contract/index.html.eex @@ -0,0 +1,57 @@ +
    + + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> + +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> +
    + <%= render BlockScoutWeb.SmartContractView, "_connect_container.html" %> +
    + <%= if @non_custom_abi && assigns[:custom_abi] do %> + + <% else %> + <%= if assigns[:custom_abi] do %> +

    <%= gettext "Custom ABI from account" %>

    + <% end %> + <% end %> + <%= + for status <- ["error", "warning", "success", "question"] do + render BlockScoutWeb.CommonComponentsView, "_modal_status.html", status: status + end + %> + <%= render BlockScoutWeb.SmartContractView, "_pending_contract_write.html" %> + <%= if @non_custom_abi && assigns[:custom_abi] do %> +
    + <% end %> + <%= if @non_custom_abi do %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    + <% end %> + <%= if assigns[:custom_abi] do %> + +
    " id="custom" role="tabpanel" aria-labelledby="custom-tab" data-smart-contract-functions-custom data-hash="<%= to_string(@address.hash) %>" data-type="<%= @type %>" data-action="<%= @action %>" data-url="<%= smart_contract_path(@conn, :index) %>"> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    + <% end %> + <%= if @non_custom_abi && assigns[:custom_abi] do %> +
    + <% end %> +
    + + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/_metatags.html.eex new file mode 100644 index 0000000..3ef2a67 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.AddressView, "_metatags.html", conn: @conn, address: @address %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex new file mode 100644 index 0000000..a3577a7 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_write_proxy/index.html.eex @@ -0,0 +1,17 @@ +
    + + <% is_proxy = BlockScoutWeb.AddressView.smart_contract_is_proxy?(@address) %> + + <%= render BlockScoutWeb.AddressView, "overview.html", address: @address, is_proxy: is_proxy, conn: @conn, exchange_rate: @exchange_rate, coin_balance_status: @coin_balance_status, counters_path: @counters_path, tags: @tags %> + +
    + <%= render BlockScoutWeb.AddressView, "_tabs.html", address: @address, is_proxy: is_proxy, conn: @conn %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/admin/dashboard/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/admin/dashboard/index.html.eex new file mode 100644 index 0000000..77140e5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/admin/dashboard/index.html.eex @@ -0,0 +1,37 @@ +
    +
    +
    +

    Tasks

    +
    + +
    +
    +
    +
    +
    +

    Create Contract Methods

    +
    +
    +

    + <%= gettext("For any existing contracts in the database, insert all ABI entries into the contract_methods table. Use this in case you have verified smart contracts before early March 2019 and you want other contracts with the same functions to show those ABI's as candidate matches.") %> +

    +
    + + +
    +
    +
    +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/admin/session/login_form.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/admin/session/login_form.html.eex new file mode 100644 index 0000000..b2ed066 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/admin/session/login_form.html.eex @@ -0,0 +1,17 @@ +
    +
    +
    +
    +
    +

    Administrator Login

    + + <%= form_for @changeset, session_path(@conn, :create), [], fn f -> %> + <%= FormView.text_field(f, :username, :text, id: "username", required: true, label: "Username") %> + <%= FormView.text_field(f, :password, :password, id: "password", required: true, label: "Password") %> + + <% end %> +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/admin/setup/admin_registration.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/admin/setup/admin_registration.html.eex new file mode 100644 index 0000000..6019a0a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/admin/setup/admin_registration.html.eex @@ -0,0 +1,19 @@ +
    +
    +
    +
    +
    +

    Administrator Setup

    + + <%= form_for @changeset, setup_path(@conn, :configure_admin, %{state: @conn.query_params["state"]}), [], fn f -> %> + <%= FormView.text_field(f, :username, :text, id: "username", required: true, label: "Username") %> + <%= FormView.text_field(f, :email, :email, id: "email", required: true, label: "Email Address") %> + <%= FormView.text_field(f, :password, :password, id: "password", required: true, label: "Password") %> + <%= FormView.text_field(f, :password_confirmation, :password, id: "password-confirmation", required: true, label: "Password Confirmation") %> + + <% end %> +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/admin/setup/verify.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/admin/setup/verify.html.eex new file mode 100644 index 0000000..12ba89c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/admin/setup/verify.html.eex @@ -0,0 +1,25 @@ +
    +
    +
    +
    +
    +

    Administrator Setup

    + +

    You have not setup the administrator account for BlockScout. Run the following command in your terminal at the root directory of where your application is deployed. Paste the value inside of the Recovery Key field. Do not share this key!

    + +
    +
    pbcopy < apps/explorer/priv/.recovery
    +
    + <%= form_for @conn, setup_path(@conn, :configure_admin), [as: "verify"], fn f -> %> +
    + + <%= password_input(f, :recovery_key, class: "form-control", type: "password", id: "recovery-key", placeholder: "JRAJpuEGNKM1XQK3zpWMdAAHVzQtJfDyW/sN/Zn1Ev8=", required: true) %> +
    + + + <% end %> +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/advertisement/banners_ad/_banner_728.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/advertisement/banners_ad/_banner_728.html.eex new file mode 100644 index 0000000..20c47d2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/advertisement/banners_ad/_banner_728.html.eex @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/advertisement/text_ad/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/advertisement/text_ad/index.html.eex new file mode 100644 index 0000000..c8dc85c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/advertisement/text_ad/index.html.eex @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_action_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_action_tile.html.eex new file mode 100644 index 0000000..240294c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_action_tile.html.eex @@ -0,0 +1,259 @@ +
    + + +
    +

    + <%= gettext "Parameters" %> + + +

    + +
    +
    +

    <%= gettext "Name" %>

    +

    <%= gettext "Description" %>

    +
    + +
    +
    +
    <%= gettext "Module" %> *<%= gettext "required" %>
    +

    <%= gettext "string" %> <%= gettext "(query)" %>

    +
    +
    +

    <%= gettext "A string with the name of the module to be invoked." %>

    +

    <%= gettext "Must be set to:" %> <%= @module_name %> +

    +
    + +
    +
    +
    <%= gettext "Action" %> *<%= gettext "required" %>
    +

    <%= gettext "string" %> <%= gettext "(query)" %>

    +
    +
    +

    <%= gettext "A string with the name of the action to be invoked." %>

    +

    <%= gettext "Must be set to:" %> <%= @action.name %>

    +
    +
    + + <%= for required_param <- @action.required_params do %> +
    +
    +
    <%= required_param.key %> *<%= gettext "required" %>
    +

    <%= required_param.type %> <%= gettext "(query)" %>

    +
    +
    +

    <%= required_param.description %>

    +
    + +
    +
    +
    + <% end %> + + <%= for optional_param <- @action.optional_params do %> +
    +
    +
    <%= optional_param.key %>
    +

    <%= optional_param.type %> <%= gettext "(query)" %>

    +
    +
    +

    <%= optional_param.description %>

    + +
    +
    + <% end %> + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    <%= gettext "Curl" %>
    +
    +
    
    +          
    +
    +
    +
    <%= gettext "Request URL" %>
    +
    +
    
    +          
    +
    +
    <%= gettext "Server Response" %>
    +
    +

    <%= gettext "Code" %>

    +

    <%= gettext "Details" %>

    +
    +
    +
    +
    +

    <%= gettext "Response Body" %>

    +
    +
    
    +            
    +
    +
    +
    +
    + +

    <%= gettext "Responses" %>

    +
    +

    <%= gettext "Code" %>

    +
    <%= gettext "Description" %>
    +
    + <%= for {response, index} <- Enum.with_index(@action.responses) do %> +
    +
    <%= response.code %>
    +
    +
    +
    <%= response.description %>
    +
    + + + +
    + +
    +
    +
    
    +            
    +
    + <%= if index == 0 do %> + +
    + <%= render "_model_table.html", model: response.model %> +
    + <% end %> +
    +
    +
    + <% end %> +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex new file mode 100644 index 0000000..3330a3a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex @@ -0,0 +1,182 @@ +
    +
    +
    +

    <%= @action %>

    +

    <%= raw @info.notes %>

    + + curl -X POST --data '{"id":0,"jsonrpc":"2.0","method": "<%= @action %>", "params": []}' + +

    +

    +
    
    +        
    +

    +
    + +
    + +
    +

    + <%= gettext "Parameters" %> + + +

    + +
    +
    +

    <%= gettext "Name" %>

    +

    <%= gettext "Description" %>

    +
    + + <%= for param <- @info.params do %> +
    +
    +
    + <%= param.name %> + <%= if param.required do %> + + *<%= gettext "required" %> + + <% end %> +
    +
    +
    +

    <%= param.description %>

    + " + data-parameter-type='<%= param.type %>' + data-required='<%= if param.required, do: "true", else: "false" %>' + data-selector='<%= "eth-#{@action}-try-api-ui" %>' + type="text", + value='<%= param.default %>' + /> +
    +
    + <% end %> + + +
    +
    + + +
    +
    + + +
    +
    +
    <%= gettext "Curl" %>
    +
    +
    
    +          
    +
    +
    <%= gettext "Server Response" %>
    +
    +

    <%= gettext "Code" %>

    +

    <%= gettext "Details" %>

    +
    +
    +
    +
    +

    <%= gettext "Response Body" %>

    +
    +
    
    +            
    +
    +
    +
    +
    + + +

    <%= gettext "Responses" %>

    +
    +

    <%= gettext "Code" %>

    +
    <%= gettext "Description" %>
    +
    +
    +
    200
    +
    +
    +
    successful operation
    +
    + + + +
    + +
    +
    +
    
    +            
    +
    +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_metatags.html.eex new file mode 100644 index 0000000..9264738 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_metatags.html.eex @@ -0,0 +1,5 @@ + + <%= gettext "API for the %{subnetwork} - BlockScout", subnetwork: LayoutView.subnetwork_title() %> + +"> + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_model_table.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_model_table.html.eex new file mode 100644 index 0000000..1452cc9 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_model_table.html.eex @@ -0,0 +1,65 @@ +
    +

    <%= @model.name %> {

    + <%= for {key, details} <- @model.fields do %> +
    +
    + <%= key %> +
    +
    + + <%= if details[:type] != "model", do: details.type %> + <%= if details[:definition] do %> + + <% end %> + + <%= if details[:type] == "model" do %> + + <%= details.model.name %> + + <% end %> + <%= if details[:type] == "array" do %> +
    + [<%= details.array_type.name %>] +
    + <% end %> + <%= if details[:enum] do %> +
    enum: <%= details.enum %>
    +
    +
    enum +
    interpretation +
    + <%= for {enum, interpretation} <- details[:enum_interpretation] do %> +
    +
    + "<%= enum %>" +
    +
    + <%= interpretation %> +
    +
    + <% end %> + <% end %> + <%= if details[:example] do %> +
    example: <%= details.example %>
    + <% end %> + <%= if details[:description] do %> +
    description: <%= details.description %>
    + <% end %> +
    + +
    + <% end %> +

    }

    +
    + +<%= if @model.fields[:result][:type] == "array" do %> + <%= render "_model_table.html", model: @model.fields[:result].array_type %> +<% end %> + +<%= if @model.fields[:result][:type] == "model" do %> + <%= render "_model_table.html", model: @model.fields[:result].model %> +<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_module_card.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_module_card.html.eex new file mode 100644 index 0000000..5def65b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_module_card.html.eex @@ -0,0 +1,12 @@ +
    +
    +

    + <%= "#{String.capitalize(@module.name)}" %> + ?module=<%= @module.name %> +

    +
    + <%= for action <- @module.actions do %> + <%= render "_action_tile.html", module_name: @module.name, action: action %> + <% end %> +
    + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_module_item.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_module_item.html.eex new file mode 100644 index 0000000..2224c2a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/_module_item.html.eex @@ -0,0 +1,4 @@ + + <%= "#{String.capitalize(@module.name)}" %> + ?module=<%= @module.name %> + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/eth_rpc.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/eth_rpc.html.eex new file mode 100644 index 0000000..ac80b13 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/eth_rpc.html.eex @@ -0,0 +1,25 @@ +
    +
    +
    +

    <%= gettext("ETH RPC API Documentation") %>

    +

    [ <%= gettext "Base URL:" %> <%= eth_rpc_api_url()%> ]

    +

    + <%= gettext "This API is provided to support some rpc methods in the exact format specified for ethereum nodes, which can be found " %> + + <%= gettext "here." %> + <%= gettext "This is useful to allow sending requests to blockscout without having to change anything about the request." %> + <%= gettext "However, in general, the" %> <%= link( + gettext("custom RPC"), + to: api_docs_path(@conn, :index) + ) %> <%= gettext " is recommended." %> + <%= gettext "Anything not in this list is not supported. Click on the method to be taken to the documentation for that method, and check the notes section for any potential differences." %> +

    +
    +
    +
    + <%= for {method, info} <- Map.to_list(@documentation) do %> + <%= render "_eth_rpc_item.html", action: method, info: info %> + <% end %> +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/api_docs/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/index.html.eex new file mode 100644 index 0000000..6f27e0b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/api_docs/index.html.eex @@ -0,0 +1,18 @@ +
    +
    +
    +

    <%= gettext("API Documentation") %>

    +

    [ <%= gettext "Base URL:" %> <%= api_url()%> ]

    +

    <%= gettext "This API is provided for developers transitioning their applications from Etherscan to BlockScout. It supports GET and POST requests." %>

    +
    +
    + <%= for module <- @documentation do %> + <%= render "_module_item.html", module: module %> + <% end %> +
    +
    + <%= for module <- @documentation do %> + <%= render "_module_card.html", module: module %> + <% end %> + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block/_link.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block/_link.html.eex new file mode 100644 index 0000000..d910c2a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/block/_link.html.eex @@ -0,0 +1,4 @@ +<%= link( + gettext("Block #%{number}", number: to_string(@block.number)), + to: block_path(BlockScoutWeb.Endpoint, :show, @block) +) %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block/_metatags.html.eex new file mode 100644 index 0000000..c83fffd --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/block/_metatags.html.eex @@ -0,0 +1,13 @@ +<%= if assigns[:block] do %> + + <%= gettext( + "Block %{block_number} - %{subnetwork} Explorer", + block_number: @block.number, + subnetwork: BlockScoutWeb.LayoutView.subnetwork_title() + ) %> + + "> + "> +<% else %> + <%= BlockScoutWeb.LayoutView.render("_default_title.html") %> +<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block/_tile.html.eex new file mode 100644 index 0000000..f41857e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/block/_tile.html.eex @@ -0,0 +1,82 @@ +<% burned_fee = if !is_nil(@block.base_fee_per_gas), do: Wei.mult(@block.base_fee_per_gas, BlockBurnedFeeCounter.fetch(@block.hash)), else: nil %> +<% priority_fee = if !is_nil(@block.base_fee_per_gas), do: BlockPriorityFeeCounter.fetch(@block.hash), else: nil %> +
    +
    +
    + <%= if @block_type == "Block" do %> + <%= link( + class: "tile-label", + to: block_path(BlockScoutWeb.Endpoint, :show, @block), + "data-selector": "block-number" + ) do %> + #<%= @block %> + <% end %> + <% else %> + <%= link( + class: "tile-label", + to: block_path(BlockScoutWeb.Endpoint, :show, @block.hash), + "data-selector": "block-number" + ) do %> + #<%= @block %> + <% end %> + <% end %> + <%= @block_type %> +
    +
    +
    + + + <%= ngettext("%{count} transaction", "%{count} transactions", Enum.count(@block.transactions)) %> + + <%= if @block.size do %> + + <%= Cldr.Unit.new!(:byte, @block.size) |> cldr_unit_to_string!() %> + <% end %> + + +
    + <%= if System.get_env("HIDE_BLOCK_MINER") !== "true" do %> +
    + + <%= gettext "Miner" %> + <%= render BlockScoutWeb.AddressView, + "_link.html", + address: @block.miner, + contract: false, + use_custom_tooltip: false %> +
    + <% end %> + <%= if show_reward?(@block.rewards) do %> +
    + + <%= gettext "Reward" %> + + <%= combined_rewards_value(@block) %> + +
    + <% end %> +
    +
    + <%= if !is_nil(@block.base_fee_per_gas) do %> + + <%= format_wei_value(%Wei{value: priority_fee}, :ether) %> <%= gettext "Priority Fees" %> + + <%= format_wei_value(burned_fee, :ether) %> <%= gettext "Burnt Fees" %> + <% end %> + + <%= formatted_gas(@block.gas_limit) %> <%= gettext "Gas Limit" %> + +
    + <%= formatted_gas(@block.gas_used) %> + <% gas = if Decimal.compare(@block.gas_limit, 0) == :gt, do: Decimal.to_integer(@block.gas_used) / Decimal.to_integer(@block.gas_limit), else: 0 %> + (<%= formatted_gas(gas, format: "#.#%") %>) + <%= gettext "Gas Used" %> +
    + +
    +
    ;" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100"> +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block/index.html.eex new file mode 100644 index 0000000..dcee144 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/block/index.html.eex @@ -0,0 +1,25 @@ +
    + <%= render BlockScoutWeb.Advertisement.TextAdView, "index.html", conn: @conn %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost, click to load newer blocks") %> + +

    <%= gettext("%{block_type}s", block_type: @block_type) %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    +
    + <%= gettext "There are no blocks." %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex new file mode 100644 index 0000000..500e35a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex @@ -0,0 +1,271 @@ +<% burned_fee = if !is_nil(@block.base_fee_per_gas), do: Wei.mult(@block.base_fee_per_gas, BlockBurnedFeeCounter.fetch(@block.hash)), else: nil %> +<% priority_fee = if !is_nil(@block.base_fee_per_gas), do: BlockPriorityFeeCounter.fetch(@block.hash), else: nil %> +
    + <%= render BlockScoutWeb.Advertisement.TextAdView, "index.html", conn: @conn %> +
    +
    + +
    +
    +
    +

    + <%= gettext("%{block_type} Details", block_type: block_type(@block)) %> +

    + +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("The block height of a particular block is defined as the number of blocks preceding it in the blockchain.") %> + <%= if block_type(@block) == "Block" do %> + <%= gettext("Block Height") %> + <% else %> + <%= gettext("%{block_type} Height", block_type: block_type(@block)) %> + <% end %> +
    +
    + <%= if block_type(@block) == "Block" do %> + <%= @block.number %> <%= if @block.number == 0, do: " - " <> gettext("Genesis Block")%> + <% else %> + <%= link(@block, to: block_path(BlockScoutWeb.Endpoint, :show, @block.number)) %> + <% end %> +
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Date & time at which block was produced.") %> + <%= gettext("Timestamp") %> +
    +
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("The number of transactions in the block.") %> + <%= gettext("Transactions") %> +
    +
    + + <%= if @block_transaction_count == 1 do %> + <%= gettext "%{count} Transaction", count: @block_transaction_count %> + <% else %> + <%= gettext "%{count} Transactions", count: @block_transaction_count %> + <% end %> + +
    +
    + + <%= if System.get_env("HIDE_BLOCK_MINER") !== "true" do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("A block producer who successfully included the block onto the blockchain.") %> + <%= gettext("Miner") %> +
    +
    <%= render BlockScoutWeb.AddressView, "_link.html", address: @block.miner, contract: false, class: "", use_custom_tooltip: false, show_full_hash: true %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], + clipboard_text: @block.miner, + aria_label: gettext("Copy Address"), + title: gettext("Copy Address") %> +
    +
    + <% end %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Size of the block in bytes.") %> + <%= gettext("Size") %> +
    +
    <%= if !is_nil(@block.size), do: (Cldr.Unit.new!(:byte, @block.size) |> cldr_unit_to_string!()), else: gettext("N/A bytes") %>
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("The SHA256 hash of the block.") %> + <%= gettext("Hash") %> +
    +
    <%= to_string(@block.hash) %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], + clipboard_text: to_string(@block.hash), + aria_label: gettext("Copy Hash"), + title: gettext("Copy Hash") %> +
    +
    + <%= unless @block.number == 0 do %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("The hash of the block from which this block was generated.") %> + <%= gettext("Parent Hash") %> +
    +
    <%= link( + @block.parent_hash, + class: "transaction__link", + to: block_path(@conn, :show, @block.number - 1) + ) %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], + clipboard_text: to_string(@block.parent_hash), + aria_label: gettext("Copy Parent Hash"), + title: gettext("Copy Parent Hash") %> +
    +
    + <% end %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Block difficulty for miner, used to calibrate block generation time (Note: constant in POA based networks).") %> + <%= gettext("Difficulty") %> +
    +
    <%= @block.difficulty |> Decimal.to_integer() |> BlockScoutWeb.Cldr.Number.to_string! %>
    +
    + <%= if block_type(@block) == "Block" do %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Total difficulty of the chain until this block.") %> + <%= gettext("Total Difficulty") %> +
    +
    <%= @block.total_difficulty |> Decimal.to_integer() |> BlockScoutWeb.Cldr.Number.to_string! %>
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("The total gas amount used in the block and its percentage of gas filled in the block.") %> + <%= gettext("Gas Used") %> +
    +
    <%= @block.gas_used |> BlockScoutWeb.Cldr.Number.to_string! %> | <%= if Decimal.compare(@block.gas_limit, 0) == :eq, do: "0%", else: ((Decimal.to_integer(@block.gas_used) / Decimal.to_integer(@block.gas_limit)) |> BlockScoutWeb.Cldr.Number.to_string!(format: "#.#%")) %>
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Total gas limit provided by all transactions in the block.") %> + <%= gettext("Gas Limit") %> +
    +
    <%= BlockScoutWeb.Cldr.Number.to_string!(@block.gas_limit) %>
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("64-bit hash of value verifying proof-of-work (note: null for POA chains).") %> + <%= gettext("Nonce") %> +
    +
    <%= to_string(@block.nonce) %>
    +
    + <% end %> + <%= if !is_nil(@block.base_fee_per_gas) do%> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Minimum fee required per unit of gas. Fee adjusts based on network congestion.") %> + <%= gettext("Base Fee per Gas") %> +
    +
    <%= format_wei_value(@block.base_fee_per_gas, :gwei) %>
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: Explorer.coin_name() <> " " <> gettext("burned from transactions included in the block (Base fee (per unit of gas) * Gas Used).") %> + <%= gettext("Burnt Fees") %> +
    +
    <%= format_wei_value(burned_fee, :ether) %>
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("User-defined tips sent to validator for transaction priority/inclusion.") %> + <%= gettext("Priority Fee / Tip") %> +
    +
    <%= format_wei_value(%Wei{value: priority_fee}, :ether) %>
    +
    + <% end %> + <%= if show_reward?(@block.rewards) do %> +
    + <%= for block_reward <- @block.rewards do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Amount of distributed reward. Miners receive a static block reward + Tx fees + uncle fees.") %> + <%= block_reward_text(block_reward, @block.miner.hash) %> +
    +
    <%= format_wei_value(block_reward.reward, :ether) %>
    +
    + <% end %> + <% end %> + <%= if block_type(@block) == "Block" do %> + <%= if length(@block.uncle_relations) > 0 do %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Index position(s) of referenced stale blocks.") %> + <%= gettext("Uncles") %> +
    +
    <%= for {relation, index} <- Enum.with_index(@block.uncle_relations) do %> + <%= link( + gettext("Position %{index}", index: index), + class: "transaction__link", + "data-toggle": "tooltip", + "data-placement": "top" , + "data-test": "uncle_link", + "data-uncle-hash": to_string(relation.uncle_hash), + to: block_path(@conn, :show, relation.uncle_hash) + ) %><%= if index < length(@block.uncle_relations) - 1 do %>,<% end %> + <% end %>
    +
    + <% end %> + <% end %> +
    +
    +
    +
    + +<%= render BlockScoutWeb.Advertisement.BannersAdView, "_banner_728.html", conn: @conn %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block_transaction/404.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block_transaction/404.html.eex new file mode 100644 index 0000000..5f87d8d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/block_transaction/404.html.eex @@ -0,0 +1,13 @@ +
    +
    +
    + Block Not Found +
    +
    +

    <%= gettext("Block Details") %>

    +

    <%= block_not_found_message(@block_above_tip) %>

    + Back Home +
    +
    +
    + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block_transaction/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block_transaction/_metatags.html.eex new file mode 100644 index 0000000..bff7f94 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/block_transaction/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.BlockView, "_metatags.html", conn: @conn, block: @block %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block_transaction/index.html.eex new file mode 100644 index 0000000..c3f9ab4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/block_transaction/index.html.eex @@ -0,0 +1,41 @@ +
    + + <%= render BlockScoutWeb.BlockView, "overview.html", assigns %> + +
    +
    +
    + <%= + link( + gettext("Transactions"), + class: "card-tab #{tab_status("transactions", @conn.request_path)}", + to: block_transaction_path(@conn, :index, @conn.params["block_hash_or_number"]) + ) + %> +
    + +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + + + +
    +
    + <%= gettext "There are no transactions for this block." %> +
    +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    + + +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/chain/_block.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/chain/_block.html.eex new file mode 100644 index 0000000..e1551fd --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/chain/_block.html.eex @@ -0,0 +1,32 @@ +
    +
    + <%= link( + @block, + class: "tile-title", + to: block_path(BlockScoutWeb.Endpoint, :show, @block), + "data-selector": "block-number" + ) %> +
    +
    + <%= gettext("%{count} Transactions", count: Enum.count(@block.transactions)) %> + +
    + <%= if System.get_env("HIDE_BLOCK_MINER") !== "true" do %> +
    + <%= gettext "Miner" %> + <%= render BlockScoutWeb.AddressView, + "_link.html", + address: @block.miner, + contract: false, + use_custom_tooltip: false, + custom_classes_tooltip: ["miner-address-tooltip"] %> +
    + <% end %> + <%= if BlockScoutWeb.BlockView.show_reward?(@block.rewards) do %> +
    + <%= gettext "Reward" %> <%= BlockScoutWeb.BlockView.combined_rewards_value(@block) %> +
    + <% end %> +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/chain/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/chain/_metatags.html.eex new file mode 100644 index 0000000..a46a91b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/chain/_metatags.html.eex @@ -0,0 +1,5 @@ + + <%= gettext("%{subnetwork} %{network} Explorer", subnetwork: LayoutView.subnetwork_title(), network: LayoutView.network_title()) %> + +"> + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex new file mode 100644 index 0000000..4b84dce --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex @@ -0,0 +1,43 @@ +
    + + <%= gettext "Gas tracker" %> + +
    + <% gas_prices_from_oracle = gas_prices() %> + <%= if gas_prices_from_oracle do %> + +
    +
    +
    <%= "#{gas_prices_from_oracle["average"]}" <> " " %><%= gettext "Gwei" %>
    +
    +
    +
    <%= gettext "Slow" %><%= gas_prices_from_oracle["slow"] %> <%= gettext "Gwei" %>
    +
    <%= gettext "Average" %><%= gas_prices_from_oracle["average"] %> <%= gettext "Gwei" %>
    +
    <%= gettext "Fast" %><%= gas_prices_from_oracle["fast"] %> <%= gettext "Gwei" %>
    +
    + " + > + + +
    +
    + + <% else %> + <%= if @gas_price do %> + + + <%= render BlockScoutWeb.IconsView, "_gas_price_icon.html" %> + + <%= @gas_price <> " " %> + <%= gettext "Gwei" %> + + <% end %> + <% end %> +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex new file mode 100644 index 0000000..07e77eb --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex @@ -0,0 +1,231 @@ +
    +
    +
    + +
    + +
    + + + "<%= key %>":"<%= value %>" + <%= if x<(map_size(@chart_data_paths)-1) do %> + , + <% end %> + <% end %>}' + data-history_chart_config = '<%= @chart_config_json %>' + width="350" height="152"> + +
    + + +
    + <%= if Map.has_key?(@chart_config, :market) do %> + + <%# THE FOLLOWING LINE PREVENTS COPY/PASTE ERRORS %> + <%# Explicity put @chart_config.market in a variable %> + <%# This is done so that when people add a new chart source, x, %> + <%# They wont just access @chart_config.x w/o first checking if x exists%> + <% market_chart_config = @chart_config.market%> + + <%= if Enum.member?(market_chart_config, :price) do %> +
    + + <%= Explorer.coin_name() %> <%= gettext "Price" %> + +
    + + +
    +
    + <% end %> + <%= if Enum.member?(@chart_config.market, :market_cap) do %> +
    + + <%= gettext "Market Cap" %> + +
    + <% total_market_cap = market_cap(@market_cap_calculation, @exchange_rate) %> + + +
    +
    + <% end %> + <% end %> + <%= render BlockScoutWeb.ChainView, "gas_price_oracle_legend_item.html", gas_price: @gas_price %> + <%= if Map.has_key?(@chart_config, :transactions) do %> + + <% transaction_chart_config = @chart_config.transactions%> + <%= if Enum.member?(transaction_chart_config, :transactions_per_day) do %> +
    + + <%= gettext "Daily Transactions" %> + + + <% num_of_transactions = BlockScoutWeb.Cldr.Number.to_string!(Enum.at(@transaction_stats, 0).number_of_transactions, format: "#,###") %> + <%= num_of_transactions %> + <% gas_used = Enum.at(@transaction_stats, 0).gas_used %> + <%= if gas_used && gas_used > 0 do %> +
    "> + + + <% end %> + +
    + <% end %> + <% end %> +
    +
    + +
    +
    + <%= case @average_block_time do %> + <% {:error, :disabled} -> %> + <%= nil %> + <% average_block_time -> %> +
    + + <%= gettext "Average block time" %> + + + <%= Timex.format_duration(average_block_time, Explorer.Counters.AverageBlockTimeDurationFormat) %> + +
    + <% end %> +
    + + <%= gettext "Total transactions" %> + +
    + + <%= BlockScoutWeb.Cldr.Number.to_string!(@transaction_estimated_count, format: "#,###") %> + + <%= if @total_gas_usage > 0 do %> +
    " + class="custom-tooltip-total-transactions"> + + + <% end %> +
    +
    +
    + + <%= gettext "Total blocks" %> + + + <%= BlockScoutWeb.Cldr.Number.to_string!(@block_count, format: "#,###") %> + +
    +
    + + <%= gettext "Wallet addresses" %> + + + <%= BlockScoutWeb.Cldr.Number.to_string!(@address_count, format: "#,###") %> + +
    +
    +
    +
    +
    + +
    +
    +
    + <%= link(gettext("View All Blocks"), to: blocks_path(BlockScoutWeb.Endpoint, :index), class: "btn-line float-right") %> +

    <%= gettext "Blocks" %>

    +
    + + + + + +
    +
    +
    + + <%= render BlockScoutWeb.Advertisement.BannersAdView, "_banner_728.html", conn: @conn %> + +
    +
    + <%= link(gettext("View All Transactions"), to: transaction_path(BlockScoutWeb.Endpoint, :index), class: "btn-line float-right") %> +

    <%= gettext "Transactions" %>

    + + + + + +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_add_full.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_add_full.html.eex new file mode 100644 index 0000000..deae2f8 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_add_full.html.eex @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_add_line.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_add_line.html.eex new file mode 100644 index 0000000..586d5da --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_add_line.html.eex @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_copy.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_copy.html.eex new file mode 100644 index 0000000..d36e97f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_copy.html.eex @@ -0,0 +1,14 @@ + " + data-placement='top' + data-toggle='tooltip' + title='<%= @title %>' + style='<%= if assigns[:style] do @style end %>' + > + + + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_copy_for_table.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_copy_for_table.html.eex new file mode 100644 index 0000000..486ef4f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_copy_for_table.html.eex @@ -0,0 +1,15 @@ + " + style='<%= if assigns[:style] do @style end %>' + > + + + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_external_link.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_external_link.html.eex new file mode 100644 index 0000000..45081b8 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_external_link.html.eex @@ -0,0 +1,8 @@ + + <%= render BlockScoutWeb.IconsView, "_external_link.html" %> + <%= @text %> + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_line.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_line.html.eex new file mode 100644 index 0000000..4c1444c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_line.html.eex @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_qr_code.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_qr_code.html.eex new file mode 100644 index 0000000..183a55c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_btn_qr_code.html.eex @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex new file mode 100644 index 0000000..665733f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex @@ -0,0 +1,4 @@ +
    + <%= render BlockScoutWeb.CommonComponentsView, "_info.html" %> + <%= gettext("Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.") %> +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_channel_disconnected_message.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_channel_disconnected_message.html.eex new file mode 100644 index 0000000..4dd8e27 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_channel_disconnected_message.html.eex @@ -0,0 +1,5 @@ +
    +
    + <%= @text %> +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_check_tooltip.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_check_tooltip.html.eex new file mode 100644 index 0000000..7b7b1de --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_check_tooltip.html.eex @@ -0,0 +1,14 @@ +
    + + + + +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_csv_export_button.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_csv_export_button.html.eex new file mode 100644 index 0000000..9fd1933 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_csv_export_button.html.eex @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_i_tooltip.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_i_tooltip.html.eex new file mode 100644 index 0000000..b72928c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_i_tooltip.html.eex @@ -0,0 +1,14 @@ +
    " + data-boundary="window" + data-container="body" + data-html="true" + data-placement="top" + data-toggle="tooltip" + title="<%= @text %>" +> + " height="<%= if assigns[:height] do @height else "16" end %>"> + + + +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_i_tooltip_2.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_i_tooltip_2.html.eex new file mode 100644 index 0000000..87a55cf --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_i_tooltip_2.html.eex @@ -0,0 +1,11 @@ +" + data-boundary="window" + data-container="body" + data-html="true" + data-placement="top" + data-toggle="tooltip" + title="<%= @text %>" +> + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_error_modal.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_error_modal.html.eex new file mode 100644 index 0000000..8e1242d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_error_modal.html.eex @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_question_modal.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_question_modal.html.eex new file mode 100644 index 0000000..14c5089 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_question_modal.html.eex @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_success_modal.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_success_modal.html.eex new file mode 100644 index 0000000..71779f2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_success_modal.html.eex @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_warning_modal.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_warning_modal.html.eex new file mode 100644 index 0000000..a8b4011 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_icon_warning_modal.html.eex @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_info.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_info.html.eex new file mode 100644 index 0000000..0ab5088 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_info.html.eex @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_input_group.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_input_group.html.eex new file mode 100644 index 0000000..5c9c929 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_input_group.html.eex @@ -0,0 +1,17 @@ +
    + + type="<%= if assigns[:type] do @type end %>" + class="<%= if assigns[:input_classes] do @input_classes end %>" + placeholder="<%= if assigns[:placeholder] do @placeholder end %>" + value="<%= if assigns[:value] do @value end %>" + <%= if assigns[:disabled] do "disabled" end %> + /> + <%= if assigns[:prepend] do %> +
    +
    <%= @prepend %>
    +
    + <% end %> +
    <%= if assigns[:message] do @message end %>
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_loading_spinner.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_loading_spinner.html.eex new file mode 100644 index 0000000..d501012 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_loading_spinner.html.eex @@ -0,0 +1,6 @@ + + + + +<%= if assigns[:loading_text], do: @loading_text %> + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_minimal_proxy_pattern.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_minimal_proxy_pattern.html.eex new file mode 100644 index 0000000..c3f069b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_minimal_proxy_pattern.html.eex @@ -0,0 +1,10 @@ +
    + <%= render BlockScoutWeb.CommonComponentsView, "_info.html" %> + <%= gettext("Minimal Proxy Contract for") %> <%= link( +@address_hash, +to: address_contract_path(@conn, :index, @address_hash)) %>.
    <%= link( + gettext("EIP-1167"), + to: "https://eips.ethereum.org/EIPS/eip-1167", + target: "_blank" +) %><%= gettext(" - minimal bytecode implementation that delegates all calls to a known address") %>
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_bottom_disclaimer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_bottom_disclaimer.html.eex new file mode 100644 index 0000000..2846db4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_bottom_disclaimer.html.eex @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_close_button.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_close_button.html.eex new file mode 100644 index 0000000..681d1c4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_close_button.html.eex @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_qr_code.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_qr_code.html.eex new file mode 100644 index 0000000..e95d088 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_qr_code.html.eex @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_status.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_status.html.eex new file mode 100644 index 0000000..afb09a4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_modal_status.html.eex @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_pagination_container.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_pagination_container.html.eex new file mode 100644 index 0000000..7fd9f01 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_pagination_container.html.eex @@ -0,0 +1,66 @@ +
    + <%= if false do %> + +
    + <%= gettext "Show" %> + + <%= gettext "Records" %> +
    + <% end %> + + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_progress_from_to.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_progress_from_to.html.eex new file mode 100644 index 0000000..4ae44e5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_progress_from_to.html.eex @@ -0,0 +1,9 @@ +
    +
    +
    <%= @from %>
    +
    <%= @to %>
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex new file mode 100644 index 0000000..efd1ce6 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex @@ -0,0 +1,15 @@ +
    +
      +
    +
      + + +
    • +
    +
    +<%= if assigns[:showing_limit] do %> +
    (<%= gettext("Only the first")%> <%= assigns[:showing_limit] |> BlockScoutWeb.Cldr.Number.to_string! %> <%= gettext("elements are displayed")%>) +
    +<% end %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_status_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_status_icon.html.eex new file mode 100644 index 0000000..bda361e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_status_icon.html.eex @@ -0,0 +1,10 @@ +<%= case @status do %> + <% :success -> %> + + <% {:error, _} -> %> + + <% :awaiting_internal_transactions -> %> + + <% :pending -> %> + <% _ -> %> +<% end %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_minus.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_minus.html.eex new file mode 100644 index 0000000..1c08c85 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_minus.html.eex @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_pen.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_pen.html.eex new file mode 100644 index 0000000..559468d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_pen.html.eex @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_plus.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_plus.html.eex new file mode 100644 index 0000000..4f93df8 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_plus.html.eex @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_trash.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_trash.html.eex new file mode 100644 index 0000000..7d83186 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_svg_trash.html.eex @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_table-loader.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_table-loader.html.eex new file mode 100644 index 0000000..ecaf52d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_table-loader.html.eex @@ -0,0 +1,9 @@ +<%= for _r <- 1..5 do %> + + <%= for _c <- 1..@columns_num do %> + + + + <% end %> + +<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_tenderly_link.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_tenderly_link.html.eex new file mode 100644 index 0000000..665a567 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_tenderly_link.html.eex @@ -0,0 +1,4 @@ +<% tenderly_link = "https://dashboard.tenderly.co/tx#{@tenderly_chain_path}/" <> "0x" <> Base.encode16(@transaction_hash.bytes, case: :lower) %> + + Open in Tenderly <%= render BlockScoutWeb.IconsView, "_external_link.html" %> + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_tile-loader.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_tile-loader.html.eex new file mode 100644 index 0000000..91195d5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_tile-loader.html.eex @@ -0,0 +1,120 @@ +
    +
    +
    + + + + + + +
    +
    + + +
    +
    + + + + + + +
    +
    +
    +
    +
    +
    + + + + + + +
    +
    + + +
    +
    + + + + + + +
    +
    +
    +
    +
    +
    + + + + + + +
    +
    + + +
    +
    + + + + + + +
    +
    +
    +
    +
    +
    + + + + + + +
    +
    + + +
    +
    + + + + + + +
    +
    +
    +
    +
    +
    + + + + + + +
    +
    + + +
    +
    + + + + + + +
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex new file mode 100644 index 0000000..d79aff6 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex @@ -0,0 +1,12 @@ +<%= case @type do %> + <% :token_burning -> %> + <%= gettext("Token Burning") %> + <% :token_minting -> %> + <%= gettext("Token Minting") %> + <% :token_spawning -> %> + <%= gettext("Token Creation") %> + <% :token_transfer -> %> + <%= gettext("Token Transfer") %> + <% _ -> %> + <%= gettext("Token Transfer") %> +<% end %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex new file mode 100644 index 0000000..98293cc --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/csv_export/index.html.eex @@ -0,0 +1,52 @@ +<%= + for status <- ["error", "warning", "success", "question"] do + render BlockScoutWeb.CommonComponentsView, "_modal_status.html", status: status + end + %> +"> +
    +
    +
    +

    <%= gettext "Export Data" %>

    + +
    +

    Export <%= type_display_name(@type) %> for address <%= link( + @address_hash_string, + to: address_path(@conn, :show, @address_hash_string) + ) %> to CSV file

    +
    + +
    + from to +
    + +
    + + +
    +
    + + + +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/error422/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/error422/index.html.eex new file mode 100644 index 0000000..bbf314d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/error422/index.html.eex @@ -0,0 +1,12 @@ +
    +
    +
    + Page Not Found +
    +
    +

    Unprocessable Entity

    +

    The request was well-formed but was unable to be followed due to semantic errors. Maybe, you mistype a hash of tx/block/address?

    + Back Home +
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/form/_tag.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/form/_tag.html.eex new file mode 100644 index 0000000..54f032d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/form/_tag.html.eex @@ -0,0 +1,3 @@ +
    "> + <%= @text %> +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/form/text_field.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/form/text_field.html.eex new file mode 100644 index 0000000..c0a307c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/form/text_field.html.eex @@ -0,0 +1,15 @@ +
    + <%= if @label do %> + <%= if @id do %> + + <% else %> + + <% end %> + <% end %> + <%= @input_field %> + <%= for error <- @errors do %> +
    + <%= error %> +
    + <% end %> +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_accounts_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_accounts_icon.html.eex new file mode 100644 index 0000000..9b30e9e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_accounts_icon.html.eex @@ -0,0 +1,4 @@ + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_active_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_active_icon.html.eex new file mode 100644 index 0000000..6da126f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_active_icon.html.eex @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_api_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_api_icon.html.eex new file mode 100644 index 0000000..5f7da6f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_api_icon.html.eex @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_apps_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_apps_icon.html.eex new file mode 100644 index 0000000..af60841 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_apps_icon.html.eex @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_block_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_block_icon.html.eex new file mode 100644 index 0000000..fa782fe --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_block_icon.html.eex @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_blockchain_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_blockchain_icon.html.eex new file mode 100644 index 0000000..f823473 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_blockchain_icon.html.eex @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_check_dark_forest_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_check_dark_forest_icon.html.eex new file mode 100644 index 0000000..56a4854 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_check_dark_forest_icon.html.eex @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_external_link.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_external_link.html.eex new file mode 100644 index 0000000..5f9f06e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_external_link.html.eex @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_gas_price_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_gas_price_icon.html.eex new file mode 100644 index 0000000..01cdad2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_gas_price_icon.html.eex @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_guage_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_guage_icon.html.eex new file mode 100644 index 0000000..e5cae29 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_guage_icon.html.eex @@ -0,0 +1,18 @@ + + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_hourglass_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_hourglass_icon.html.eex new file mode 100644 index 0000000..761b37c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_hourglass_icon.html.eex @@ -0,0 +1,17 @@ + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_inactive_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_inactive_icon.html.eex new file mode 100644 index 0000000..93b7a65 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_inactive_icon.html.eex @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_network_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_network_icon.html.eex new file mode 100644 index 0000000..cf668d0 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_network_icon.html.eex @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_search_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_search_icon.html.eex new file mode 100644 index 0000000..b93ed63 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_search_icon.html.eex @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_smart_contract.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_smart_contract.html.eex new file mode 100644 index 0000000..372858f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_smart_contract.html.eex @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_test_network_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_test_network_icon.html.eex new file mode 100644 index 0000000..111fc1c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_test_network_icon.html.eex @@ -0,0 +1,3 @@ + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_tokens_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_tokens_icon.html.eex new file mode 100644 index 0000000..6d808a1 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_tokens_icon.html.eex @@ -0,0 +1,4 @@ + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/icons/_transaction_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/icons/_transaction_icon.html.eex new file mode 100644 index 0000000..179613d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/icons/_transaction_icon.html.eex @@ -0,0 +1,4 @@ + + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/internal_transaction/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/internal_transaction/_tile.html.eex new file mode 100644 index 0000000..d88a6d9 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/internal_transaction/_tile.html.eex @@ -0,0 +1,48 @@ +<% error = @internal_transaction.error %> +
    " data-test="internal_transaction" data-key="<%= @internal_transaction.transaction_hash %>_<%= @internal_transaction.index %>" data-internal-transaction-transaction-hash="<%= @internal_transaction.transaction_hash %>" data-internal-transaction-index="<%= @internal_transaction.index %>"> +
    + +
    + + <%= gettext("Internal Transaction") %> + + <%= type(@internal_transaction) %> + <%= if error do %> + <%= gettext "Error" %>: <%= error %> + <% end %> +
    + +
    + <%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @internal_transaction.transaction_hash %> + + <%= @internal_transaction |> BlockScoutWeb.AddressView.address_partial_selector(:from, assigns[:current_address]) |> (&(if is_list(&1), do: Keyword.put(&1, :ignore_implementation_name, true), else: &1)).() |> BlockScoutWeb.RenderHelpers.render_partial() %> + → + <%= @internal_transaction |> BlockScoutWeb.AddressView.address_partial_selector(:to, assigns[:current_address]) |> (&(if is_list(&1), do: Keyword.put(&1, :ignore_implementation_name, true), else: &1)).() |> BlockScoutWeb.RenderHelpers.render_partial() %> + + + + <%= BlockScoutWeb.TransactionView.value(@internal_transaction, include_label: false) %> <%= Explorer.coin_name() %> + + +
    + +
    + + <%= link( + gettext("Block #%{number}", number: to_string(@internal_transaction.block_number)), + to: block_path(BlockScoutWeb.Endpoint, :show, @internal_transaction.block_number) + ) %> + + + <%= if assigns[:current_address] do %> + + <%= if assigns[:current_address].hash == @internal_transaction.from_address_hash do %> + <%= gettext "OUT" %> + <% else %> + <%= gettext "IN" %> + <% end %> + + <% end %> +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/_account_menu_item.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/_account_menu_item.html.eex new file mode 100644 index 0000000..e8f8e58 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/_account_menu_item.html.eex @@ -0,0 +1,33 @@ +<%= if Explorer.Account.enabled?() do %> + <%= if @current_user do %> + + <% else %> +
  • + + + <%= render BlockScoutWeb.IconsView, "_accounts_icon.html" %> + + Sign in + +
  • + <% end %> +<% end %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/_add_chain_to_mm.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/_add_chain_to_mm.html.eex new file mode 100644 index 0000000..0e777ee --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/_add_chain_to_mm.html.eex @@ -0,0 +1,13 @@ + + +<% sub_network = Keyword.get(Application.get_env(:block_scout_web, BlockScoutWeb.Chain), :subnetwork) %> + + +
  • + + <%= gettext("Add") <> " #{sub_network}" %> + +
  • \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/_default_title.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/_default_title.html.eex new file mode 100644 index 0000000..38affa0 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/_default_title.html.eex @@ -0,0 +1,3 @@ + + <%= gettext("%{subnetwork} Explorer - BlockScout", subnetwork: subnetwork_title()) %> + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex new file mode 100644 index 0000000..43b26a2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex @@ -0,0 +1,58 @@ +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/_search.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/_search.html.eex new file mode 100644 index 0000000..18dbd03 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/_search.html.eex @@ -0,0 +1,35 @@ + +
    "> +
    +
    "> + " + type="text" + tabindex="1" + > +
    +
    + +
    +
    +
    + / +
    +
    +
    + +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex new file mode 100644 index 0000000..297a834 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex @@ -0,0 +1,172 @@ +<% apps_menu = Application.get_env(:block_scout_web, :apps_menu) %> +<% other_nets = dropdown_other_nets() %> +<% test_nets = test_nets(dropdown_nets()) %> +<% main_nets = dropdown_head_main_nets() %> + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex new file mode 100644 index 0000000..2c578cf --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex @@ -0,0 +1,308 @@ + + + + + + + + <%= case @view_module do %> + <% Elixir.BlockScoutWeb.ChainView -> %> + "> + " as="script"> + " as="script"> + " as="script"> + <% _ -> %> + "> + <% end %> + " as="script"> + " as="image" crossorigin> + " as="image" crossorigin> + " as="image" crossorigin> + " as="image" crossorigin> + " as="image" crossorigin> + " as="image" crossorigin> + " as="style" onload="this.onload=null;this.rel='stylesheet'"> + "> + <%= render_existing(@view_module, "styles.html", assigns) %> + + "> + "> + "> + "> + " color="#5bbad5"> + "> + + "> + + + <%= render_existing(@view_module, "_metatags.html", assigns) || render("_default_title.html") %> + + + + + + + + + <% raw_dark_forest_addresses_0_4 = CustomContractsHelpers.get_raw_custom_addresses_list(:dark_forest_addresses) || "" %> + <% raw_dark_forest_addresses_0_5 = CustomContractsHelpers.get_raw_custom_addresses_list(:dark_forest_addresses_v_0_5) || "" %> + <% raw_dark_forest_addresses_0_6 = CustomContractsHelpers.get_raw_custom_addresses_list(:dark_forest_addresses_v_0_6) || "" %> + <% raw_dark_forest_addresses_0_6_r2 = CustomContractsHelpers.get_raw_custom_addresses_list(:dark_forest_addresses_v_0_6_r2) || "" %> + <% raw_dark_forest_addresses = raw_dark_forest_addresses_0_4 %> + <% raw_dark_forest_addresses = if raw_dark_forest_addresses_0_5 !== "", do: raw_dark_forest_addresses <> "," <> raw_dark_forest_addresses_0_5, else: raw_dark_forest_addresses %> + <% raw_dark_forest_addresses = if raw_dark_forest_addresses_0_6 !== "", do: raw_dark_forest_addresses <> "," <> raw_dark_forest_addresses_0_6, else: raw_dark_forest_addresses %> + <% raw_dark_forest_addresses = if raw_dark_forest_addresses_0_6_r2 !== "", do: raw_dark_forest_addresses <> "," <> raw_dark_forest_addresses_0_6_r2, else: raw_dark_forest_addresses %> + + <% raw_circles_addresses = CustomContractsHelpers.get_raw_custom_addresses_list(:circles_addresses) %> + <%= cond do %> + <% ( + @view_module == Elixir.BlockScoutWeb.TransactionInternalTransactionView || + @view_module == Elixir.BlockScoutWeb.TransactionLogView || + @view_module == Elixir.BlockScoutWeb.TransactionRawTraceView || + @view_module == Elixir.BlockScoutWeb.TransactionTokenTransferView || + @view_module == Elixir.BlockScoutWeb.TransactionStateView + ) -> %> + <% to_address = @transaction && @transaction.to_address && "0x" <> Base.encode16(@transaction.to_address.hash.bytes, case: :lower) %> + <% {:ok, created_from_address} = if @transaction.to_address_hash, do: Chain.hash_to_address(@transaction.to_address_hash), else: {:ok, nil} %> + <% created_from_address_hash_str = if from_address_hash(created_from_address), do: "0x" <> Base.encode16(from_address_hash(created_from_address).bytes, case: :lower), else: nil %> + + <% ( + @view_module == Elixir.BlockScoutWeb.AddressTransactionView || + @view_module == Elixir.BlockScoutWeb.AddressTokenTransferView || + @view_module == Elixir.BlockScoutWeb.AddressTokenView || + @view_module == Elixir.BlockScoutWeb.AddressInternalTransactionView || + @view_module == Elixir.BlockScoutWeb.AddressCoinBalanceView || + @view_module == Elixir.BlockScoutWeb.AddressLogsView || + @view_module == Elixir.BlockScoutWeb.AddressValidationView || + @view_module == Elixir.BlockScoutWeb.AddressContractView || + @view_module == Elixir.BlockScoutWeb.AddressReadContractView || + @view_module == Elixir.BlockScoutWeb.AddressReadProxyView || + @view_module == Elixir.BlockScoutWeb.AddressWriteContractView || + @view_module == Elixir.BlockScoutWeb.AddressWriteProxyView + ) -> %> + <% created_from_address = if @address && from_address_hash(@address), do: "0x" <> Base.encode16(from_address_hash(@address).bytes, case: :lower), else: nil %> + + <% ( + @view_module == Elixir.BlockScoutWeb.Tokens.TransferView || + @view_module == Elixir.BlockScoutWeb.Tokens.ReadContractView || + @view_module == Elixir.BlockScoutWeb.Tokens.HolderView || + @view_module == Elixir.BlockScoutWeb.Tokens.Instance.TransferView || + @view_module == Elixir.BlockScoutWeb.Tokens.Instance.MetadataView || + @view_module == Elixir.BlockScoutWeb.PageNotFoundView + ) -> %> + <% {:ok, created_from_address} = if @token && @token.contract_address_hash, do: Chain.hash_to_address(@token.contract_address_hash), else: {:ok, nil} %> + <% created_from_address_hash = if from_address_hash(created_from_address), do: "0x" <> Base.encode16(from_address_hash(created_from_address).bytes, case: :lower), else: nil %> + + <% true -> %> + <%= nil %> + <% end %> +
    + <% show_maintenance_alert = Application.get_env(:block_scout_web, BlockScoutWeb.Chain)[:show_maintenance_alert] %> + <%= if show_maintenance_alert do %> +
    + <%= raw(System.get_env("MAINTENANCE_ALERT_MESSAGE")) %> +
    + <% end %> + <% indexed_ratio_blocks = Explorer.Chain.indexed_ratio_blocks() %> + <% indexed_ratio = + case Chain.finished_blocks_indexing?(indexed_ratio_blocks) do + false -> indexed_ratio_blocks + _ -> Explorer.Chain.indexed_ratio_internal_transactions() + end %> + <%= if not Explorer.Chain.finished_indexing?(indexed_ratio_blocks) do %> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html" %> + + <%= gettext("- We're indexing this chain right now. Some of the counts may be inaccurate.") %> +
    + <% end %> + <%= render BlockScoutWeb.LayoutView, "_topnav.html", current_user: Conn.get_session(@conn, :current_user), conn: @conn %> + +
    + + + <%= @inner_content %> +
    + <%= render BlockScoutWeb.LayoutView, "_footer.html", assigns %> +
    + <%= if ( + @view_module != Elixir.BlockScoutWeb.ChainView && + @view_module != Elixir.BlockScoutWeb.BlockView && + @view_module != Elixir.BlockScoutWeb.BlockTransactionView && + @view_module != Elixir.BlockScoutWeb.AddressView && + @view_module != Elixir.BlockScoutWeb.TokensView && + @view_module != Elixir.BlockScoutWeb.TransactionView && + @view_module != Elixir.BlockScoutWeb.PendingTransactionView && + @view_module != Elixir.BlockScoutWeb.TransactionInternalTransactionView && + @view_module != Elixir.BlockScoutWeb.TransactionLogView && + @view_module != Elixir.BlockScoutWeb.TransactionRawTraceView && + @view_module != Elixir.BlockScoutWeb.TransactionTokenTransferView && + @view_module != Elixir.BlockScoutWeb.TransactionStateView && + @view_module != Elixir.BlockScoutWeb.AddressTransactionView && + @view_module != Elixir.BlockScoutWeb.AddressTokenTransferView && + @view_module != Elixir.BlockScoutWeb.AddressTokenView && + @view_module != Elixir.BlockScoutWeb.AddressInternalTransactionView && + @view_module != Elixir.BlockScoutWeb.AddressCoinBalanceView && + @view_module != Elixir.BlockScoutWeb.AddressLogsView && + @view_module != Elixir.BlockScoutWeb.AddressValidationView && + @view_module != Elixir.BlockScoutWeb.AddressContractView && + @view_module != Elixir.BlockScoutWeb.AddressContractVerificationView && + @view_module != Elixir.BlockScoutWeb.AddressContractVerificationViaJsonView && + @view_module != Elixir.BlockScoutWeb.AddressContractVerificationViaFlattenedCodeView && + @view_module != Elixir.BlockScoutWeb.AddressContractVerificationVyperView && + @view_module != Elixir.BlockScoutWeb.AddressReadContractView && + @view_module != Elixir.BlockScoutWeb.AddressReadProxyView && + @view_module != Elixir.BlockScoutWeb.AddressWriteContractView && + @view_module != Elixir.BlockScoutWeb.AddressWriteProxyView && + @view_module != Elixir.BlockScoutWeb.Tokens.TransferView && + @view_module != Elixir.BlockScoutWeb.Tokens.ContractView && + @view_module != Elixir.BlockScoutWeb.Tokens.HolderView && + @view_module != Elixir.BlockScoutWeb.Tokens.InventoryView && + @view_module != Elixir.BlockScoutWeb.Tokens.InstanceView && + @view_module != Elixir.BlockScoutWeb.Tokens.Instance.MetadataView && + @view_module != Elixir.BlockScoutWeb.Tokens.Instance.OverviewView && + @view_module != Elixir.BlockScoutWeb.Tokens.Instance.TransferView && + @view_module != Elixir.BlockScoutWeb.VerifiedContractsView && + @view_module != Elixir.BlockScoutWeb.APIDocsView && + @view_module != Elixir.BlockScoutWeb.Admin.DashboardView && + @view_module != Elixir.BlockScoutWeb.SearchView && + @view_module != Elixir.BlockScoutWeb.AddressContractVerificationViaStandardJsonInputView && + @view_module != Elixir.BlockScoutWeb.AddressContractVerificationViaMultiPartFilesView && + @view_module != Elixir.BlockScoutWeb.StakesView + ) do %> + + <% end %> + <%= + for status <- ["error", "warning", "success", "question"] do + render BlockScoutWeb.CommonComponentsView, "_modal_status.html", status: status + end + %> + <%= render_existing(@view_module, "scripts.html", assigns) %> + <%= if @view_module == Elixir.BlockScoutWeb.ChainView do %> + + + + <% end %> + + <%= if @view_module in [Elixir.BlockScoutWeb.AddressContractVerificationView, Elixir.BlockScoutWeb.AddressContractVerificationVyperView, Elixir.BlockScoutWeb.AddressContractVerificationViaFlattenedCodeView, Elixir.BlockScoutWeb.AddressContractVerificationViaMultiPartFilesView, Elixir.BlockScoutWeb.AddressContractVerificationViaJsonView, Elixir.BlockScoutWeb.AddressContractVerificationViaStandardJsonInputView] do %> + + <% end %> + <%= if @view_module in [Elixir.BlockScoutWeb.AddressContractVerificationViaMultiPartFilesView, Elixir.BlockScoutWeb.AddressContractVerificationViaJsonView, Elixir.BlockScoutWeb.AddressContractVerificationViaStandardJsonInputView] do %> + + <% end %> + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/log/_data_decoded_view.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/log/_data_decoded_view.html.eex new file mode 100644 index 0000000..3c0fdaa --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/log/_data_decoded_view.html.eex @@ -0,0 +1,37 @@ +
    + " class="table thead-light table-bordered"> + + + + + + + <%= for {name, type, indexed?, value} <- @mapping do %> + + + + + + + <% end %> +
    <%= gettext "Name" %><%= gettext "Type" %><%= gettext "Indexed?" %><%= gettext "Data" %>
    <%= name %><%= type %><%= indexed? %> + <%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %> + <% :error -> %> + <%= nil %> + <% copy_text -> %> + + + + + + <% end %> +
    <%= BlockScoutWeb.ABIEncodedValueView.value_html(type, value) %>
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/page_not_found/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/page_not_found/index.html.eex new file mode 100644 index 0000000..9d44b7f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/page_not_found/index.html.eex @@ -0,0 +1,12 @@ +
    +
    +
    + Page Not Found +
    +
    +

    <%= gettext "Page not found" %>

    +

    <%= gettext "The requested path was not found on BlockScout." %>

    + <%= gettext "Back Home" %> +
    +
    +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/pending_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/pending_transaction/index.html.eex new file mode 100644 index 0000000..57a9545 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/pending_transaction/index.html.eex @@ -0,0 +1,35 @@ +
    + <%= render BlockScoutWeb.Advertisement.TextAdView, "index.html", conn: @conn %> +
    +
    +

    <%= gettext "Pending Transactions" %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + + + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost, click to load newer transactions") %> + + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    +
    + + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/search/_empty_td.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/search/_empty_td.html.eex new file mode 100644 index 0000000..ce2107a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/search/_empty_td.html.eex @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/search/_name_td.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/search/_name_td.html.eex new file mode 100644 index 0000000..7a98114 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/search/_name_td.html.eex @@ -0,0 +1,7 @@ + + <%= if @result.name do %> + + <%= highlight_search_result(@result.name, @query) %> + + <% end %> + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/search/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/search/_tile.html.eex new file mode 100644 index 0000000..1df5a4d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/search/_tile.html.eex @@ -0,0 +1,94 @@ +-<%= if @result.block_hash, do: Base.encode16(@result.block_hash, case: :lower), else: "" %>"> + + <%= render BlockScoutWeb.SearchView, "_empty_td.html" %> + <%= case @result.type do %> + <% "token" -> %> + + + <%= if System.get_env("DISPLAY_TOKEN_ICONS") === "true" do %> + <% chain_id_for_token_icon = Application.get_env(:block_scout_web, :chain_id) %> + <% address_hash = @result.address_hash %> + <%= + render BlockScoutWeb.TokensView, + "_token_icon.html", + chain_id: chain_id_for_token_icon, + address: address_hash + %> + <% end %> + + + + <% res = @result.name <> " (" <> @result.symbol <> ")" %> + + <%= highlight_search_result(res, @query) %> + + + <% "address" -> %> + <%= render BlockScoutWeb.SearchView, "_empty_td.html" %> + <%= render BlockScoutWeb.SearchView, "_name_td.html", result: @result, query: @query, conn: @conn %> + <% "contract" -> %> + <%= render BlockScoutWeb.SearchView, "_empty_td.html" %> + <%= render BlockScoutWeb.SearchView, "_name_td.html", result: @result, query: @query, conn: @conn %> + <% "block" -> %> + <%= render BlockScoutWeb.SearchView, "_empty_td.html" %> + + <%= link( + highlight_search_result(to_string(@result.block_number), @query), + to: block_path(@conn, :show, @result.block_number) + ) %> + + <% _ -> %> + <%= render BlockScoutWeb.SearchView, "_empty_td.html" %> + <%= render BlockScoutWeb.SearchView, "_empty_td.html" %> + <% end %> + + <%= case @result.type do %> + <% "token" -> %> + <%= with {:ok, address_hash} = Chain.string_to_address_hash(@result.address_hash), + {:ok, address} <- Chain.hash_to_address(address_hash) do %> + <%= render BlockScoutWeb.AddressView, + "_link.html", + address: address, + contract: false, + use_custom_tooltip: false + %> + <% end %> + <% "address" -> %> + <%= with {:ok, address_hash} = Chain.string_to_address_hash(@result.address_hash), + {:ok, address} <- Chain.hash_to_address(address_hash) do %> + <%= render BlockScoutWeb.AddressView, + "_link.html", + address: address, + contract: false, + use_custom_tooltip: false + %> + <% end %> + <% "contract" -> %> + <%= with {:ok, address_hash} = Chain.string_to_address_hash(@result.address_hash), + {:ok, address} <- Chain.hash_to_address(address_hash) do %> + <%= render BlockScoutWeb.AddressView, + "_link.html", + address: address, + contract: true, + use_custom_tooltip: false + %> + <% end %> + <% "transaction" -> %> + <%= render BlockScoutWeb.TransactionView, + "_link.html", + transaction_hash: "0x" <> Base.encode16(@result.tx_hash, case: :lower) %> + <% "block" -> %> + <%= link( + "0x" <> Base.encode16(@result.block_hash, case: :lower), + to: block_path(@conn, :show, @result.block_number) + ) %> + <% _ -> %> + <% end %> + + +
    <%= @result.type %>
    + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/search/results.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/search/results.html.eex new file mode 100644 index 0000000..c973f6c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/search/results.html.eex @@ -0,0 +1,51 @@ +
    " +> + <%= render BlockScoutWeb.Advertisement.TextAdView, "index.html", conn: @conn %> +
    +
    +
    + " placeholder="Search" id="search-text-input"> +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    +

    <%= gettext "Search Results" %>: <%= @query %>

    + +
    +
    + + + + + + + + + + + + <%= render BlockScoutWeb.CommonComponentsView, "_table-loader.html", columns_num: 5 %> + +
    +
     
    +
    +
     
    +
    +
    Search Result
    +
    +
    Hash
    +
    +
    Category
    +
    +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    +
    + +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_connect_container.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_connect_container.html.eex new file mode 100644 index 0000000..2438dd2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_connect_container.html.eex @@ -0,0 +1,16 @@ +
    + + <%= render BlockScoutWeb.IconsView, "_inactive_icon.html" %> + +

    Disconnected

    + +
    + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex new file mode 100644 index 0000000..e361e2c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex @@ -0,0 +1,44 @@ +
    +
    +[ <%= @function_name %> <%= gettext("method Response") %> ]
    +
    +
    +<%= case @outputs do %> + <% {:error, %{code: code, message: message, data: data}} -> %> + <% revert_reason = Chain.format_revert_reason_message(data) %> + <%= case decode_revert_reason(@smart_contract_address, revert_reason) do %> + <% {:ok, _identifier, text, mapping} -> %> +
    <%= raw(values_with_type(text, :error, nil)) %>
    +
    + <%= for {name, type, value} <- mapping do %> +
    <%= raw(values_with_type(value, type, name, 1)) %>
    + <% end %> +
    + <% {:error, _contract_verified, []} -> %> + <% decoded_revert_reason = decode_hex_revert_reason(revert_reason) %> +
    <%= "(#{code}) #{message} (#{if String.valid?(decoded_revert_reason), do: decoded_revert_reason, else: revert_reason})" %>
    + <% {:error, _contract_verified, candidates} -> %> + <% {:ok, _identifier, text, mapping} = Enum.at(candidates, 0) %> +
    <%= raw(values_with_type(text, :error, nil)) %>
    +
    + <%= for {name, type, value} <- mapping do %> +
    <%= raw(values_with_type(value, type, name, 1)) %>
    + <% end %> +
    + <% _ -> %> + <% decoded_revert_reason = decode_hex_revert_reason(revert_reason) %> +
    <%= "(#{code}) #{message} (#{if String.valid?(decoded_revert_reason), do: decoded_revert_reason, else: revert_reason})" %>
    + <% end %> + <% {:error, %{code: code, message: message}} -> %> +
    (error) : <%= "(#{code}) #{message}" %>
    + <% {:error, error} -> %> +
    (error) : <%= error %>
    + <% _ -> %> +
    +[<%= for {item, index} <- Enum.with_index(@outputs) do %>
    +<%= if named_argument?(item) do %><%= item["name"] %><% end %>
    +<%= raw(values_with_type(item["value"], item["type"], fetch_name(@names, index), 0)) %>
    +<% end %>]
    +<% end %>
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex new file mode 100644 index 0000000..23ec24c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex @@ -0,0 +1,155 @@ +<% minimal_proxy_template = if assigns[:custom_abi], do: nil, else: Chain.get_minimal_proxy_template(@address.hash) %> +<% metadata_for_verification = if assigns[:custom_abi], do: nil, else: minimal_proxy_template || Chain.get_address_verified_twin_contract(@address.hash).verified_contract %> +<% smart_contract_verified = if assigns[:custom_abi], do: false, else: BlockScoutWeb.AddressView.smart_contract_verified?(@address) %> +<%= unless smart_contract_verified do %> + <%= if metadata_for_verification do %> + <%= if minimal_proxy_template do %> + <%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: metadata_for_verification.address_hash, conn: @conn %> + <% else %> + <% path = address_verify_contract_path(@conn, :new, @address.hash) %> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_info.html" %> <%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link( + metadata_for_verification.address_hash, + to: address_contract_path(@conn, :index, metadata_for_verification.address_hash)) %>.
    <%= gettext("All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with") %> <%= link( + gettext("Verify & Publish"), + to: path + ) %> <%= gettext("page") %>
    +
    + <% end %> + <% end %> +<% end %> +<%= if smart_contract_verified && @address.smart_contract.is_changed_bytecode do %> + <%= render BlockScoutWeb.CommonComponentsView, "_changed_bytecode_warning.html" %> +<% end %> +<%= if @contract_type == "proxy" do %> +
    +

    Implementation address:

    <%= link( + @implementation_address, + to: address_path(@conn, :show, @implementation_address) + ) %>

    +
    +<% end %> +<%= for {function, counter} <- Enum.with_index(@read_only_functions ++ @read_functions_required_wallet, 1) do %> +
    > +
    + <%= counter %>. + <%= case function["type"] do %> + <% "fallback" -> %> + <%= gettext "fallback" %><%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", text: gettext("The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable.") %> + <% "receive" -> %> + <%= gettext "receive" %><%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", text: gettext("The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception.") %> + <% _ -> %> + <%= function["name"] %> + <% end %> + → +
    + + <%= if queryable?(function["inputs"]) || writable?(function) || Helper.read_with_wallet_method?(function) do %> +
    + <% function_abi = + case Jason.encode([function]) do + {:ok, abi_string} -> + abi_string + _ -> + if @contract_type == "proxy" do + @implementation_abi + else + @contract_abi + end + end %> +
    " data-type="<%= @contract_type %>" data-url="<%= smart_contract_path(@conn, :show, Address.checksum(@address.hash)) %>" data-contract-address="<%= @address.hash %>" data-contract-abi="<%= function_abi %>" data-implementation-abi="<%= function_abi %>" data-chain-id="<%= Explorer.Chain.Cache.NetVersion.get_version() %>" data-custom-abi="<%= if assigns[:custom_abi], do: true, else: false %>"> + + + + <%= if queryable?(function["inputs"]) do %> + <%= for input <- function["inputs"] do %> +
    + <%= if int?(input["type"]) do %> + px;"/> + + + + + + + + <% else %> + " /> + <% end %> +
    + <% end %> + <% end %> + + <%= if Helper.payable?(function) do %> +
    + +
    + <% end %> + +
    + +
    +
    + + + <%= if outputs?(function["outputs"]) do %> +
    + <%= if (queryable?(function["inputs"])), do: raw "↳" %> + + <%= for output <- function["outputs"] do %> + <%= if output["name"] && output["name"] !== "", do: "#{output["name"]}(#{output["type"]})", else: output["type"] %> + <% end %> +
    + <% end %> +
    +
    + <% else %> + <%= cond do %> + <% outputs?(function["outputs"]) -> %> +
    + <% length = Enum.count(function["outputs"]) %> + <%= for {output, index} <- Enum.with_index(function["outputs"]) do %> + <%= if address?(output["type"]) do %> +
    + <%= link( + output["value"], + to: address_path(@conn, :show, output["value"])) %><%= if not_last_element?(length, index) do %>, <% end %> +
    + <% else %> + <%= if output["type"] == "uint256" do %> +
    +
    + (uint256): + "><%= output["value"] %> + + + <%= gettext("WEI")%> + <%= Explorer.coin_name() %> + +
    +
    + <% else %> +
    "><%= raw(values_with_type(output["value"], output["type"], [output["name"]], 0, output["components"])) %>
    + <% end %> + <% end %> + <% end %> +
    + <% error?(function["outputs"]) -> %> + <% {:error, text_error} = function["outputs"] %> +
    <%= text_error %>
    + <% true -> %> + <% nil %> + <% end %> + <% end %> +
    +<% end %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex new file mode 100644 index 0000000..316f571 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/_tile.html.eex new file mode 100644 index 0000000..14a749e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/_tile.html.eex @@ -0,0 +1,49 @@ + + + + + <%= @index %> + + + + <%= if System.get_env("DISPLAY_TOKEN_ICONS") === "true" do %> + <% chain_id_for_token_icon = Application.get_env(:block_scout_web, :chain_id) %> + <% foreign_token_contract_address_hash = nil %> + <% token_hash_for_token_icon = if foreign_token_contract_address_hash, do: foreign_token_contract_address_hash, else: Address.checksum(@token.contract_address_hash) %> + <%= + render BlockScoutWeb.TokensView, + "_token_icon.html", + chain_id: chain_id_for_token_icon, + address: token_hash_for_token_icon + %> + <% end %> + + + <% token = token_display_name(@token) %> + <%= link(token, + to: token_path(BlockScoutWeb.Endpoint, :show, @token.contract_address_hash), + "data-test": "token_link", + class: "text-truncate") %> + + + <%= render BlockScoutWeb.AddressView, + "_link.html", + address: @token.contract_address, + contract: true, + use_custom_tooltip: false + %> + + + <%= if decimals?(@token) do %> + <%= format_according_to_decimals(@token.total_supply, @token.decimals) %> + <% else %> + <%= format_integer_to_currency(@token.total_supply) %> + <% end %> <%= @token.symbol %> + + + + + <%= @token.holder_count %> + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/_token_icon.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/_token_icon.html.eex new file mode 100644 index 0000000..b4db9e4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/_token_icon.html.eex @@ -0,0 +1,2 @@ +<% token_icon_url = Explorer.Chain.get_token_icon_url_by(@chain_id, @address) %> +" alt="" onerror="this.style.visibility='hidden'"/> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/contract/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/contract/_metatags.html.eex new file mode 100644 index 0000000..e3754d4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/contract/_metatags.html.eex @@ -0,0 +1 @@ +<%= BlockScoutWeb.Tokens.OverviewView.render "_metatags.html", conn: @conn, token: @token %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/contract/index.html.eex new file mode 100644 index 0000000..99bb263 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/contract/index.html.eex @@ -0,0 +1,23 @@ +
    + <%= render( + OverviewView, + "_details.html", + token: @token, + counters_path: @counters_path, + tags: @tags, + conn: @conn + ) %> + +
    +
    + <%= render OverviewView, "_tabs.html", assigns %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Loading...") %> +
    +
    +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_metatags.html.eex new file mode 100644 index 0000000..e3754d4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_metatags.html.eex @@ -0,0 +1 @@ +<%= BlockScoutWeb.Tokens.OverviewView.render "_metatags.html", conn: @conn, token: @token %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex new file mode 100644 index 0000000..f8fecf5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex @@ -0,0 +1,19 @@ +
    +
    +
    + + <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_balance.address, contract: BlockScoutWeb.AddressView.contract?(@token_balance.address), use_custom_tooltip: false %> + + + + + <%= format_token_balance_value(@token_balance.value, @token_balance.token_id, @token) %> <%= @token.symbol %> + + + <%= if show_total_supply_percentage?(@token.total_supply) do %> + <%= total_supply_percentage(@token_balance.value, @token.total_supply) %> + <% end %> + +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/index.html.eex new file mode 100644 index 0000000..9d78dcc --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/index.html.eex @@ -0,0 +1,44 @@ +
    + <%= render( + OverviewView, + "_details.html", + token: @token, + counters_path: @counters_path, + tags: @tags, + conn: @conn + ) %> + +
    +
    + <%= render OverviewView, "_tabs.html", assigns %> + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost") %> +

    <%= gettext "Token Holders" %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + + +
    +
    + + <%= gettext "There are no holders for this Token." %> + +
    +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/index.html.eex new file mode 100644 index 0000000..49fc7ae --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/index.html.eex @@ -0,0 +1,60 @@ +
    " +> + <%= render BlockScoutWeb.Advertisement.TextAdView, "index.html", conn: @conn %> +
    +
    +

    <%= gettext "Tokens" %>

    + +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + +
    +
    + + + + + + + + + + + + + <%= render BlockScoutWeb.CommonComponentsView, "_table-loader.html", columns_num: 6 %> + +
    +
     
    +
    +
     
    +
    +
    Token
    +
    +
    Address
    +
    +
    + Total Supply +
    +
    +
    + Holders Count +
    +
    +
    +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    +
    + + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/holder/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/holder/index.html.eex new file mode 100644 index 0000000..7fe594a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/holder/index.html.eex @@ -0,0 +1,41 @@ +
    + <%= render( + OverviewView, + "_details.html", + token: @token, + total_token_transfers: @total_token_transfers, + token_id: @token_instance.token_id, + token_instance: @token_instance, + conn: @conn + ) %> + +
    +
    + <%= render OverviewView, "_tabs.html", assigns %> +
    +

    <%= gettext "Token Holders" %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + + + + + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex new file mode 100644 index 0000000..ca5cc7e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex @@ -0,0 +1,30 @@ +
    + <%= render( + OverviewView, + "_details.html", + token: @token, + total_token_transfers: @total_token_transfers, + token_id: @token_instance.token_id, + token_instance: @token_instance, + conn: @conn + ) %> + +
    +
    + <%= render OverviewView, "_tabs.html", assigns %> +
    +
    +
    +

    <%= gettext "Metadata" %>

    + +
    +
    +
    <%= format_metadata(@token_instance.instance.metadata) %>
    +            
    +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex new file mode 100644 index 0000000..bb2b4d7 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex @@ -0,0 +1,97 @@ +
    +
    +
    +
    +
    +

    + <%= if token_name?(@token) do %> + <%= link(@token.name, to: token_path(BlockScoutWeb.Endpoint, :show, @token.contract_address_hash)) %> + <% else %> + <%= gettext("Token Details") %> + <% end %> + + + + ' + > + + + + + + + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["overview-title-item"], + clipboard_text: @token_id, + aria_label: gettext("Copy Token ID"), + title: gettext("Copy Token ID") %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_qr_code.html" %> + +

    + +

    <%= gettext "Token ID" %>: <%= to_string(@token_id) %>

    + +
    +
    + <%= if external_url(@token_instance.instance) do %> + + target="_blank"> + View In App <%= render BlockScoutWeb.IconsView, "_external_link.html" %> + + + <% end %> + <%= @token.type %> + <%= @total_token_transfers %> <%= gettext "Transfers" %> + <%= if decimals?(@token) do %> + <%= @token.decimals %> <%= gettext "Decimals" %> + <% end %> +
    +
    +
    +
    +
    + +
    +
    +
    +
    + <%= if media_type(media_src(@token_instance.instance, true)) == "video" do %> + + <% else %> + /> + <% end %> +
    +
    +
    +
    +
    + +
    + + +<%= render BlockScoutWeb.Advertisement.BannersAdView, "_banner_728.html", conn: @conn %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex new file mode 100644 index 0000000..de8a031 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex @@ -0,0 +1,22 @@ +
    + <%= link( + gettext("Token Transfers"), + class: "card-tab #{tab_status("token-transfers", @conn.request_path)}", + to: token_instance_path(@conn, :show, @token.contract_address_hash, to_string(@token_instance.token_id)) + ) + %> + <%= if @token_instance.instance && @token_instance.instance.metadata do %> + <%= link( + gettext("Metadata"), + to: token_instance_metadata_path(@conn, :index, Address.checksum(@token.contract_address_hash), to_string(@token_instance.token_id)), + class: "card-tab #{tab_status("metadata", @conn.request_path)}") + %> + <% end %> + <%= if !Chain.token_id_1155_is_unique?(@token.contract_address_hash, @token_instance.token_id) and @token.type == "ERC-1155" do %> + <%= link( + gettext("Token Holders"), + to: token_instance_holder_path(@conn, :index, Address.checksum(@token.contract_address_hash), to_string(@token_instance.token_id)), + class: "card-tab #{tab_status("token-holders", @conn.request_path)}") + %> + <% end %> +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex new file mode 100644 index 0000000..c49b042 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex @@ -0,0 +1,41 @@ +
    + <%= render( + OverviewView, + "_details.html", + token: @token, + total_token_transfers: @total_token_transfers, + token_id: @token_instance.token_id, + token_instance: @token_instance, + conn: @conn + ) %> + +
    +
    + <%= render OverviewView, "_tabs.html", assigns %> +
    +

    <%= gettext "Token Transfers" %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + + + + + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_metatags.html.eex new file mode 100644 index 0000000..e3754d4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_metatags.html.eex @@ -0,0 +1 @@ +<%= BlockScoutWeb.Tokens.OverviewView.render "_metatags.html", conn: @conn, token: @token %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex new file mode 100644 index 0000000..9d2ce63 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/_token.html.eex @@ -0,0 +1,58 @@ +<% is_1155 = @token.type == "ERC-1155"%> +<% is_unique = Chain.token_id_1155_is_unique?(@token.contract_address_hash, @token_transfer.token_id) or not is_1155%> + +
    +
    +
    + + <%= if is_unique do%> + <%= gettext "Unique Token" %> + <% else %> + <%= gettext "Not unique Token" %> + <% end %> +
    + + <%= if is_unique do %> +
    + + <%= gettext "Token ID" %>: + + <%= link(@token_transfer.token_id, to: token_instance_path(@conn, :show, "#{@token.contract_address_hash}", "#{@token_transfer.token_id}")) %> + + + + <%= gettext "Owner Address" %>: + + <%= render BlockScoutWeb.AddressView, + "_link.html", + address: @token_transfer.to_address, + contract: false, + use_custom_tooltip: false %> + + +
    + <% else %> +
    + + <%= gettext "Token ID" %>: + + <%= link(@token_transfer.token_id, to: token_instance_path(@conn, :show, "#{@token.contract_address_hash}", "#{@token_transfer.token_id}")) %> + + +
    + <% end %> + +
    + +
    + <%= if media_type(media_src(@token_transfer.instance)) == "video" do %> + + <% else %> + /> + <% end %> +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/index.html.eex new file mode 100644 index 0000000..e41c420 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/inventory/index.html.eex @@ -0,0 +1,42 @@ +
    + <%= render( + OverviewView, + "_details.html", + token: @token, + counters_path: @counters_path, + tags: @tags, + conn: @conn + ) %> + +
    +
    + <%= render OverviewView, "_tabs.html", assigns %> + +
    +

    <%= gettext "Inventory" %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + + + +
    +
    + <%= gettext "There are no tokens." %> +
    +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex new file mode 100644 index 0000000..86470ae --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex @@ -0,0 +1,156 @@ +<% circles_addresses_list = CustomContractsHelpers.get_custom_addresses_list(:circles_addresses) %> +<% address_hash_str = "0x" <> Base.encode16(@token.contract_address_hash.bytes, case: :lower) %> +<% {:ok, created_from_address} = if @token.contract_address_hash, do: Chain.hash_to_address(@token.contract_address_hash), else: {:ok, nil} %> +<% created_from_address_hash = if from_address_hash(created_from_address), do: "0x" <> Base.encode16(from_address_hash(created_from_address).bytes, case: :lower), else: nil %> +
    + <%= render BlockScoutWeb.Advertisement.TextAdView, "index.html", conn: @conn %> +
    +
    +
    +
    +

    +
    + <%= cond do %> + <% Enum.member?(circles_addresses_list, address_hash_str) -> %> +
    + +
    + <% Enum.member?(circles_addresses_list, created_from_address_hash) -> %> +
    + +
    + <% true -> %> + <%= nil %> + <% end %> + <%= if token_name?(@token) do %> + "> + +
    <%= @token.name %>
    + <% else %> + <%= gettext("Token Details") %> + <% end %> + <%= render BlockScoutWeb.AddressView, "_labels.html", address_hash: @token.contract_address_hash, tags: @tags %> +
    + + + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["overview-title-item"], + clipboard_text: Address.checksum(@token.contract_address_hash), + aria_label: gettext("Copy Address"), + title: gettext("Copy Address") %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_qr_code.html" %> + +

    +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Address of the token contract") %> + <%= gettext("Contract") %> +
    +
    + <%= link( + @token.contract_address_hash, + to: AccessHelpers.get_path(@conn, :address_path, :show, + Address.checksum(@token.contract_address_hash)), + "data-test": "token_contract_address" + ) + %> +
    +
    + <%= if total_supply?(@token) do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("The total amount of tokens issued") %> + <%= gettext("Total supply") %> +
    +
    + <%= if decimals?(@token) do %> + <%= format_according_to_decimals(@token.total_supply, @token.decimals) %> + <% else %> + <%= format_integer_to_currency(@token.total_supply) %> + <% end %> <%= @token.symbol %> +
    +
    + <%= if @token.usd_value do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Total Supply * Price") %> + <%= gettext("Market Cap") %> +
    +
    + +
    +
    + <%= unless Map.has_key?(@token, :custom_cap) && @token.custom_cap do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Price per token on the exchanges") %> + <%= gettext("Price") %> +
    +
    + +
    +
    + <% end %> + <% end %> + <% end %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Number of accounts holding the token") %> + <%= gettext("Holders") %> +
    +
    + <% link = if @conn.request_path |> String.contains?("/token-holders"), do: "", else: AccessHelpers.get_path(@conn, :token_holder_path, :index, @token.contract_address_hash) %> + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Fetching holders...") %> +
    +
    +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Number of transfers for the token") %> + <%= gettext("Transfers") %> +
    +
    + <% link = if @conn.request_path |> String.contains?("/token-transfers"), do: "", else: AccessHelpers.get_path(@conn, :token_transfer_path, :index, @token.contract_address_hash) %> + <%= render BlockScoutWeb.CommonComponentsView, "_loading_spinner.html", loading_text: gettext("Fetching transfers...") %> +
    +
    + <%= if decimals?(@token) do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Number of digits that come after the decimal place when displaying token value") %> + <%= gettext("Decimals") %> +
    +
    + <%= @token.decimals %> +
    +
    + <% end %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Type of the token standard") %> + <%= gettext("Token type") %> +
    +
    + <%= @token.type %> +
    +
    +
    +
    +
    +
    +
    + +<%= render BlockScoutWeb.CommonComponentsView, "_modal_qr_code.html", qr_code: BlockScoutWeb.AddressView.qr_code(Address.checksum(@token.contract_address_hash)), title: @token.contract_address %> +<%= render BlockScoutWeb.Advertisement.BannersAdView, "_banner_728.html", conn: @conn %> + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_metatags.html.eex new file mode 100644 index 0000000..7c4a80f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_metatags.html.eex @@ -0,0 +1,5 @@ + + <%= "#{token_name(@token)} (#{token_symbol(@token)}) - #{LayoutView.subnetwork_title()} - BlockScout" %> + + +"> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_tabs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_tabs.html.eex new file mode 100644 index 0000000..38d779d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_tabs.html.eex @@ -0,0 +1,53 @@ +<% address_hash = Address.checksum(@token.contract_address_hash) %> +<% is_proxy = BlockScoutWeb.Tokens.OverviewView.smart_contract_is_proxy?(@token) %> +
    + <%= link( + gettext("Token Transfers"), + class: "card-tab #{tab_status("token-transfers", @conn.request_path)}", + to: AccessHelpers.get_path(@conn, :token_path, :show, @token.contract_address_hash) + ) + %> + <%= link( + gettext("Token Holders"), + class: "card-tab #{tab_status("token-holders", @conn.request_path)}", + "data-test": "token_holders_tab", + to: AccessHelpers.get_path(@conn, :token_holder_path, :index, address_hash) + ) + %> + <%= if display_inventory?(@token) do %> + <%= link( + gettext("Inventory"), + class: "card-tab #{tab_status("inventory", @conn.request_path)}", + to: AccessHelpers.get_path(@conn, :token_inventory_path, :index, address_hash) + ) + %> + <% end %> + <%= if smart_contract_with_read_only_functions?(@token) do %> + <%= link( + gettext("Read Contract"), + to: AccessHelpers.get_path(@conn, :token_read_contract_path, :index, address_hash), + class: "card-tab #{tab_status("read-contract", @conn.request_path)}") + %> + <% end %> + <%= if smart_contract_with_write_functions?(@token) do %> + <%= link( + gettext("Write Contract"), + to: AccessHelpers.get_path(@conn, :token_write_contract_path, :index, address_hash), + class: "card-tab #{tab_status("write-contract", @conn.request_path)}") + %> + <% end %> + <%= if is_proxy do %> + <%= link( + gettext("Read Proxy"), + to: AccessHelpers.get_path(@conn, :token_read_proxy_path, :index, address_hash), + class: "card-tab #{tab_status("read-proxy", @conn.request_path)}") + %> + <% end %> + <%= if smart_contract_with_write_functions?(@token) && is_proxy do %> + <%= link( + gettext("Write Proxy"), + to: AccessHelpers.get_path(@conn, :token_write_proxy_path, :index, address_hash), + class: "card-tab #{tab_status("write-proxy", @conn.request_path)}") + %> + <% end %> +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_metatags.html.eex new file mode 100644 index 0000000..e3754d4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_metatags.html.eex @@ -0,0 +1 @@ +<%= BlockScoutWeb.Tokens.OverviewView.render "_metatags.html", conn: @conn, token: @token %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex new file mode 100644 index 0000000..d1d151f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex @@ -0,0 +1,50 @@ +
    +
    + +
    + + <%= render(BlockScoutWeb.CommonComponentsView, "_token_transfer_type_display_name.html", type: Chain.get_token_transfer_type(@token_transfer)) %> + +
    + +
    + <%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @token_transfer.transaction_hash %> + + <%= link to: address_token_transfers_path(@conn, :index, Address.checksum(@token_transfer.from_address), Address.checksum(@token.contract_address_hash)), "data-test": "address_hash_link" do %> + <%= render( + BlockScoutWeb.AddressView, + "_responsive_hash.html", + address: @token_transfer.from_address, + contract: BlockScoutWeb.AddressView.contract?(@token_transfer.from_address), + use_custom_tooltip: false + ) %> + <% end %> + → + <%= link to: address_token_transfers_path(@conn, :index, Address.checksum(@token_transfer.to_address), Address.checksum(@token.contract_address_hash)), "data-test": "address_hash_link" do %> + <%= render( + BlockScoutWeb.AddressView, + "_responsive_hash.html", + address: @token_transfer.to_address, + contract: BlockScoutWeb.AddressView.contract?(@token_transfer.to_address), + use_custom_tooltip: false + ) %> + <% end %> + + + + <%= render BlockScoutWeb.TransactionView, "_total_transfers.html", Map.put(assigns, :transfer, @token_transfer) %> + + +
    + +
    + + <%= link( + gettext("Block #%{number}", number: @token_transfer.block_number), + to: block_path(BlockScoutWeb.Endpoint, :show, @token_transfer.block_number) + ) %> + + +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex new file mode 100644 index 0000000..5ea6121 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex @@ -0,0 +1,42 @@ +
    + <%= render( + OverviewView, + "_details.html", + token: @token, + counters_path: @counters_path, + tags: @tags, + conn: @conn + ) %> + +
    +
    + <%= render OverviewView, "_tabs.html", assigns %> +
    +

    <%= gettext "Token Transfers" %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + + + + + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    +
    + + +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex new file mode 100644 index 0000000..741344e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex @@ -0,0 +1,44 @@ +
    +
    +

    <%= gettext "Input" %>

    + + <%= case @decoded_input_data do %> + <% {:error, :contract_not_verified, candidates} -> %> +
    + <%= gettext "To see accurate decoded input data, the contract must be verified." %> + <%= case @transaction do %> + <% %{to_address: %{hash: hash}} -> %> + <% path = address_verify_contract_path(@conn, :new, hash) %> + <%= gettext "Verify the contract " %><%= gettext "here" %> + <% _ -> %> + <%= nil %> + <% end %> +
    + <%= unless Enum.empty?(candidates) do %> +

    <%= gettext "Potential matches from our contract method database:" %>

    + <%= gettext "IMPORTANT: This information is a best guess based on similar functions from other verified contracts." %> + <%= gettext "To have guaranteed accuracy, use the link above to verify the contract's source code." %> + + <%= for {:ok, method_id, text, mapping} <- candidates do %> +
    +

    <%= text %>:

    + + <%= render(BlockScoutWeb.TransactionView, "_decoded_input_body.html", method_id: method_id, text: text, mapping: mapping) %> + <% end %> + <% end %> + <% {:ok, method_id, text, mapping} -> %> + <%= render(BlockScoutWeb.TransactionView, "_decoded_input_body.html", method_id: method_id, text: text, mapping: mapping) %> + <% {:error, :contract_verified, candidates} -> %> +

    <%= gettext "Potential matches from our contract method database:" %>

    + <%= for {:ok, method_id, text, mapping} <- candidates do %> +
    +

    <%= text %>:

    + <%= render(BlockScoutWeb.TransactionView, "_decoded_input_body.html", method_id: method_id, text: text, mapping: mapping) %> + <% end %> + <% _ -> %> +
    + <%= gettext "Failed to decode input data." %> +
    + <% end %> +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex new file mode 100644 index 0000000..3675d4c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex @@ -0,0 +1,62 @@ +
    + " class="table thead-light table-bordered"> + <%= if !assigns[:error] do %> + + + + + <% end %> + + + + +
    <%= gettext "Method Id" %>0x<%= @method_id %>
    <%= if assigns[:error], do: gettext("Error"), else: gettext("Call") %><%= @text %>
    +
    + +<% max_length = get_max_length() %> +<%= unless Enum.empty?(@mapping) do %> +
    + " class="table thead-light table-bordered"> + + + + + + <%= for {name, type, value} <- @mapping do %> + + + + + + <% end %> +
    <%= gettext "Name" %><%= gettext "Type" %><%= gettext "Data" %>
    <%= name %><%= type %> + <%= case BlockScoutWeb.ABIEncodedValueView.value_html(type, value, true) do %> + <% :error -> %> +
    + <%= gettext "Error rendering value" %> +
    + <% value_with_no_links -> %> + <%= case BlockScoutWeb.ABIEncodedValueView.copy_text(type, value) do %> + <% :error -> %> + <%= nil %> + <% copy_text -> %> + + + + + + <% end %> + <% value_with_links = BlockScoutWeb.ABIEncodedValueView.value_html(type, value, false)%> + <% string = template_to_string(value_with_no_links) %> +
    <%= if String.length(string) > max_length do %>
    <% input = trim(max_length, string) %><%= input[:show] %>...<%= input[:hide] %>
    <% else %><%= value_with_links %><% end %>
    + <% end %> +
    +
    +<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex new file mode 100644 index 0000000..4fb50ef --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex @@ -0,0 +1,34 @@ +
    +
    +
    + + <%= gettext("Emission Contract") %> + + + <%= gettext("Success") %> + +
    +
    + <%= link( + @validator.block.hash, + to: block_path(BlockScoutWeb.Endpoint, :show, @validator.block.hash), + class: "text-truncate") %> + + <%= @emission_funds |> BlockScoutWeb.AddressView.address_partial_selector(nil, @current_address) |> BlockScoutWeb.RenderHelpers.render_partial() %> + → + <%= @validator |> BlockScoutWeb.AddressView.address_partial_selector(nil, @current_address) |> BlockScoutWeb.RenderHelpers.render_partial() %> + + + + <%= format_wei_value(@emission_funds.reward, :ether) %> + + +
    +
    + + <%= @validator |> block_number() |> BlockScoutWeb.RenderHelpers.render_partial() %> + + +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link.html.eex new file mode 100644 index 0000000..9b0333f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link.html.eex @@ -0,0 +1,4 @@ +<%= link(@transaction_hash, + to: transaction_path(BlockScoutWeb.Endpoint, :show, @transaction_hash), + "data-test": "transaction_hash_link", + class: "text-truncate") %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_instance.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_instance.html.eex new file mode 100644 index 0000000..88273f4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_instance.html.eex @@ -0,0 +1 @@ +<%= "[" %><%= link(short_token_id(@token_id, 30), to: token_instance_path(BlockScoutWeb.Endpoint, :show, @transfer.token.contract_address_hash, to_string(@token_id)), "data-test": "token_link") %><%= "]" %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_symbol.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_symbol.html.eex new file mode 100644 index 0000000..73cb4a2 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_link_to_token_symbol.html.eex @@ -0,0 +1 @@ +<%= link(token_symbol(@transfer.token), to: token_path(BlockScoutWeb.Endpoint, :show, @transfer.token.contract_address_hash), "data-test": "token_link") %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_metatags.html.eex new file mode 100644 index 0000000..920306f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_metatags.html.eex @@ -0,0 +1,14 @@ +<%= if assigns[:transaction] do %> + + <%= gettext( + "Transaction %{transaction} - %{subnetwork} Explorer", + transaction: to_string(@transaction.hash), + subnetwork: BlockScoutWeb.LayoutView.subnetwork_title() + ) %> + + + "> + "> +<% else %> + <%= BlockScoutWeb.LayoutView.render("_default_title.html") %> +<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex new file mode 100644 index 0000000..d1b33ff --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_pending_tile.html.eex @@ -0,0 +1,25 @@ +<% status = BlockScoutWeb.TransactionView.transaction_status(@transaction) %> +
    +
    +
    + <%= BlockScoutWeb.TransactionView.transaction_display_type(@transaction) %> +
    <%= BlockScoutWeb.TransactionView.formatted_result(status) %>
    +
    +
    + <%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @transaction.hash %> + + <%= render BlockScoutWeb.AddressView, "_link.html", address: @transaction.from_address, contract: BlockScoutWeb.AddressView.contract?(@transaction.from_address), use_custom_tooltip: false %> + → + <%= if @transaction.to_address_hash do %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: @transaction.to_address, contract: BlockScoutWeb.AddressView.contract?(@transaction.to_address), use_custom_tooltip: false %> + <% else %> + <%= gettext("Contract Address Pending") %> + <% end %> + + + <%= BlockScoutWeb.TransactionView.value(@transaction, include_label: false) %> <%= Explorer.coin_name() %> + <%= BlockScoutWeb.TransactionView.formatted_fee(@transaction, denomination: :ether, include_label: false) %> <%= gettext "TX Fee" %> + +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tabs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tabs.html.eex new file mode 100644 index 0000000..c99a8f1 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tabs.html.eex @@ -0,0 +1,34 @@ +
    + <%= if @show_token_transfers do %> + <%= link( + gettext("Token Transfers"), + class: "card-tab #{tab_status("token-transfers", @conn.request_path, @show_token_transfers)}", + to: AccessHelpers.get_path(@conn, :transaction_token_transfer_path, :index, @transaction) + ) + %> + <% end %> + <%= link( + gettext("Internal Transactions"), + class: "card-tab #{tab_status("internal-transactions", @conn.request_path, @show_token_transfers)}", + to: AccessHelpers.get_path(@conn, :transaction_internal_transaction_path, :index, @transaction) + ) + %> + <%= link( + gettext("Logs"), + class: "card-tab #{tab_status("logs", @conn.request_path)}", + to: AccessHelpers.get_path(@conn, :transaction_log_path, :index, @transaction), + "data-test": "transaction_logs_link" + ) + %> + <%= link( + gettext("Raw Trace"), + class: "card-tab #{tab_status("raw-trace", @conn.request_path)}", + to: AccessHelpers.get_path(@conn, :transaction_raw_trace_path, :index, @transaction) + ) %> + <%= link( + gettext("State changes"), + class: "card-tab #{tab_status("state", @conn.request_path)}", + to: AccessHelpers.get_path(@conn, :transaction_state_path, :index, @transaction) + ) + %> +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex new file mode 100644 index 0000000..f555c37 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex @@ -0,0 +1,99 @@ +<% status = transaction_status(@transaction) %> +<% error_in_internal_tx = @transaction.has_error_in_internal_txs %> +<% current_user = AuthController.current_user(@conn) %> +<% tx_tags = BlockScoutWeb.Models.GetTransactionTags.get_transaction_with_addresses_tags(@transaction, current_user) %> +
    +
    + +
    +
    + <%= if error_in_internal_tx do %> + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", text: gettext("Error in internal transactions"), additional_classes: ["color-inherit"] %> + <% end %> + + <%= transaction_display_type(@transaction) %> + +
    + + <%= if status_class(@transaction) == "tile-status--pending" do %> +
    + + +
    + <% end %> + <%= formatted_result(status) %> +
    +
    + +
    + +
    + <%= render "_link.html", transaction_hash: @transaction.hash, data_test: "address_hash_link" %> + <% method_name = Transaction.get_method_name(@transaction) %> + <%= if method_name do %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: method_name, additional_classes: ["method", "ml-1"] %> + <% end %> + <%= if tx_tags.personal_tx_tag && tx_tags.personal_tx_tag.name !== :error do %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: tx_tags.personal_tx_tag.name, additional_classes: [tag_name_to_label(tx_tags.personal_tx_tag.name), "ml-1"] %> + <% end %> + <%= render BlockScoutWeb.AddressView, "_labels.html", tags: tx_tags %> +
    +
    + + <%= @transaction |> BlockScoutWeb.AddressView.address_partial_selector(:from, assigns[:current_address]) |> BlockScoutWeb.RenderHelpers.render_partial() %> + → + <%= @transaction |> BlockScoutWeb.AddressView.address_partial_selector(:to, assigns[:current_address]) |> BlockScoutWeb.RenderHelpers.render_partial() %> + + + + <%= value(@transaction, include_label: false) %> <%= Explorer.coin_name() %> + + + <%= formatted_fee(@transaction, denomination: :ether, include_label: false) %> <%= gettext "TX Fee" %> + + + + + <%= if involves_token_transfers?(@transaction) do %> +
    + <% [first_token_transfer | remaining_token_transfers] = @transaction.token_transfers %> + + <%= render "_token_transfer.html", address: assigns[:current_address], token_transfer: first_token_transfer %> + +
    + <%= for token_transfer <- remaining_token_transfers do %> + <%= render "_token_transfer.html", address: assigns[:current_address], token_transfer: token_transfer, burn_address_hash: @burn_address_hash %> + <% end %> +
    +
    + + <%= if Enum.any?(remaining_token_transfers) do %> +
    + <%= link gettext("View More Transfers"), to: "#transaction-#{@transaction.hash}", "data-toggle": "collapse", "data-selector": "token-transfer-open", "data-test": "token_transfers_expansion" %> + <%= link gettext("View Less Transfers"), class: "d-none", to: "#transaction-#{@transaction.hash}", "data-toggle": "collapse", "data-selector": "token-transfer-close" %> +
    + <% end %> + <% end %> +
    + +
    + + <%= @transaction |> block_number() |> BlockScoutWeb.RenderHelpers.render_partial() %> + + + <%= if from_or_to_address?(@transaction, assigns[:current_address]) do %> + + <%= if @transaction.from_address_hash == assigns[:current_address].hash do %> + + <%= gettext "OUT" %> + + <% else %> + + <%= gettext "IN" %> + + <% end %> + + <% end %> +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_token_transfer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_token_transfer.html.eex new file mode 100644 index 0000000..815d9bd --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_token_transfer.html.eex @@ -0,0 +1,25 @@ +
    + + <%= if from_or_to_address?(@token_transfer, @address) do %> + <%= if @token_transfer.from_address_hash == @address.hash do %> + + ↳ + + <% else %> + + ↳ + + <% end %> + <% end %> + + <%= @token_transfer |> BlockScoutWeb.AddressView.address_partial_selector(:from, @address, true) |> BlockScoutWeb.RenderHelpers.render_partial() %> + + → + + <%= @token_transfer |> BlockScoutWeb.AddressView.address_partial_selector(:to, @address, true) |> BlockScoutWeb.RenderHelpers.render_partial() %> + + + + <%= render BlockScoutWeb.TransactionView, "_total_transfers.html", Map.put(assigns, :transfer, @token_transfer) %> + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex new file mode 100644 index 0000000..fd9efa4 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers.html.eex @@ -0,0 +1,23 @@ +<%= case token_transfer_amount(@transfer) do %> + <% {:ok, :erc721_instance} -> %> + <%= render BlockScoutWeb.TransactionView, "_transfer_token_with_id.html", transfer: @transfer, token_id: @transfer.token_id %> + <% {:ok, :erc1155_instance, value} -> %> + <% transfer_type = Chain.get_token_transfer_type(@transfer) %> + <%= if transfer_type == :token_spawning do %> + <%= render BlockScoutWeb.TransactionView, "_transfer_token_with_id.html", transfer: @transfer, token_id: @transfer.token_id %> + <% else %> + <%= "#{value} " %> + <%= render BlockScoutWeb.TransactionView, "_transfer_token_with_id.html", transfer: @transfer, token_id: @transfer.token_id %> + <% end %> + <% {:ok, :erc1155_instance, values, token_ids, _decimals} -> %> + <% values_ids = Enum.zip(values, token_ids) %> + <%= for {value, token_id} <- values_ids do %> +
    + <%= "#{value} "%> + <%= render BlockScoutWeb.TransactionView, "_transfer_token_with_id.html", transfer: @transfer, token_id: token_id %> +
    + <% end %> + <% {:ok, value} -> %> + <%= value %> + <%= " " %><%= render BlockScoutWeb.TransactionView, "_link_to_token_symbol.html", transfer: @transfer %> +<% end %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex new file mode 100644 index 0000000..d26557b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex @@ -0,0 +1,48 @@ +<%= with {:ok, from_address} <- Chain.hash_to_address(@transfer.from_address_hash), +{:ok, to_address} <- Chain.hash_to_address(@transfer.to_address_hash) do %> +<% from_tags = BlockScoutWeb.Models.GetAddressTags.get_address_tags(@transfer.from_address_hash, @current_user) %> +<% to_tags = BlockScoutWeb.Models.GetAddressTags.get_address_tags(@transfer.to_address_hash, @current_user) %> + + + From + + + <%= render BlockScoutWeb.AddressView, "_link.html", address: from_address, contract: BlockScoutWeb.AddressView.contract?(from_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_labels.html", tags: from_tags %> + + + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy_for_table.html", +additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders", "btn-copy-token-transfer"], +clipboard_text: from_address, +aria_label: gettext("Copy From Address"), +title: gettext("Copy From Address"), +style: "position: relative;" %> + + + + + To + + + <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: BlockScoutWeb.AddressView.contract?(to_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_labels.html", tags: to_tags %> + + + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy_for_table.html", +additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders", "btn-copy-token-transfer"], +clipboard_text: to_address, +aria_label: gettext("Copy To Address"), +title: gettext("Copy To Address"), +style: "position: relative;"%> + + + + + For + +<% end %> + + <%= render BlockScoutWeb.TransactionView, "_total_transfers.html", transfer: @transfer %> + + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_transfer_token_with_id.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_transfer_token_with_id.html.eex new file mode 100644 index 0000000..44a3444 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_transfer_token_with_id.html.eex @@ -0,0 +1,2 @@ +<%= "TokenID " %><%= render BlockScoutWeb.TransactionView, "_link_to_token_instance.html", transfer: @transfer, token_id: @token_id %> +<%= " " %><%= render BlockScoutWeb.TransactionView, "_link_to_token_symbol.html", transfer: @transfer %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex new file mode 100644 index 0000000..37f358b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex @@ -0,0 +1,45 @@ + +
    + <%= render BlockScoutWeb.Advertisement.TextAdView, "index.html", conn: @conn %> +
    +
    +

    <%= gettext "Validated Transactions" %>

    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_rap_pagination_container.html", position: "top", showing_limit: if Chain.transactions_available_count() == Chain.limit_showing_transactions(), do: Chain.limit_showing_transactions(), else: nil %> +
    + + + <%= render BlockScoutWeb.CommonComponentsView, "_channel_disconnected_message.html", text: gettext("Connection Lost, click to load newer transactions") %> + + + +
    +
    + + <%= gettext "There are no transactions." %> + +
    +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_rap_pagination_container.html", position: "bottom" %> + +
    + + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/invalid.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/invalid.html.eex new file mode 100644 index 0000000..8ec4188 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/invalid.html.eex @@ -0,0 +1,14 @@ +
    +
    +
    +
    +
    +

    <%= gettext "Invalid Transaction Hash" %>

    +
    + <%= @transaction_hash %> <%= gettext "is not a valid transaction hash" %> +
    +
    +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/not_found.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/not_found.html.eex new file mode 100644 index 0000000..97f721e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/not_found.html.eex @@ -0,0 +1,34 @@ +
    +
    +
    + Block Not Found +
    +
    +

    <%= gettext("Sorry, We are unable to locate this transaction Hash") %>

    +
    +
    +
    + 1 +

    <%= gettext("If you have just submitted this transaction please wait for at least 30 seconds before refreshing this page.") %>

    +
    +
    + 2 +

    <%= gettext("It could still be in the TX Pool of a different node, waiting to be broadcasted.") %>

    +
    +
    +
    +
    + 3 +

    <%= gettext("During times when the network is busy (i.e during ICOs) it can take a while for your transaction to propagate through the network and for us to index it.") %>

    +
    +
    + 4 +

    <%= gettext("If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information.") %>

    +
    +
    +
    + <%= gettext("Back Home") %> +
    +
    + +
    \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex new file mode 100644 index 0000000..cefcc6c --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex @@ -0,0 +1,512 @@ +<% block = @transaction.block %> +<% from_address_hash = @transaction.from_address_hash %> +<% from_address = @transaction.from_address %> +<% to_address_hash = @transaction.to_address_hash %> +<% created_address_hash = @transaction.created_contract_address_hash %> +<% type = if @transaction.type == 2, do: "2 (EIP-1559)", else: @transaction.type %> +<% base_fee_per_gas = if block, do: block.base_fee_per_gas, else: nil %> +<% max_priority_fee_per_gas = @transaction.max_priority_fee_per_gas %> +<% max_fee_per_gas = @transaction.max_fee_per_gas %> +<% burned_fee = + if !is_nil(max_fee_per_gas) and !is_nil(@transaction.gas_used) and !is_nil(base_fee_per_gas) do + if Decimal.compare(max_fee_per_gas.value, 0) == :eq do + %Wei{value: Decimal.new(0)} + else + Wei.mult(base_fee_per_gas, @transaction.gas_used) + end + else + nil + end %> +<% %Wei{value: burned_fee_decimal} = if is_nil(burned_fee), do: %Wei{value: Decimal.new(0)}, else: burned_fee %> +<% priority_fee_per_gas = if is_nil(max_priority_fee_per_gas) or is_nil(base_fee_per_gas), do: nil, else: Enum.min_by([max_priority_fee_per_gas, Wei.sub(max_fee_per_gas, base_fee_per_gas)], fn x -> Wei.to(x, :wei) end) %> +<% priority_fee = if is_nil(priority_fee_per_gas), do: nil, else: Wei.mult(priority_fee_per_gas, @transaction.gas_used) %> +<% decoded_input_data = decoded_input_data(@transaction) %> +<% status = transaction_status(@transaction) %> +<% circles_addresses_list = CustomContractsHelpers.get_custom_addresses_list(:circles_addresses) %> +<% address_hash_str = if to_address_hash, do: "0x" <> Base.encode16(to_address_hash.bytes, case: :lower), else: nil %> +<% {:ok, created_from_address} = if to_address_hash, do: Chain.hash_to_address(to_address_hash), else: {:ok, nil} %> +<% created_from_address_hash_str = if from_address_hash(created_from_address), do: "0x" <> Base.encode16(from_address_hash(created_from_address).bytes, case: :lower), else: nil %> +<%= render BlockScoutWeb.Advertisement.TextAdView, "index.html", conn: @conn %> +
    +
    +
    + +
    +
    + <%= cond do %> + <% Enum.member?(circles_addresses_list, address_hash_str) -> %> +
    + +
    + <% Enum.member?(circles_addresses_list, created_from_address_hash_str) -> %> +
    + +
    + <% true -> %> + <%= nil %> + <% end %> +

    +
    + <%= gettext "Transaction Details" %> + <% personal_tx_tag = if assigns[:tx_tags], do: @tx_tags.personal_tx_tag, else: nil %> + <%= if personal_tx_tag && personal_tx_tag.name !== :error do %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: personal_tx_tag.name, additional_classes: [tag_name_to_label(personal_tx_tag.name), "ml-1"] %> + <% end %> + <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @tx_tags %> +
    +

    + <%= if status == :pending do %> +
    +
    + + +
    + <%= gettext("This transaction is pending confirmation.") %> +
    + <% end %> +
    + <%= if show_tenderly_link?() do %> +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tenderly_link.html", + transaction_hash: @transaction.hash, + tenderly_chain_path: tenderly_chain_path() %> +
    + <% end %> +
    +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Unique character string (TxID) assigned to every verified transaction.") %> + <%= gettext "Transaction Hash" %> +
    +
    + <%= @transaction %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], + clipboard_text: @transaction, + aria_label: gettext("Copy Transaction Hash"), + title: gettext("Copy Txn Hash") %> +
    +
    + + + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Current transaction state: Success, Failed (Error), or Pending (In Process)") %> + <%= gettext "Result" %> +
    +
    + <% formatted_result = BlockScoutWeb.TransactionView.formatted_result(status) %> + <%= render BlockScoutWeb.CommonComponentsView, "_status_icon.html", status: status %><%= formatted_result %> +
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("The status of the transaction: Confirmed or Unconfirmed.") %> + <%= gettext "Status" %> +
    +
    + <% formatted_status = BlockScoutWeb.TransactionView.formatted_status(status) %> + <% confirmations = confirmations(@transaction, block_height: @block_height) %> + + + <%= if status == :pending do %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: formatted_status, additional_classes: ["large"] %> + <% else %> + <%= render BlockScoutWeb.FormView, "_tag.html", text: formatted_status, additional_classes: ["success", "large"] %> + <% end %> + + <%= if confirmations > 0 do %> + <%= gettext "Confirmed by " %><%= confirmations %><%= " " <> confirmations_ds_name(confirmations) %> + <% end %> + +
    +
    + + <%= if status == {:error, "Reverted"} || status == {:error, "execution reverted"} do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("The revert reason of the transaction.") %> + <%= gettext "Revert reason" %>
    +
    + <%= case BlockScoutWeb.TransactionView.transaction_revert_reason(@transaction) do %> + <% {:error, _contract_not_verified, candidates} when candidates != [] -> %> + <% {:ok, method_id, text, mapping} = Enum.at(candidates, 0) %> + <%= render(BlockScoutWeb.TransactionView, "_decoded_input_body.html", method_id: method_id, text: text, mapping: mapping, error: true) %> + <% {:ok, method_id, text, mapping} -> %> + <%= render(BlockScoutWeb.TransactionView, "_decoded_input_body.html", method_id: method_id, text: text, mapping: mapping, error: true) %> + <% _ -> %> + <% hex = BlockScoutWeb.TransactionView.get_pure_transaction_revert_reason(@transaction) %> + <% utf8 = BlockScoutWeb.TransactionView.decoded_revert_reason(@transaction) %> +
    +
    Raw:<%= raw("\t") %><%= hex %><%= raw("\n") %>UTF-8:<%= raw("\t") %><%= utf8 %>
    +
    + <% end %> +
    +
    + <% end %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Block number containing the transaction.") %> + <%= gettext "Block" %>
    +
    + <%= if block do %> + <%= link( + block, + class: "transaction__link", + to: block_path(@conn, :show, block) + ) %> + <% else %> + <%= formatted_result(status) %> + <% end %> +
    +
    + + <%= if block && block.timestamp do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Date & time of transaction inclusion, including length of time for confirmation.") %> + <%= gettext "Timestamp" %> +
    +
    + + + + + <%= case processing_time_duration(@transaction) do %> + <% :pending -> %> + <% nil %> + <% :unknown -> %> + <% nil %> + <% {:ok, interval_string} -> %> + | <%= gettext("Confirmed within") %> <%= interval_string %> + <% end %> +
    +
    + <% end %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Address (external or contract) sending the transaction.") %> + <%= gettext "From" %>
    +
    + <%= render BlockScoutWeb.AddressView, "_link.html", address: from_address, contract: BlockScoutWeb.AddressView.contract?(from_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @from_tags %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], + clipboard_text: Address.checksum(from_address_hash), + aria_label: gettext("Copy From Address"), + title: gettext("Copy From Address") %> +
    +
    + + <% to_address = @transaction |> Map.get(:to_address) || @transaction |> Map.get(:created_contract_address) %> + <% recipient_address_hash = to_address_hash || created_address_hash %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Address (external or contract) receiving the transaction.") %> + <%= if BlockScoutWeb.AddressView.contract?(to_address) && !created_address_hash do %> + <%= gettext "Interacted With (To)" %> + <% else %> + <%= gettext "To" %> + <% end %> +
    +
    + <%= cond do %> + <% created_address_hash -> %> + [<%= gettext("Contract") %>  + <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: BlockScoutWeb.AddressView.contract?(to_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @to_tags %> +  <%= gettext("created") %>] + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], + clipboard_text: Address.checksum(recipient_address_hash), + aria_label: gettext("Copy To Address"), + title: gettext("Copy To Address") %> + <% recipient_address_hash -> %> + <%= render BlockScoutWeb.AddressView, "_link.html", address: to_address, contract: BlockScoutWeb.AddressView.contract?(to_address), use_custom_tooltip: false, trimmed: false %> + <%= render BlockScoutWeb.AddressView, "_labels.html", tags: @to_tags %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-custom", "btn-copy-icon-no-borders"], + clipboard_text: Address.checksum(recipient_address_hash), + aria_label: gettext("Copy To Address"), + title: gettext("Copy To Address") %> + <% true -> %> + <% end %> +
    +
    + <%= case token_transfer_type(@transaction) do %> + <% {_type, %{token_transfers: token_transfers} = transaction_with_transfers} when is_list(token_transfers) and token_transfers != [] -> %> + + <% %{transfers: transfers, mintings: mintings, burnings: burnings, creations: creations} = aggregate_token_transfers(transaction_with_transfers.token_transfers) %> + <%= if Enum.count(transfers) > 0 do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("List of token transferred in the transaction.") %> + <%= gettext "Tokens Transferred" %>
    +
    + + <%= for transfer <- transfers do %> + <%= render BlockScoutWeb.TransactionView, "_total_transfers_from_to.html", Map.put(assigns, :transfer, transfer) %> + <% end %> +
    +
    +
    + <% end %> + + <%= if Enum.count(mintings) > 0 do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("List of token minted in the transaction.") %> + <%= gettext "Tokens Minted" %> +
    +
    + + <%= for minting <- mintings do %> + <%= render BlockScoutWeb.TransactionView, "_total_transfers_from_to.html", Map.put(assigns, :transfer, minting) %> + <% end %> +
    +
    +
    + <% end %> + + <%= if Enum.count(burnings) > 0 do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("List of token burnt in the transaction.") %> + <%= gettext "Tokens Burnt" %>
    +
    + <%= for burning <- burnings do %> + + <%= render BlockScoutWeb.TransactionView, "_total_transfers_from_to.html", Map.put(assigns, :transfer, burning) %> +
    + <% end %> +
    +
    + <% end %> + + <%= if Enum.count(creations) > 0 do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("List of ERC-1155 tokens created in the transaction.") %> + <%= gettext "Tokens Created" %>
    +
    + <%= for creation <- creations do %> + + <%= render BlockScoutWeb.TransactionView, "_total_transfers_from_to.html", Map.put(assigns, :transfer, creation) %> +
    + <% end %> +
    +
    + <% end %> + <% _ -> %> + <% end %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Value sent in the native token (and USD) if applicable.") %> + <%= gettext "Value" %> +
    +
    <%= value(@transaction) %> + <%= if !empty_exchange_rate?(@exchange_rate) do %> + ( + data-usd-exchange-rate=<%= @exchange_rate.usd_value %>> + ) + <% end %> +
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Total transaction fee.") %> + <%= gettext "Transaction Fee" %> +
    +
    + <%= formatted_fee(@transaction, denomination: :ether) %> + + <%= if !empty_exchange_rate?(@exchange_rate) do %> + ( data-usd-exchange-rate=<%= @exchange_rate.usd_value %>>) + <% end %> +
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage.") %> + <%= gettext "Gas Price" %> +
    +
    <%= gas_price(@transaction, :gwei) %>
    +
    + <%= if !is_nil(type) do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Transaction type, introduced in EIP-2718.") %> + <%= gettext "Transaction Type" %> +
    +
    <%= type %>
    +
    + <% end %> +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Maximum gas amount approved for the transaction.") %> + <%= gettext "Gas Limit" %> +
    +
    <%= format_gas_limit(@transaction.gas) %>
    +
    + <%= if !is_nil(max_fee_per_gas) do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Maximum total amount per unit of gas a user is willing to pay for a transaction, including base fee and priority fee.") %> + <%= gettext "Max Fee per Gas" %> +
    +
    <%= format_wei_value(max_fee_per_gas, :gwei) %>
    +
    + <% end %> + <%= if !is_nil(max_priority_fee_per_gas) do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("User defined maximum fee (tip) per unit of gas paid to validator for transaction prioritization.") %> + <%= gettext "Max Priority Fee per Gas" %> +
    +
    <%= format_wei_value(max_priority_fee_per_gas, :gwei) %>
    +
    + <% end %> + <%= if !is_nil(priority_fee) do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("User-defined tip sent to validator for transaction priority/inclusion.") %> + <%= gettext "Priority Fee / Tip" %> +
    +
    <%= format_wei_value(priority_fee, :ether) %>
    +
    + <% end %> + <%= if !is_nil(burned_fee) do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Amount of") <> " " <> Explorer.coin_name() <> " " <> gettext("burned for this transaction. Equals Block Base Fee per Gas * Gas Used.") %> + <%= gettext "Transaction Burnt Fee" %> +
    +
    <%= format_wei_value(burned_fee, :ether) %> + <%= unless empty_exchange_rate?(@exchange_rate) do %> + ( data-usd-exchange-rate=<%= @exchange_rate.usd_value %>>) + <% end %> +
    +
    + <% end %> + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Actual gas amount used by the transaction.") %> + <%= gettext "Gas Used by Transaction" %> +
    + <% gas_used_perc = gas_used_perc(@transaction) %> +
    <%= gas_used(@transaction) %> <%= if gas_used_perc, do: "| #{gas_used_perc}%" %>
    +
    + +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Transaction number from the sending address. Each transaction sent from an address increments the nonce by 1.") %> + <%= gettext "Nonce" %>"><%= gettext "Position" %> +
    +
    <%= @transaction.nonce %><%= if block, do: @transaction.index, else: formatted_result(status) %>
    +
    + <%= unless value_transfer?(@transaction) do %> +
    +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Binary data included with the transaction. See input / logs below for additional info.") %> + <%= gettext "Raw Input" %> +
    +
    +
    + + +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + id: "tx-raw-input", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-no-borders", "btn-copy-icon-ml-0", "btn-copy-tx-raw-input", "tx-raw-input"], + clipboard_text: @transaction.input, + aria_label: gettext("Copy Value"), + title: gettext("Copy Txn Hex Input") %> + + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + additional_classes: ["btn-copy-icon-small", "btn-copy-icon-no-borders", "btn-copy-icon-ml-0", "btn-copy-tx-raw-input", "tx-utf8-input"], + clipboard_text: @transaction.input.bytes, + aria_label: gettext("Copy Value"), + title: gettext("Copy Txn UTF-8 Input"), + style: "display: none;" %> +
    +
    + +
    +
    +
    <%= @transaction.input %>
    +
    +
    + + +
    +
    + <% end %> +
    +
    +
    +
    + + <%= render BlockScoutWeb.Advertisement.BannersAdView, "_banner_728.html", conn: @conn %> + + <%= unless skip_decoding?(@transaction) do %> +
    +
    + <%= render BlockScoutWeb.TransactionView, "_decoded_input.html", Map.put(assigns, :decoded_input_data, decoded_input_data) %> +
    +
    + <% end %> + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/show_internal_transactions.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/show_internal_transactions.html.eex new file mode 100644 index 0000000..cbfb925 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/show_internal_transactions.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.TransactionInternalTransactionView, "index.html", assigns %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/show_token_transfers.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/show_token_transfers.html.eex new file mode 100644 index 0000000..b524c8e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/show_token_transfers.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.TransactionTokenTransferView, "index.html", assigns %> \ No newline at end of file diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_internal_transaction/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_internal_transaction/_metatags.html.eex new file mode 100644 index 0000000..85c3d66 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_internal_transaction/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.TransactionView, "_metatags.html", conn: @conn, transaction: @transaction %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex new file mode 100644 index 0000000..0194017 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex @@ -0,0 +1,29 @@ +
    + <%= render BlockScoutWeb.TransactionView, "overview.html", assigns %> +
    + <%= render BlockScoutWeb.TransactionView, "_tabs.html", assigns %> +
    +

    <%= gettext "Internal Transactions" %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + + +
    +
    + <%= gettext "There are no internal transactions for this transaction." %> +
    +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex new file mode 100644 index 0000000..e64f3bd --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex @@ -0,0 +1,139 @@ +
    + <% decoded_result = decode(@log, @transaction) %> + <%= case decoded_result do %> + <% {:error, :contract_not_verified, _cadidates} -> %> +
    + <%= gettext "To see accurate decoded input data, the contract must be verified." %> + <%= case @log do %> + <% %{address_hash: %Explorer.Chain.Hash{} = address_hash} -> %> + <% path = address_verify_contract_path(@conn, :new, address_hash) %> + <%= gettext "Verify the contract " %><%= gettext "here" %> + <% _ -> %> + <%= nil %> + <% end %> +
    + <% _ -> %> + <%= nil %> + <% end %> + +
    +
    <%= gettext "Address" %>
    +
    +

    + <% name = implementation_name(@log.address) || primary_name(@log.address)%> + <%= link( + (if name, do: name <> " | "<> to_string(@log.address), else: @log.address), + to: address_path(@conn, :show, @log.address), + "data-test": "log_address_link", + "data-address-hash": @log.address + ) %> +

    +
    + <%= case decoded_result do %> + <% {:error, :could_not_decode} -> %> +
    <%= gettext "Decoded" %>
    +
    +
    + <%= gettext "Failed to decode log data." %> +
    + <% {:error, :no_matching_function} -> %> + <%= nil %> + <% {:ok, method_id, text, mapping} -> %> +
    <%= gettext "Decoded" %>
    +
    + + + + + + + + + +
    Method Id0x<%= method_id %>
    Call<%= text %>
    + <%= render BlockScoutWeb.LogView, "_data_decoded_view.html", mapping: mapping %> + <% {:error, :contract_not_verified, results} -> %> + <%= for {:ok, method_id, text, mapping} <- results do %> +
    <%= gettext "Decoded" %>
    +
    + + + + + + + + + +
    Method Id0x<%= method_id %>
    Call<%= text %>
    + <%= render BlockScoutWeb.LogView, "_data_decoded_view.html", mapping: mapping %> + <% end %> + <% {:error, :contract_verified, results} -> %> + <%= for {:ok, method_id, text, mapping} <- results do %> +
    <%= gettext "Decoded" %>
    +
    + + + + + + + + + +
    Method Id0x<%= method_id %>
    Call<%= text %>
    + <%= render BlockScoutWeb.LogView, "_data_decoded_view.html", mapping: mapping %> + <% end %> + <% _ -> %> + <%= nil %> + <% end %> + +
    <%= gettext "Topics" %>
    +
    +
    + <%= unless is_nil(@log.first_topic) do %> +
    + [0] + <%= @log.first_topic %> +
    + <% end %> + <%= unless is_nil(@log.second_topic) do %> +
    + [1] + <%= @log.second_topic %> +
    + <% end %> + <%= unless is_nil(@log.third_topic) do %> +
    + [2] + <%= @log.third_topic %> +
    + <% end %> + <%= unless is_nil(@log.fourth_topic) do %> +
    + [3] + <%= @log.fourth_topic %> +
    + <% end %> +
    +
    +
    + <%= gettext "Data" %> +
    +
    + <%= unless is_nil(@log.data) do %> +
    + <%= @log.data %> +
    + <% end %> +
    +
    + <%= gettext "Log Index" %> +
    +
    +
    + <%= @log.index %> +
    +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_metatags.html.eex new file mode 100644 index 0000000..85c3d66 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.TransactionView, "_metatags.html", conn: @conn, transaction: @transaction %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/index.html.eex new file mode 100644 index 0000000..88db746 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/index.html.eex @@ -0,0 +1,32 @@ +
    + <%= render BlockScoutWeb.TransactionView, "overview.html", assigns %> + +
    + <%= render BlockScoutWeb.TransactionView, "_tabs.html", assigns %> + +
    +

    <%= gettext "Logs" %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + + + +
    +
    + <%= gettext "There are no logs for this transaction." %> +
    +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/_metatags.html.eex new file mode 100644 index 0000000..85c3d66 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.TransactionView, "_metatags.html", conn: @conn, transaction: @transaction %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/index.html.eex new file mode 100644 index 0000000..8bb5c03 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_raw_trace/index.html.eex @@ -0,0 +1,28 @@ +
    + <%= render BlockScoutWeb.TransactionView, "overview.html", assigns %> + +
    + <%= render BlockScoutWeb.TransactionView, "_tabs.html", assigns %> +
    +

    <%= gettext "Raw Trace" %> + <%= if Enum.count(@internal_transactions) > 0 do %> + <% raw_trace_text = for {line, _} <- raw_traces_with_lines(@internal_transactions), do: line %> + <%= render BlockScoutWeb.CommonComponentsView, "_btn_copy.html", + id: "tx-raw-trace-input", + additional_classes: ["tx-raw-input", "transaction-input"], + clipboard_text: raw_trace_text, + aria_label: gettext("Copy Value"), + title: gettext("Copy Raw Trace"), + style: "float: right;" %> + <% end %> +

    + <%= if Enum.count(@internal_transactions) > 0 do %> +
    <%= for {line, number} <- raw_traces_with_lines(@internal_transactions) do %>
    <%= line %>
    <% end %>
    + <% else %> +
    + <%= gettext "No trace entries found." %> +
    + <% end %> +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_metatags.html.eex new file mode 100644 index 0000000..85c3d66 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.TransactionView, "_metatags.html", conn: @conn, transaction: @transaction %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex new file mode 100644 index 0000000..180d51d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_state_change.html.eex @@ -0,0 +1,67 @@ + +<% coin_or_transfer = if @coin_or_token_transfers == :coin, do: :coin, else: elem(List.first(@coin_or_token_transfers), 1)%> +<%= if coin_or_transfer != :coin and coin_or_transfer.token.type != "ERC-20" or has_diff?(@balance_diff) do %> + + <%= if @address.hash == @burn_address_hash do %> + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("Address used in token mintings and burnings.") %> + <%= gettext("Burn address") %> +
    + + + <%= render BlockScoutWeb.AddressView, "_link.html", address: @address, contract: BlockScoutWeb.AddressView.contract?(@address), use_custom_tooltip: false %> + + + + <% else %> + <%= if Map.get(assigns, :miner) do %> + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_i_tooltip_2.html", + text: gettext("A block producer who successfully included the block onto the blockchain.") %> + <%= gettext("Miner") %> +
    + + + <%= render BlockScoutWeb.AddressView, "_link.html", address: @address, contract: false, use_custom_tooltip: false %> + + <% else %> + + + <%= render BlockScoutWeb.AddressView, "_link.html", address: @address, contract: BlockScoutWeb.AddressView.contract?(@address), use_custom_tooltip: false %> + + <% end %> + <%= if not_negative?(@balance_before) and not_negative?(@balance_after) do %> + + <%= display_value(@balance_before, coin_or_transfer) %> + + + <%= display_value(@balance_after, coin_or_transfer) %> + + <% else %> + + + <% end %> + <% end %> + + <%= if is_list(@coin_or_token_transfers) and elem(List.first(@coin_or_token_transfers), 1).token.type != "ERC-20" do %> + <%= for {type, transfer} <- @coin_or_token_transfers do %> + <%= case type do %> + <% :from -> %> +
    ▼ <%= display_nft(transfer) %>
    + <% :to -> %> +
    ▲ <%= display_nft(transfer) %>
    + <% end %> + <% end %> + <% else %> + <%= if not_negative?(@balance_diff) do %> + ▲ <%= display_value(@balance_diff, coin_or_transfer) %> + <% else %> + ▼ <%= display_value(absolute_value_of(@balance_diff), coin_or_transfer) %> + <% end %> + <% end %> + + +<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_token_balance.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_token_balance.html.eex new file mode 100644 index 0000000..6803a22 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/_token_balance.html.eex @@ -0,0 +1 @@ +<%= format_according_to_decimals(@balance, @transfer.token.decimals) %><%= " " %><%= render BlockScoutWeb.TransactionView, "_link_to_token_symbol.html", transfer: @transfer %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/index.html.eex new file mode 100644 index 0000000..c62dbb1 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_state/index.html.eex @@ -0,0 +1,51 @@ +
    + <%= render BlockScoutWeb.TransactionView, "overview.html", assigns %> +
    + <%= render BlockScoutWeb.TransactionView, "_tabs.html", assigns %> +
    +

    <%= gettext "State changes" %>

    + + <%= cond do %> + <% Chain.transaction_to_status(@transaction) == :pending -> %> +
    + <%= gettext "The changes from this transaction have not yet happened since the transaction is still pending." %> +
    + <% not has_state_changes?(@transaction) -> %> +
    + <%= gettext "This transaction hasn't changed state." %> +
    + <% true -> %> +
    +
    + + + + + + + + + + + + <%= render BlockScoutWeb.CommonComponentsView, "_table-loader.html", columns_num: 5 %> + +
    +
     
    +
    +
    <%= gettext "Address" %>
    +
    +
    <%= gettext "Balance before" %>
    +
    +
    <%= gettext "Balance after" %>
    +
    +
    <%= gettext "Change" %>
    +
    +
    +
    + <% end %> +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_metatags.html.eex new file mode 100644 index 0000000..85c3d66 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_metatags.html.eex @@ -0,0 +1 @@ +<%= render BlockScoutWeb.TransactionView, "_metatags.html", conn: @conn, transaction: @transaction %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex new file mode 100644 index 0000000..885990a --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex @@ -0,0 +1,20 @@ +
    +
    +
    + <%= render(BlockScoutWeb.CommonComponentsView, "_token_transfer_type_display_name.html", type: Chain.get_token_transfer_type(@token_transfer)) %> +
    + +
    + <%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @token_transfer.transaction_hash %> + + <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.from_address, contract: BlockScoutWeb.AddressView.contract?(@token_transfer.from_address), use_custom_tooltip: false %> + → + <%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.to_address, contract: BlockScoutWeb.AddressView.contract?(@token_transfer.to_address), use_custom_tooltip: false %> + + + + <%= render BlockScoutWeb.TransactionView, "_total_transfers.html", Map.put(assigns, :transfer, @token_transfer) %> + +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/index.html.eex new file mode 100644 index 0000000..46f8214 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/index.html.eex @@ -0,0 +1,32 @@ +
    + <%= render BlockScoutWeb.TransactionView, "overview.html", assigns %> + +
    + <%= render BlockScoutWeb.TransactionView, "_tabs.html", assigns %> +
    +

    <%= gettext "Token Transfers" %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + + + +
    +
    + <%= gettext "There are no token transfers for this transaction" %> +
    +
    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_tile-loader.html" %> +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> + +
    +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_contract.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_contract.html.eex new file mode 100644 index 0000000..dbd6471 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_contract.html.eex @@ -0,0 +1,61 @@ + + + + <%= render BlockScoutWeb.AddressView, + "_link.html", + address: @contract.address, + contract: true, + use_custom_tooltip: false + %> + + + + <%= balance(@contract.address) %> + + + + + <%= if @contract.address.transactions_count do %> + <%= Number.Delimit.number_to_delimited(@contract.address.transactions_count, precision: 0) %> + <% else %> + <%= gettext "N/A" %> + <% end %> + + + + + + <%= if @contract.is_vyper_contract, do: gettext("Vyper"), else: gettext("Solidity") %> + + + + + <%= @contract.compiler_version %> + + + + + <%= if @contract.optimization do %> + + <% else %> + + <% end %> + + + + <%= if @contract.constructor_arguments do %> + + <% else %> + + <% end %> + + + + + + + + <% market_cap_usd = if @token && @token.market_cap_usd, do: @token.market_cap_usd, else: gettext("N/A") %> + <%= market_cap_usd %> + + diff --git a/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_metatags.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_metatags.html.eex new file mode 100644 index 0000000..88e38e1 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_metatags.html.eex @@ -0,0 +1,8 @@ + + <%= gettext( + "Verified contracts - %{subnetwork} Explorer", + subnetwork: BlockScoutWeb.LayoutView.subnetwork_title() + ) %> + +"> +"> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_stats.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_stats.html.eex new file mode 100644 index 0000000..d69ebe0 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/_stats.html.eex @@ -0,0 +1,33 @@ +
    +
    +
    +
    +

    <%= gettext "Contracts" %>

    +
    +
    +

    <%= BlockScoutWeb.Cldr.Number.to_string!(@contracts_count, format: "#,###") %>

    + <%= gettext ("Total") %> +
    +
    +

    <%= if 0 |> Decimal.new() |> Decimal.lt?(@new_contracts_count), do: "+" %><%= BlockScoutWeb.Cldr.Number.to_string!(@new_contracts_count, format: "#,###") %>

    + <%= gettext ("Last 24h") %> +
    +
    +
    +
    +

    <%= gettext "Verified Contracts" %>

    +
    +
    +

    <%= BlockScoutWeb.Cldr.Number.to_string!(@verified_contracts_count, format: "#,###") %>

    + <%= gettext ("Total") %> +
    +
    +

    <%= if 0 |> Decimal.new() |> Decimal.lt?(@new_verified_contracts_count), do: "+" %><%= BlockScoutWeb.Cldr.Number.to_string!(@new_verified_contracts_count, format: "#,###") %>

    + <%= gettext ("Last 24h") %> +
    +
    +
    + +
    +
    +
    diff --git a/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/index.html.eex new file mode 100644 index 0000000..b8f801b --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/verified_contracts/index.html.eex @@ -0,0 +1,101 @@ +<%= render "_stats.html", assigns %> +
    + <%= render BlockScoutWeb.Advertisement.TextAdView, "index.html", conn: @conn %> +
    +
    +

    <%= gettext "Verified Contracts" %>

    + +
    + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: @page_number, show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + + + +
    + " placeholder="<%= gettext "Contract name or address" %>" id="search-text-input"> +
    + + + +
    +
    + + + + + + + + + + + + + + + + <%= render BlockScoutWeb.CommonComponentsView, "_table-loader.html", columns_num: 9 %> + +
    +
    <%= gettext "Address" %>
    +
    +
    <%= gettext "Balance" %>
    +
    +
    <%= gettext "Txns" %>
    +
    +
    <%= gettext "Compiler" %>
    +
    +
    <%= gettext "Version" %>
    +
    +
    <%= gettext "Optimization" %>
    +
    +
    <%= gettext "Constructor args" %>
    +
    +
    <%= gettext "Verified" %>
    +
    +
    <%= gettext "Market cap" %>
    +
    +
    +
    + +
    +
    +
    + + <%= gettext "There are no verified contracts." %> + +
    +
    + + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: @page_number, show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> +
    + +
    diff --git a/apps/block_scout_web/lib/block_scout_web/tracer.ex b/apps/block_scout_web/lib/block_scout_web/tracer.ex new file mode 100644 index 0000000..6bca34d --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/tracer.ex @@ -0,0 +1,5 @@ +defmodule BlockScoutWeb.Tracer do + @moduledoc false + + use Spandex.Tracer, otp_app: :block_scout_web +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex b/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex new file mode 100644 index 0000000..77f4fa3 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/abi_encoded_value_view.ex @@ -0,0 +1,213 @@ +defmodule BlockScoutWeb.ABIEncodedValueView do + @moduledoc """ + Renders a decoded value that is encoded according to an ABI. + + Does not leverage an eex template because it renders formatted + values via `
    ` tags, and that is hard to do in an eex template.
    +  """
    +  use BlockScoutWeb, :view
    +
    +  alias ABI.FunctionSelector
    +  alias Phoenix.HTML
    +
    +  require Logger
    +
    +  def value_html(type, value, no_links \\ false)
    +
    +  def value_html(type, value, no_links) do
    +    decoded_type = FunctionSelector.decode_type(type)
    +
    +    do_value_html(decoded_type, value, no_links)
    +  rescue
    +    exception ->
    +      Logger.warn(fn ->
    +        ["Error determining value html for #{inspect(type)}: ", Exception.format(:error, exception)]
    +      end)
    +
    +      :error
    +  end
    +
    +  def value_json(type, value) do
    +    decoded_type = FunctionSelector.decode_type(type)
    +
    +    do_value_json(decoded_type, value)
    +  rescue
    +    exception ->
    +      Logger.warn(fn ->
    +        ["Error determining value json for #{inspect(type)}: ", Exception.format(:error, exception)]
    +      end)
    +
    +      nil
    +  end
    +
    +  def copy_text(type, value) do
    +    decoded_type = FunctionSelector.decode_type(type)
    +
    +    do_copy_text(decoded_type, value)
    +  rescue
    +    exception ->
    +      Logger.warn(fn ->
    +        ["Error determining copy text for #{inspect(type)}: ", Exception.format(:error, exception)]
    +      end)
    +
    +      :error
    +  end
    +
    +  defp do_copy_text({:bytes, _type}, value) do
    +    hex(value)
    +  end
    +
    +  defp do_copy_text({:array, type, _}, value) do
    +    do_copy_text({:array, type}, value)
    +  end
    +
    +  defp do_copy_text({:array, type}, value) do
    +    values =
    +      value
    +      |> Enum.map(&do_copy_text(type, &1))
    +      |> Enum.intersperse(", ")
    +
    +    ~E|[<%= values %>]|
    +  end
    +
    +  defp do_copy_text(_, {:dynamic, value}) do
    +    hex(value)
    +  end
    +
    +  defp do_copy_text(type, value) when type in [:bytes, :address] do
    +    hex(value)
    +  end
    +
    +  defp do_copy_text({:tuple, types}, value) do
    +    values =
    +      value
    +      |> Tuple.to_list()
    +      |> Enum.with_index()
    +      |> Enum.map(fn {val, ind} -> do_copy_text(Enum.at(types, ind), val) end)
    +      |> Enum.intersperse(", ")
    +
    +    ~E|(<%= values %>)|
    +  end
    +
    +  defp do_copy_text(_type, value) do
    +    to_string(value)
    +  end
    +
    +  defp do_value_html(type, value, no_links, depth \\ 0)
    +
    +  defp do_value_html({:bytes, _}, value, no_links, depth) do
    +    do_value_html(:bytes, value, no_links, depth)
    +  end
    +
    +  defp do_value_html({:array, type, _}, value, no_links, depth) do
    +    do_value_html({:array, type}, value, no_links, depth)
    +  end
    +
    +  defp do_value_html({:array, type}, value, no_links, depth) do
    +    values =
    +      Enum.map(value, fn inner_value ->
    +        do_value_html(type, inner_value, no_links, depth + 1)
    +      end)
    +
    +    spacing = String.duplicate(" ", depth * 2)
    +    delimited = Enum.intersperse(values, ",\n")
    +
    +    ~E|<%= spacing %>[<%= "\n" %><%= delimited %><%= "\n" %><%= spacing %>]|
    +  end
    +
    +  defp do_value_html({:tuple, types}, values, no_links, _) do
    +    values_list =
    +      values
    +      |> Tuple.to_list()
    +      |> Enum.with_index()
    +      |> Enum.map(fn {value, i} ->
    +        do_value_html(Enum.at(types, i), value, no_links)
    +      end)
    +
    +    delimited = Enum.intersperse(values_list, ",")
    +    ~E|(<%= delimited %>)|
    +  end
    +
    +  defp do_value_html(type, value, no_links, depth) do
    +    spacing = String.duplicate(" ", depth * 2)
    +    ~E|<%= spacing %><%=base_value_html(type, value, no_links)%>|
    +    [spacing, base_value_html(type, value, no_links)]
    +  end
    +
    +  defp base_value_html(_, {:dynamic, value}, _no_links) do
    +    ~E|<%= hex(value) %>|
    +  end
    +
    +  defp base_value_html(:address, value, no_links) do
    +    if no_links do
    +      base_value_html(:address_text, value, no_links)
    +    else
    +      address = hex(value)
    +
    +      ~E|<%= address %>|
    +    end
    +  end
    +
    +  defp base_value_html(:address_text, value, _no_links) do
    +    ~E|<%= hex(value) %>|
    +  end
    +
    +  defp base_value_html(:bytes, value, _no_links) do
    +    ~E|<%= hex(value) %>|
    +  end
    +
    +  defp base_value_html(_, value, _no_links), do: HTML.html_escape(value)
    +
    +  defp do_value_json({:bytes, _}, value) do
    +    do_value_json(:bytes, value)
    +  end
    +
    +  defp do_value_json({:array, type, _}, value) do
    +    do_value_json({:array, type}, value)
    +  end
    +
    +  defp do_value_json({:array, type}, value) do
    +    values =
    +      Enum.map(value, fn inner_value ->
    +        do_value_json(type, inner_value)
    +      end)
    +
    +    values
    +  end
    +
    +  defp do_value_json({:tuple, types}, values) do
    +    values_list =
    +      values
    +      |> Tuple.to_list()
    +      |> Enum.with_index()
    +      |> Enum.map(fn {value, i} ->
    +        do_value_json(Enum.at(types, i), value)
    +      end)
    +
    +    values_list
    +  end
    +
    +  defp do_value_json(type, value) do
    +    base_value_json(type, value)
    +  end
    +
    +  defp base_value_json(_, {:dynamic, value}) do
    +    hex(value)
    +  end
    +
    +  defp base_value_json(:address, value) do
    +    hex(value)
    +  end
    +
    +  defp base_value_json(:address_text, value) do
    +    hex(value)
    +  end
    +
    +  defp base_value_json(:bytes, value) do
    +    hex(value)
    +  end
    +
    +  defp base_value_json(_, value), do: value
    +
    +  defp hex(value), do: "0x" <> Base.encode16(value, case: :lower)
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/access_helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/access_helpers.ex
    new file mode 100644
    index 0000000..ba89600
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/access_helpers.ex
    @@ -0,0 +1,177 @@
    +defmodule BlockScoutWeb.AccessHelpers do
    +  @moduledoc """
    +  Helpers to restrict access to some pages filtering by address
    +  """
    +
    +  import Phoenix.Controller
    +
    +  alias BlockScoutWeb.API.APILogger
    +  alias BlockScoutWeb.API.RPC.RPCView
    +  alias BlockScoutWeb.WebRouter.Helpers
    +  alias Explorer.Account.Api.Key, as: ApiKey
    +  alias Plug.Conn
    +
    +  alias RemoteIp
    +
    +  def restricted_access?(address_hash, params) do
    +    restricted_list_var = Application.get_env(:block_scout_web, :restricted_list)
    +    restricted_list = (restricted_list_var && String.split(restricted_list_var, ",")) || []
    +
    +    if Enum.count(restricted_list) > 0 do
    +      formatted_restricted_list =
    +        restricted_list
    +        |> Enum.map(fn addr ->
    +          String.downcase(addr)
    +        end)
    +
    +      formatted_address_hash = String.downcase(address_hash)
    +
    +      address_restricted =
    +        formatted_restricted_list
    +        |> Enum.member?(formatted_address_hash)
    +
    +      key = if params && Map.has_key?(params, "key"), do: Map.get(params, "key"), else: nil
    +      correct_key = key && key == Application.get_env(:block_scout_web, :restricted_list_key)
    +
    +      if address_restricted && !correct_key, do: {:restricted_access, true}, else: {:ok, false}
    +    else
    +      {:ok, false}
    +    end
    +  end
    +
    +  def get_path(conn, path, template, address_hash) do
    +    basic_args = [conn, template, address_hash]
    +    key = get_restricted_key(conn)
    +    # credo:disable-for-next-line
    +    full_args = if key, do: basic_args ++ [%{:key => key}], else: basic_args
    +
    +    apply(Helpers, path, full_args)
    +  end
    +
    +  def get_path(conn, path, template, address_hash, additional_params) do
    +    basic_args = [conn, template, address_hash]
    +    key = get_restricted_key(conn)
    +    full_additional_params = if key, do: Map.put(additional_params, :key, key), else: additional_params
    +    # credo:disable-for-next-line
    +    full_args = basic_args ++ [full_additional_params]
    +
    +    apply(Helpers, path, full_args)
    +  end
    +
    +  def handle_rate_limit_deny(conn) do
    +    APILogger.message("API rate limit reached")
    +
    +    conn
    +    |> Conn.put_status(429)
    +    |> put_view(RPCView)
    +    |> render(:error, %{error: "429 Too Many Requests"})
    +    |> Conn.halt()
    +  end
    +
    +  def check_rate_limit(conn) do
    +    if Mix.env() == :test do
    +      :ok
    +    else
    +      global_api_rate_limit = Application.get_env(:block_scout_web, :api_rate_limit)[:global_limit]
    +      api_rate_limit_by_key = Application.get_env(:block_scout_web, :api_rate_limit)[:api_rate_limit_by_key]
    +      api_rate_limit_by_ip = Application.get_env(:block_scout_web, :api_rate_limit)[:limit_by_ip]
    +      static_api_key = Application.get_env(:block_scout_web, :api_rate_limit)[:static_api_key]
    +
    +      remote_ip = conn.remote_ip
    +      remote_ip_from_headers = RemoteIp.from(conn.resp_headers)
    +      ip = remote_ip_from_headers || remote_ip
    +      ip_string = to_string(:inet_parse.ntoa(ip))
    +
    +      plan = get_plan(conn.query_params)
    +
    +      cond do
    +        check_api_key(conn) && get_api_key(conn) == static_api_key ->
    +          rate_limit_by_key(static_api_key, api_rate_limit_by_key)
    +
    +        check_api_key(conn) && !is_nil(plan) ->
    +          conn
    +          |> get_api_key()
    +          |> rate_limit_by_key(plan.max_req_per_second)
    +
    +        Enum.member?(api_rate_limit_whitelisted_ips(), ip_string) ->
    +          rate_limit_by_ip(ip_string, api_rate_limit_by_ip)
    +
    +        true ->
    +          global_rate_limit(global_api_rate_limit)
    +      end
    +    end
    +  end
    +
    +  defp check_api_key(conn) do
    +    conn.query_params && Map.has_key?(conn.query_params, "apikey")
    +  end
    +
    +  defp get_api_key(conn) do
    +    Map.get(conn.query_params, "apikey")
    +  end
    +
    +  defp get_plan(query_params) do
    +    with true <- query_params && Map.has_key?(query_params, "apikey"),
    +         api_key_value <- Map.get(query_params, "apikey"),
    +         api_key <- ApiKey.api_key_with_plan_by_value(api_key_value),
    +         false <- is_nil(api_key) do
    +      api_key.identity.plan
    +    else
    +      _ ->
    +        nil
    +    end
    +  end
    +
    +  defp rate_limit_by_key(api_key, api_rate_limit_by_key) do
    +    case Hammer.check_rate("api-#{api_key}", 1_000, api_rate_limit_by_key) do
    +      {:allow, _count} ->
    +        :ok
    +
    +      {:deny, _limit} ->
    +        :rate_limit_reached
    +    end
    +  end
    +
    +  defp rate_limit_by_ip(ip_string, api_rate_limit_by_ip) do
    +    case Hammer.check_rate("api-#{ip_string}", 1_000, api_rate_limit_by_ip) do
    +      {:allow, _count} ->
    +        :ok
    +
    +      {:deny, _limit} ->
    +        :rate_limit_reached
    +    end
    +  end
    +
    +  defp global_rate_limit(global_api_rate_limit) do
    +    case Hammer.check_rate("api", 1_000, global_api_rate_limit) do
    +      {:allow, _count} ->
    +        :ok
    +
    +      {:deny, _limit} ->
    +        :rate_limit_reached
    +    end
    +  end
    +
    +  defp get_restricted_key(%Phoenix.Socket{}) do
    +    nil
    +  end
    +
    +  defp get_restricted_key(conn) do
    +    conn_with_params = Conn.fetch_query_params(conn)
    +    conn_with_params.query_params["key"]
    +  end
    +
    +  defp api_rate_limit_whitelisted_ips do
    +    with api_rate_limit_object <-
    +           :block_scout_web
    +           |> Application.get_env(:api_rate_limit),
    +         {:ok, whitelisted_ips_string} <-
    +           api_rate_limit_object &&
    +             api_rate_limit_object
    +             |> Keyword.fetch(:whitelisted_ips) do
    +      if whitelisted_ips_string, do: String.split(whitelisted_ips_string, ","), else: []
    +    else
    +      _ -> []
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/api/v1/account_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/api/v1/account_view.ex
    new file mode 100644
    index 0000000..0e3a65e
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/api/v1/account_view.ex
    @@ -0,0 +1,7 @@
    +defmodule BlockScoutWeb.Account.Api.V1.AccountView do
    +  def render("message.json", %{message: message}) do
    +    %{
    +      "message" => message
    +    }
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/api/v1/tags_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/api/v1/tags_view.ex
    new file mode 100644
    index 0000000..d97e35f
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/api/v1/tags_view.ex
    @@ -0,0 +1,27 @@
    +defmodule BlockScoutWeb.Account.Api.V1.TagsView do
    +  def render("address_tags.json", %{tags_map: tags_map}) do
    +    tags_map
    +  end
    +
    +  def render("transaction_tags.json", %{
    +        tags_map: %{
    +          personal_tags: personal_tags,
    +          watchlist_names: watchlist_names,
    +          personal_tx_tag: personal_tx_tag,
    +          common_tags: common_tags
    +        }
    +      }) do
    +    %{
    +      personal_tx_tag: prepare_transaction_tag(personal_tx_tag),
    +      personal_tags: personal_tags,
    +      watchlist_names: watchlist_names,
    +      common_tags: common_tags
    +    }
    +  end
    +
    +  def prepare_transaction_tag(nil), do: nil
    +
    +  def prepare_transaction_tag(transaction_tag) do
    +    %{"label" => transaction_tag.name}
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/api/v1/user_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/api/v1/user_view.ex
    new file mode 100644
    index 0000000..9129903
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/api/v1/user_view.ex
    @@ -0,0 +1,141 @@
    +defmodule BlockScoutWeb.Account.Api.V1.UserView do
    +  alias BlockScoutWeb.Account.Api.V1.AccountView
    +  alias Ecto.Changeset
    +
    +  def render("message.json", assigns) do
    +    AccountView.render("message.json", assigns)
    +  end
    +
    +  def render("user_info.json", %{identity: identity}) do
    +    %{"name" => identity.name, "email" => identity.email, "avatar" => identity.avatar, "nickname" => identity.nickname}
    +  end
    +
    +  def render("watchlist_addresses.json", %{watchlist_addresses: watchlist_addresses, exchange_rate: exchange_rate}) do
    +    Enum.map(watchlist_addresses, &prepare_watchlist_address(&1, exchange_rate))
    +  end
    +
    +  def render("watchlist_address.json", %{watchlist_address: watchlist_address, exchange_rate: exchange_rate}) do
    +    prepare_watchlist_address(watchlist_address, exchange_rate)
    +  end
    +
    +  def render("address_tags.json", %{address_tags: address_tags}) do
    +    Enum.map(address_tags, &prepare_address_tag/1)
    +  end
    +
    +  def render("address_tag.json", %{address_tag: address_tag}) do
    +    prepare_address_tag(address_tag)
    +  end
    +
    +  def render("transaction_tags.json", %{transaction_tags: transaction_tags}) do
    +    Enum.map(transaction_tags, &prepare_transaction_tag/1)
    +  end
    +
    +  def render("transaction_tag.json", %{transaction_tag: transaction_tag}) do
    +    prepare_transaction_tag(transaction_tag)
    +  end
    +
    +  def render("api_keys.json", %{api_keys: api_keys}) do
    +    Enum.map(api_keys, &prepare_api_key/1)
    +  end
    +
    +  def render("api_key.json", %{api_key: api_key}) do
    +    prepare_api_key(api_key)
    +  end
    +
    +  def render("custom_abis.json", %{custom_abis: custom_abis}) do
    +    Enum.map(custom_abis, &prepare_custom_abi/1)
    +  end
    +
    +  def render("custom_abi.json", %{custom_abi: custom_abi}) do
    +    prepare_custom_abi(custom_abi)
    +  end
    +
    +  def render("public_tags_requests.json", %{public_tags_requests: public_tags_requests}) do
    +    Enum.map(public_tags_requests, &prepare_public_tags_request/1)
    +  end
    +
    +  def render("public_tags_request.json", %{public_tags_request: public_tags_request}) do
    +    prepare_public_tags_request(public_tags_request)
    +  end
    +
    +  def render("changeset_errors.json", %{changeset: changeset}) do
    +    %{
    +      "errors" =>
    +        Changeset.traverse_errors(changeset, fn {msg, opts} ->
    +          Regex.replace(~r"%{(\w+)}", msg, fn _, key ->
    +            opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
    +          end)
    +        end)
    +    }
    +  end
    +
    +  def prepare_watchlist_address(watchlist, exchange_rate) do
    +    %{
    +      "id" => watchlist.id,
    +      "address_hash" => watchlist.address_hash,
    +      "name" => watchlist.name,
    +      "address_balance" => if(watchlist.fetched_coin_balance, do: watchlist.fetched_coin_balance.value),
    +      "exchange_rate" => exchange_rate.usd_value,
    +      "notification_settings" => %{
    +        "native" => %{
    +          "incoming" => watchlist.watch_coin_input,
    +          "outcoming" => watchlist.watch_coin_output
    +        },
    +        "ERC-20" => %{
    +          "incoming" => watchlist.watch_erc_20_input,
    +          "outcoming" => watchlist.watch_erc_20_output
    +        },
    +        "ERC-721" => %{
    +          "incoming" => watchlist.watch_erc_721_input,
    +          "outcoming" => watchlist.watch_erc_721_output
    +        }
    +        # ,
    +        # "ERC-1155" => %{
    +        #   "incoming" => watchlist.watch_erc_1155_input,
    +        #   "outcoming" => watchlist.watch_erc_1155_output
    +        # }
    +      },
    +      "notification_methods" => %{
    +        "email" => watchlist.notify_email
    +      }
    +    }
    +  end
    +
    +  def prepare_custom_abi(custom_abi) do
    +    %{
    +      "id" => custom_abi.id,
    +      "contract_address_hash" => custom_abi.address_hash,
    +      "name" => custom_abi.name,
    +      "abi" => custom_abi.abi
    +    }
    +  end
    +
    +  def prepare_api_key(api_key) do
    +    %{"api_key" => api_key.value, "name" => api_key.name}
    +  end
    +
    +  def prepare_address_tag(address_tag) do
    +    %{"id" => address_tag.id, "address_hash" => address_tag.address_hash, "name" => address_tag.name}
    +  end
    +
    +  def prepare_transaction_tag(nil), do: nil
    +
    +  def prepare_transaction_tag(transaction_tag) do
    +    %{"id" => transaction_tag.id, "transaction_hash" => transaction_tag.tx_hash, "name" => transaction_tag.name}
    +  end
    +
    +  def prepare_public_tags_request(public_tags_request) do
    +    %{
    +      "id" => public_tags_request.id,
    +      "full_name" => public_tags_request.full_name,
    +      "email" => public_tags_request.email,
    +      "company" => public_tags_request.company,
    +      "website" => public_tags_request.website,
    +      "tags" => public_tags_request.tags,
    +      "addresses" => public_tags_request.addresses,
    +      "additional_comment" => public_tags_request.additional_comment,
    +      "is_owner" => public_tags_request.is_owner,
    +      "submission_date" => public_tags_request.inserted_at
    +    }
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/api_key_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/api_key_view.ex
    new file mode 100644
    index 0000000..a0b21b7
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/api_key_view.ex
    @@ -0,0 +1,5 @@
    +defmodule BlockScoutWeb.Account.ApiKeyView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.Account.Api.Key
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/auth_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/auth_view.ex
    new file mode 100644
    index 0000000..cfbeb00
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/auth_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.Account.AuthView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/common_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/common_view.ex
    new file mode 100644
    index 0000000..e296ca9
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/common_view.ex
    @@ -0,0 +1,11 @@
    +defmodule BlockScoutWeb.Account.CommonView do
    +  use BlockScoutWeb, :view
    +
    +  def nav_class(active_item, item) do
    +    if active_item == item do
    +      "dropdown-item active fs-14"
    +    else
    +      "dropdown-item fs-14"
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/custom_abi_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/custom_abi_view.ex
    new file mode 100644
    index 0000000..3b38eff
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/custom_abi_view.ex
    @@ -0,0 +1,22 @@
    +defmodule BlockScoutWeb.Account.CustomABIView do
    +  use BlockScoutWeb, :view
    +
    +  alias Ecto.Changeset
    +  alias Explorer.Account.CustomABI
    +
    +  def format_abi(custom_abi) do
    +    with {_type, abi} <- Changeset.fetch_field(custom_abi, :abi),
    +         false <- is_nil(abi),
    +         {:binary, false} <- {:binary, is_binary(abi)},
    +         {:ok, encoded_abi} <- Poison.encode(abi) do
    +      encoded_abi
    +    else
    +      {:binary, true} ->
    +        {_type, abi} = Changeset.fetch_field(custom_abi, :abi)
    +        abi
    +
    +      _ ->
    +        ""
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/public_tags_request_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/public_tags_request_view.ex
    new file mode 100644
    index 0000000..2a13dd8
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/public_tags_request_view.ex
    @@ -0,0 +1,70 @@
    +defmodule BlockScoutWeb.Account.PublicTagsRequestView do
    +  use BlockScoutWeb, :view
    +  use Phoenix.HTML
    +
    +  alias Explorer.Account.PublicTagsRequest
    +  alias Phoenix.HTML.Form
    +
    +  def array_input(form, field, attrs \\ []) do
    +    values = Form.input_value(form, field) || [""]
    +    id = Form.input_id(form, field)
    +
    +    content_tag :ul,
    +      id: container_id(id),
    +      data: [index: Enum.count(values), multiple_input_field_container: ""],
    +      class: "multiple-input-fields-container" do
    +      values
    +      |> Enum.map(fn v ->
    +        form_elements(form, field, to_string(v), attrs)
    +      end)
    +    end
    +  end
    +
    +  def array_add_button(form, field, attrs \\ []) do
    +    id = Form.input_id(form, field)
    +
    +    content =
    +      form
    +      |> form_elements(field, "", attrs)
    +      |> safe_to_string
    +
    +    data = [
    +      prototype: content,
    +      container: container_id(id)
    +    ]
    +
    +    content_tag(:button, render(BlockScoutWeb.CommonComponentsView, "_svg_plus.html"),
    +      data: data,
    +      class: "add-form-field"
    +    )
    +  end
    +
    +  defp form_elements(form, field, k, attrs) do
    +    type = Form.input_type(form, field)
    +    id = Form.input_id(form, field)
    +
    +    input_opts =
    +      [
    +        name: new_field_name(form, field),
    +        value: k,
    +        id: id,
    +        class: "form-control public-tags-address"
    +      ] ++ attrs
    +
    +    content_tag :li, class: "public-tags-address form-group" do
    +      [
    +        apply(Form, type, [form, field, input_opts]),
    +        content_tag(:button, render(BlockScoutWeb.CommonComponentsView, "_svg_minus.html"),
    +          data: [container: container_id(id)],
    +          class: "remove-form-field ml-1"
    +        )
    +      ]
    +    end
    +  end
    +
    +  defp container_id(id), do: id <> "_container"
    +
    +  defp new_field_name(form, field) do
    +    Form.input_name(form, field) <> "[]"
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/tag_address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/tag_address_view.ex
    new file mode 100644
    index 0000000..74886c3
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/tag_address_view.ex
    @@ -0,0 +1,7 @@
    +defmodule BlockScoutWeb.Account.TagAddressView do
    +  use BlockScoutWeb, :view
    +
    +  import BlockScoutWeb.AddressView, only: [trimmed_hash: 1]
    +
    +  alias Explorer.Account.TagAddress
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/tag_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/tag_transaction_view.ex
    new file mode 100644
    index 0000000..7edfa1e
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/tag_transaction_view.ex
    @@ -0,0 +1,5 @@
    +defmodule BlockScoutWeb.Account.TagTransactionView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.Account.TagTransaction
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/watchlist_address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/watchlist_address_view.ex
    new file mode 100644
    index 0000000..f3f5383
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/watchlist_address_view.ex
    @@ -0,0 +1,11 @@
    +defmodule BlockScoutWeb.Account.WatchlistAddressView do
    +  use BlockScoutWeb, :view
    +  import BlockScoutWeb.AddressView, only: [trimmed_hash: 1]
    +  import BlockScoutWeb.WeiHelpers, only: [format_wei_value: 2]
    +
    +  def balance_ether(nil), do: ""
    +
    +  def balance_ether(balance) do
    +    format_wei_value(balance, :ether)
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/account/watchlist_view.ex b/apps/block_scout_web/lib/block_scout_web/views/account/watchlist_view.ex
    new file mode 100644
    index 0000000..fa39663
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/account/watchlist_view.ex
    @@ -0,0 +1,17 @@
    +defmodule BlockScoutWeb.Account.WatchlistView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.Account.WatchlistAddressView
    +  alias Explorer.Account.WatchlistAddress
    +  alias Explorer.ExchangeRates.Token
    +  alias Explorer.Market
    +  alias Indexer.Fetcher.CoinBalanceOnDemand
    +
    +  def coin_balance_status(address) do
    +    CoinBalanceOnDemand.trigger_fetch(address)
    +  end
    +
    +  def exchange_rate do
    +    Market.get_exchange_rate(Explorer.coin()) || Token.null()
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex
    new file mode 100644
    index 0000000..0551d94
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_coin_balance_view.ex
    @@ -0,0 +1,33 @@
    +defmodule BlockScoutWeb.AddressCoinBalanceView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.AccessHelpers
    +  alias Explorer.Chain.Wei
    +
    +  def format(%Wei{} = value) do
    +    format_wei_value(value, :ether)
    +  end
    +
    +  def delta_arrow(value) do
    +    if value.sign == 1 do
    +      "▲"
    +    else
    +      "▼"
    +    end
    +  end
    +
    +  def delta_sign(value) do
    +    if value.sign == 1 do
    +      "Positive"
    +    else
    +      "Negative"
    +    end
    +  end
    +
    +  def format_delta(%Decimal{} = value) do
    +    value
    +    |> Decimal.abs()
    +    |> Wei.from(:wei)
    +    |> format_wei_value(:ether)
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_common_fields_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_common_fields_view.ex
    new file mode 100644
    index 0000000..9339807
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_common_fields_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.AddressContractVerificationCommonFieldsView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_flattened_code_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_flattened_code_view.ex
    new file mode 100644
    index 0000000..3a120c5
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_flattened_code_view.ex
    @@ -0,0 +1,6 @@
    +defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.Chain
    +  alias Explorer.Chain.SmartContract
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_json_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_json_view.ex
    new file mode 100644
    index 0000000..79f8681
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_json_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.AddressContractVerificationViaJsonView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_multi_part_files_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_multi_part_files_view.ex
    new file mode 100644
    index 0000000..7ec8e9b
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_multi_part_files_view.ex
    @@ -0,0 +1,6 @@
    +defmodule BlockScoutWeb.AddressContractVerificationViaMultiPartFilesView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.Chain
    +  alias Explorer.Chain.SmartContract
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_standard_json_input_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_standard_json_input_view.ex
    new file mode 100644
    index 0000000..cf45efe
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_standard_json_input_view.ex
    @@ -0,0 +1,6 @@
    +defmodule BlockScoutWeb.AddressContractVerificationViaStandardJsonInputView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.Chain
    +  alias Explorer.Chain.SmartContract
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_view.ex
    new file mode 100644
    index 0000000..385d76c
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_view.ex
    @@ -0,0 +1,5 @@
    +defmodule BlockScoutWeb.AddressContractVerificationView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.SmartContract.RustVerifierInterface
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex
    new file mode 100644
    index 0000000..e0ebba9
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex
    @@ -0,0 +1,6 @@
    +defmodule BlockScoutWeb.AddressContractVerificationVyperView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.Chain
    +  alias Explorer.Chain.SmartContract
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex
    new file mode 100644
    index 0000000..8856d14
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex
    @@ -0,0 +1,151 @@
    +defmodule BlockScoutWeb.AddressContractView do
    +  use BlockScoutWeb, :view
    +
    +  alias ABI.{FunctionSelector, TypeDecoder}
    +  alias Explorer.Chain
    +  alias Explorer.Chain.{Address, Data, InternalTransaction, Transaction}
    +
    +  def render("scripts.html", %{conn: conn}) do
    +    render_scripts(conn, "address_contract/code_highlighting.js")
    +  end
    +
    +  def format_smart_contract_abi(abi) when not is_nil(abi), do: Poison.encode!(abi, %{pretty: false})
    +
    +  @doc """
    +  Returns the correct format for the optimization text.
    +
    +    iex> BlockScoutWeb.AddressContractView.format_optimization_text(true)
    +    "true"
    +
    +    iex> BlockScoutWeb.AddressContractView.format_optimization_text(false)
    +    "false"
    +  """
    +  def format_optimization_text(true), do: gettext("true")
    +  def format_optimization_text(false), do: gettext("false")
    +
    +  def format_constructor_arguments(contract, conn) do
    +    constructor_abi = Enum.find(contract.abi, fn el -> el["type"] == "constructor" && el["inputs"] != [] end)
    +
    +    input_types = Enum.map(constructor_abi["inputs"], &FunctionSelector.parse_specification_type/1)
    +
    +    {_, result} =
    +      contract.constructor_arguments
    +      |> decode_data(input_types)
    +      |> Enum.zip(constructor_abi["inputs"])
    +      |> Enum.reduce({0, "#{contract.constructor_arguments}\n\n"}, fn {val, %{"type" => type}}, {count, acc} ->
    +        formatted_val = val_to_string(val, type, conn)
    +
    +        {count + 1, "#{acc}Arg [#{count}] (#{type}) : #{formatted_val}\n"}
    +      end)
    +
    +    result
    +  rescue
    +    _ -> contract.constructor_arguments
    +  end
    +
    +  defp val_to_string(val, type, conn) do
    +    cond do
    +      type =~ "[]" ->
    +        if is_list(val) or is_tuple(val) do
    +          "[" <>
    +            Enum.map_join(val, ", ", fn el -> val_to_string(el, String.replace_suffix(type, "[]", ""), conn) end) <> "]"
    +        else
    +          to_string(val)
    +        end
    +
    +      type =~ "address" ->
    +        address_hash = "0x" <> Base.encode16(val, case: :lower)
    +
    +        address = get_address(address_hash)
    +
    +        get_formatted_address_data(address, address_hash, conn)
    +
    +      type =~ "bytes" ->
    +        Base.encode16(val, case: :lower)
    +
    +      true ->
    +        to_string(val)
    +    end
    +  end
    +
    +  defp get_address(address_hash) do
    +    case Chain.string_to_address_hash(address_hash) do
    +      {:ok, address} -> address
    +      _ -> nil
    +    end
    +  end
    +
    +  defp get_formatted_address_data(address, address_hash, conn) do
    +    if address != nil do
    +      "" <> address_hash <> ""
    +    else
    +      address_hash
    +    end
    +  end
    +
    +  defp decode_data("0x" <> encoded_data, types) do
    +    decode_data(encoded_data, types)
    +  end
    +
    +  defp decode_data(encoded_data, types) do
    +    encoded_data
    +    |> Base.decode16!(case: :mixed)
    +    |> TypeDecoder.decode_raw(types)
    +  end
    +
    +  def format_external_libraries(libraries, conn) do
    +    Enum.reduce(libraries, "", fn %{name: name, address_hash: address_hash}, acc ->
    +      address = get_address(address_hash)
    +      "#{acc}#{name} : #{get_formatted_address_data(address, address_hash, conn)}  \n"
    +    end)
    +  end
    +
    +  def contract_lines_with_index(source_code) do
    +    contract_lines =
    +      source_code
    +      |> String.split("\n")
    +
    +    max_digits =
    +      contract_lines
    +      |> Enum.count()
    +      |> Integer.digits()
    +      |> Enum.count()
    +
    +    contract_lines
    +    |> Enum.with_index(1)
    +    |> Enum.map(fn {value, line} ->
    +      {value, String.pad_leading(to_string(line), max_digits, " ")}
    +    end)
    +  end
    +
    +  def contract_creation_code(%Address{
    +        contract_code: %Data{bytes: <<>>},
    +        contracts_creation_internal_transaction: %InternalTransaction{init: init}
    +      }) do
    +    {:selfdestructed, init}
    +  end
    +
    +  def contract_creation_code(%Address{contract_code: contract_code}) do
    +    {:ok, contract_code}
    +  end
    +
    +  def creation_code(%Address{contracts_creation_internal_transaction: %InternalTransaction{}} = address) do
    +    address.contracts_creation_internal_transaction.input
    +  end
    +
    +  def creation_code(%Address{contracts_creation_transaction: %Transaction{}} = address) do
    +    address.contracts_creation_transaction.input
    +  end
    +
    +  def creation_code(%Address{contracts_creation_transaction: nil}) do
    +    nil
    +  end
    +
    +  def sourcify_repo_url(address_hash, partial_match) do
    +    checksummed_hash = Address.checksum(address_hash)
    +    chain_id = Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:chain_id]
    +    repo_url = Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:repo_url]
    +    match = if partial_match, do: "/partial_match/", else: "/full_match/"
    +    repo_url <> match <> chain_id <> "/" <> checksummed_hash <> "/"
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex
    new file mode 100644
    index 0000000..4e06e37
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex
    @@ -0,0 +1,272 @@
    +defmodule BlockScoutWeb.AddressDecompiledContractView do
    +  use BlockScoutWeb, :view
    +
    +  @colors %{
    +    "\e[95m" => "",
    +    # red
    +    "\e[91m" => "",
    +    # gray
    +    "\e[38;5;8m" => "",
    +    # green
    +    "\e[32m" => "",
    +    # yellowgreen
    +    "\e[93m" => "",
    +    # yellow
    +    "\e[92m" => "",
    +    # red
    +    "\e[94m" => ""
    +  }
    +
    +  @comment_start "#"
    +
    +  @reserved_words_types [
    +    "var",
    +    "bool",
    +    "string",
    +    "int",
    +    "uint",
    +    "int8",
    +    "uint8",
    +    "int16",
    +    "uint16",
    +    "int24",
    +    "uint24",
    +    "int32",
    +    "uint32",
    +    "int40",
    +    "uint40",
    +    "int48",
    +    "uint48",
    +    "int56",
    +    "uint56",
    +    "int64",
    +    "uint64",
    +    "int72",
    +    "uint72",
    +    "int80",
    +    "uint80",
    +    "int88",
    +    "uint88",
    +    "int96",
    +    "uint96",
    +    "int104",
    +    "uint104",
    +    "int112",
    +    "uint112",
    +    "int120",
    +    "uint120",
    +    "int128",
    +    "uint128",
    +    "int136",
    +    "uint136",
    +    "int144",
    +    "uint144",
    +    "int152",
    +    "uint152",
    +    "int160",
    +    "uint160",
    +    "int168",
    +    "uint168",
    +    "int176",
    +    "uint176",
    +    "int184",
    +    "uint184",
    +    "int192",
    +    "uint192",
    +    "int200",
    +    "uint200",
    +    "int208",
    +    "uint208",
    +    "int216",
    +    "uint216",
    +    "int224",
    +    "uint224",
    +    "int232",
    +    "uint232",
    +    "int240",
    +    "uint240",
    +    "int248",
    +    "uint248",
    +    "int256",
    +    "uint256",
    +    "byte",
    +    "bytes",
    +    "bytes1",
    +    "bytes2",
    +    "bytes3",
    +    "bytes4",
    +    "bytes5",
    +    "bytes6",
    +    "bytes7",
    +    "bytes8",
    +    "bytes9",
    +    "bytes10",
    +    "bytes11",
    +    "bytes12",
    +    "bytes13",
    +    "bytes14",
    +    "bytes15",
    +    "bytes16",
    +    "bytes17",
    +    "bytes18",
    +    "bytes19",
    +    "bytes20",
    +    "bytes21",
    +    "bytes22",
    +    "bytes23",
    +    "bytes24",
    +    "bytes25",
    +    "bytes26",
    +    "bytes27",
    +    "bytes28",
    +    "bytes29",
    +    "bytes30",
    +    "bytes31",
    +    "bytes32",
    +    "true",
    +    "false",
    +    "enum",
    +    "struct",
    +    "mapping",
    +    "address"
    +  ]
    +
    +  @reserved_words_keywords [
    +    "def",
    +    "require",
    +    "revert",
    +    "return",
    +    "assembly",
    +    "memory",
    +    "mem"
    +  ]
    +
    +  @modifiers [
    +    "payable",
    +    "public",
    +    "view",
    +    "pure",
    +    "returns",
    +    "internal"
    +  ]
    +
    +  @reserved_words @reserved_words_keywords ++ @reserved_words_types
    +
    +  @reserved_words_regexp ([@comment_start | @reserved_words] ++ @modifiers)
    +                         |> Enum.reduce("", fn el, acc -> acc <> "|" <> el end)
    +                         |> Regex.compile!()
    +
    +  def highlight_decompiled_code(code) do
    +    {_, result} =
    +      @colors
    +      |> Enum.reduce(code, fn {symbol, rgb}, acc ->
    +        String.replace(acc, symbol, rgb)
    +      end)
    +      |> String.replace("\e[1m", "")
    +      |> String.replace("»", "»")
    +      |> String.replace("\e[0m", "")
    +      |> String.split(~r/\|\|\<\/span\>/,
    +        include_captures: true,
    +        trim: true
    +      )
    +      |> add_styles_to_every_line()
    +
    +    result
    +    |> Enum.reduce("", fn part, acc ->
    +      part <> acc
    +    end)
    +    |> add_styles_to_reserved_words()
    +    |> add_line_numbers()
    +  end
    +
    +  defp add_styles_to_every_line(lines) do
    +    lines
    +    |> Enum.reduce({"", []}, fn part, {style, acc} ->
    +      new_style =
    +        cond do
    +          String.contains?(part, " part
    +          part == "" -> ""
    +          true -> style
    +        end
    +
    +      new_part = new_part(part, new_style)
    +
    +      {new_style, [new_part | acc]}
    +    end)
    +  end
    +
    +  defp add_styles_to_reserved_words(code) do
    +    code
    +    |> String.split("\n")
    +    |> Enum.map(fn line ->
    +      add_styles_to_line(line)
    +    end)
    +    |> Enum.reduce("", fn el, acc ->
    +      acc <> el <> "\n"
    +    end)
    +  end
    +
    +  defp add_styles_to_line(line) do
    +    parts =
    +      line
    +      |> String.split(@reserved_words_regexp,
    +        include_captures: true
    +      )
    +
    +    comment_position = Enum.find_index(parts, fn part -> part == "#" end)
    +
    +    parts
    +    |> Enum.with_index()
    +    |> Enum.map(fn {el, index} ->
    +      cond do
    +        !(is_nil(comment_position) || comment_position > index) -> el
    +        el in @reserved_words -> "" <> el <> ""
    +        el in @modifiers -> "" <> el <> ""
    +        true -> el
    +      end
    +    end)
    +    |> Enum.reduce("", fn el, acc ->
    +      acc <> el
    +    end)
    +  end
    +
    +  def last_decompiled_contract_version(decompiled_contracts) when is_nil(decompiled_contracts), do: nil
    +
    +  def last_decompiled_contract_version(decompiled_contracts) when decompiled_contracts == [], do: nil
    +
    +  def last_decompiled_contract_version(decompiled_contracts) do
    +    Enum.max_by(decompiled_contracts, & &1.decompiler_version)
    +  end
    +
    +  defp add_line_numbers(code) do
    +    code
    +    |> String.split("\n")
    +    |> Enum.reduce("", fn line, acc ->
    +      acc <> "#{line}\n"
    +    end)
    +  end
    +
    +  defp new_part(part, new_style) do
    +    cond do
    +      part == "" ->
    +        ""
    +
    +      part == "" ->
    +        ""
    +
    +      part == new_style ->
    +        ""
    +
    +      new_style == "" ->
    +        part
    +
    +      true ->
    +        part
    +        |> String.split("\n")
    +        |> Enum.reduce("", fn p, a ->
    +          a <> new_style <> p <> "\n"
    +        end)
    +        |> String.slice(0..-2)
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex
    new file mode 100644
    index 0000000..092e223
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_internal_transaction_view.ex
    @@ -0,0 +1,14 @@
    +defmodule BlockScoutWeb.AddressInternalTransactionView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.AccessHelpers
    +  alias Explorer.Chain.Address
    +
    +  def format_current_filter(filter) do
    +    case filter do
    +      "to" -> gettext("To")
    +      "from" -> gettext("From")
    +      _ -> gettext("All")
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex
    new file mode 100644
    index 0000000..577661a
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex
    @@ -0,0 +1,9 @@
    +defmodule BlockScoutWeb.AddressLogsView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.Chain.{Address, Log}
    +
    +  def decode(log, transaction) do
    +    Log.decode(log, transaction)
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex
    new file mode 100644
    index 0000000..559af20
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.AddressReadContractView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex
    new file mode 100644
    index 0000000..e51247f
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_read_proxy_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.AddressReadProxyView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_token_balance_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_token_balance_view.ex
    new file mode 100644
    index 0000000..d070ddf
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_token_balance_view.ex
    @@ -0,0 +1,133 @@
    +defmodule BlockScoutWeb.AddressTokenBalanceView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.AccessHelpers
    +  alias Explorer.Chain
    +  alias Explorer.Chain.Address
    +  alias Explorer.Counters.AddressTokenUsdSum
    +
    +  def tokens_count_title(token_balances) do
    +    ngettext("%{count} token", "%{count} tokens", Enum.count(token_balances))
    +  end
    +
    +  def filter_by_type(token_balances, type) do
    +    Enum.filter(token_balances, fn {_token_balance, token} -> token.type == type end)
    +  end
    +
    +  @doc """
    +  Sorts the given list of tokens in alphabetically order considering nil values in the bottom of
    +  the list.
    +  """
    +  def sort_by_name(token_balances) do
    +    {unnamed, named} = Enum.split_with(token_balances, &is_nil(&1.token.name))
    +    Enum.sort_by(named, &String.downcase(&1.token.name)) ++ unnamed
    +  end
    +
    +  @doc """
    +  Sorts the given list of tokens by usd_value of token in descending order and alphabetically order considering nil values in the bottom of
    +  the list.
    +  """
    +  def sort_by_usd_value_and_name(token_balances) do
    +    token_balances
    +    |> Enum.sort(fn {token_balance1, token1}, {token_balance2, token2} ->
    +      usd_value1 = token1.usd_value
    +      usd_value2 = token2.usd_value
    +
    +      token_name1 = token1.name
    +      token_name2 = token2.name
    +
    +      sort_by_name = sort_2_tokens_by_name(token_name1, token_name2)
    +
    +      sort_2_tokens_by_value_desc_and_name(
    +        token_balance1,
    +        token_balance2,
    +        usd_value1,
    +        usd_value2,
    +        sort_by_name,
    +        token1,
    +        token2
    +      )
    +    end)
    +  end
    +
    +  defp sort_2_tokens_by_name(token_name1, token_name2) do
    +    cond do
    +      token_name1 && token_name2 ->
    +        String.downcase(token_name1) <= String.downcase(token_name2)
    +
    +      token_name1 && is_nil(token_name2) ->
    +        true
    +
    +      is_nil(token_name1) && token_name2 ->
    +        false
    +
    +      true ->
    +        true
    +    end
    +  end
    +
    +  defp sort_2_tokens_by_value_desc_and_name(
    +         token_balance1,
    +         token_balance2,
    +         usd_value1,
    +         usd_value2,
    +         sort_by_name,
    +         token1,
    +         token2
    +       )
    +       when not is_nil(usd_value1) and not is_nil(usd_value2) do
    +    case Decimal.compare(Chain.balance_in_usd(token_balance1, token1), Chain.balance_in_usd(token_balance2, token2)) do
    +      :gt ->
    +        true
    +
    +      :eq ->
    +        sort_by_name
    +
    +      :lt ->
    +        false
    +    end
    +  end
    +
    +  defp sort_2_tokens_by_value_desc_and_name(
    +         _token_balance1,
    +         _token_balance2,
    +         usd_value1,
    +         usd_value2,
    +         _sort_by_name,
    +         _token1,
    +         _token2
    +       )
    +       when not is_nil(usd_value1) and is_nil(usd_value2) do
    +    true
    +  end
    +
    +  defp sort_2_tokens_by_value_desc_and_name(
    +         _token_balance1,
    +         _token_balance2,
    +         usd_value1,
    +         usd_value2,
    +         _sort_by_name,
    +         _token1,
    +         _token2
    +       )
    +       when is_nil(usd_value1) and not is_nil(usd_value2) do
    +    false
    +  end
    +
    +  defp sort_2_tokens_by_value_desc_and_name(
    +         _token_balance1,
    +         _token_balance2,
    +         usd_value1,
    +         usd_value2,
    +         sort_by_name,
    +         _token1,
    +         _token2
    +       )
    +       when is_nil(usd_value1) and is_nil(usd_value2) do
    +    sort_by_name
    +  end
    +
    +  def address_tokens_usd_sum_cache(address, token_balances) do
    +    AddressTokenUsdSum.fetch(address, token_balances)
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex
    new file mode 100644
    index 0000000..ca82caa
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_token_transfer_view.ex
    @@ -0,0 +1,14 @@
    +defmodule BlockScoutWeb.AddressTokenTransferView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.AccessHelpers
    +  alias Explorer.Chain.Address
    +
    +  def format_current_filter(filter) do
    +    case filter do
    +      "to" -> gettext("To")
    +      "from" -> gettext("From")
    +      _ -> gettext("All")
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex
    new file mode 100644
    index 0000000..83cdf79
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex
    @@ -0,0 +1,7 @@
    +defmodule BlockScoutWeb.AddressTokenView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.{AddressView, ChainView}
    +  alias Explorer.Chain
    +  alias Explorer.Chain.{Address, Wei}
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex
    new file mode 100644
    index 0000000..64ba591
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_transaction_view.ex
    @@ -0,0 +1,14 @@
    +defmodule BlockScoutWeb.AddressTransactionView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.AccessHelpers
    +  alias Explorer.Chain.Address
    +
    +  def format_current_filter(filter) do
    +    case filter do
    +      "to" -> gettext("To")
    +      "from" -> gettext("From")
    +      _ -> gettext("All")
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex
    new file mode 100644
    index 0000000..65f2bb0
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_validation_view.ex
    @@ -0,0 +1,5 @@
    +defmodule BlockScoutWeb.AddressValidationView do
    +  use BlockScoutWeb, :view
    +
    +  # import BlockScoutWeb.AddressView, only: [contract?: 1, smart_contract_verified?: 1]
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex
    new file mode 100644
    index 0000000..8fff82f
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex
    @@ -0,0 +1,481 @@
    +defmodule BlockScoutWeb.AddressView do
    +  use BlockScoutWeb, :view
    +
    +  require Logger
    +
    +  alias BlockScoutWeb.{AccessHelpers, LayoutView}
    +  alias Explorer.Account.CustomABI
    +  alias Explorer.{Chain, CustomContractsHelpers, Repo}
    +  alias Explorer.Chain.{Address, Hash, InternalTransaction, SmartContract, Token, TokenTransfer, Transaction, Wei}
    +  alias Explorer.Chain.Block.Reward
    +  alias Explorer.ExchangeRates.Token, as: TokenExchangeRate
    +  alias Explorer.SmartContract.{Helper, Writer}
    +
    +  import BlockScoutWeb.Account.AuthController, only: [current_user: 1]
    +
    +  @dialyzer :no_match
    +
    +  @tabs [
    +    "coin-balances",
    +    "contracts",
    +    "decompiled-contracts",
    +    "internal-transactions",
    +    "token-transfers",
    +    "read-contract",
    +    "read-proxy",
    +    "write-contract",
    +    "write-proxy",
    +    "tokens",
    +    "transactions",
    +    "validations"
    +  ]
    +
    +  def address_partial_selector(struct_to_render_from, direction, current_address, truncate \\ false)
    +
    +  def address_partial_selector(%Address{} = address, _, current_address, truncate) do
    +    matching_address_check(current_address, address, contract?(address), truncate)
    +  end
    +
    +  def address_partial_selector(
    +        %InternalTransaction{to_address_hash: nil, created_contract_address_hash: nil},
    +        :to,
    +        _current_address,
    +        _truncate
    +      ) do
    +    gettext("Contract Address Pending")
    +  end
    +
    +  def address_partial_selector(
    +        %InternalTransaction{to_address: nil, created_contract_address: contract_address},
    +        :to,
    +        current_address,
    +        truncate
    +      ) do
    +    matching_address_check(current_address, contract_address, true, truncate)
    +  end
    +
    +  def address_partial_selector(%InternalTransaction{to_address: address}, :to, current_address, truncate) do
    +    matching_address_check(current_address, address, contract?(address), truncate)
    +  end
    +
    +  def address_partial_selector(%InternalTransaction{from_address: address}, :from, current_address, truncate) do
    +    matching_address_check(current_address, address, contract?(address), truncate)
    +  end
    +
    +  def address_partial_selector(%TokenTransfer{to_address: address}, :to, current_address, truncate) do
    +    matching_address_check(current_address, address, contract?(address), truncate)
    +  end
    +
    +  def address_partial_selector(%TokenTransfer{from_address: address}, :from, current_address, truncate) do
    +    matching_address_check(current_address, address, contract?(address), truncate)
    +  end
    +
    +  def address_partial_selector(
    +        %Transaction{to_address_hash: nil, created_contract_address_hash: nil},
    +        :to,
    +        _current_address,
    +        _truncate
    +      ) do
    +    gettext("Contract Address Pending")
    +  end
    +
    +  def address_partial_selector(
    +        %Transaction{to_address: nil, created_contract_address: contract_address},
    +        :to,
    +        current_address,
    +        truncate
    +      ) do
    +    matching_address_check(current_address, contract_address, true, truncate)
    +  end
    +
    +  def address_partial_selector(%Transaction{to_address: address}, :to, current_address, truncate) do
    +    matching_address_check(current_address, address, contract?(address), truncate)
    +  end
    +
    +  def address_partial_selector(%Transaction{from_address: address}, :from, current_address, truncate) do
    +    matching_address_check(current_address, address, contract?(address), truncate)
    +  end
    +
    +  def address_partial_selector(%Reward{address: address}, _, current_address, truncate) do
    +    matching_address_check(current_address, address, false, truncate)
    +  end
    +
    +  def address_title(%Address{} = address) do
    +    if contract?(address) do
    +      gettext("Contract Address")
    +    else
    +      gettext("Address")
    +    end
    +  end
    +
    +  @doc """
    +  Returns a formatted address balance and includes the unit.
    +  """
    +  def balance(%Address{fetched_coin_balance: nil}), do: ""
    +
    +  def balance(%Address{fetched_coin_balance: balance}) do
    +    format_wei_value(balance, :ether)
    +  end
    +
    +  def balance_percentage_enabled?(total_supply) do
    +    Application.get_env(:block_scout_web, :show_percentage) && total_supply > 0
    +  end
    +
    +  def balance_percentage(_, nil), do: ""
    +
    +  def balance_percentage(
    +        %Address{
    +          hash: %Explorer.Chain.Hash{
    +            byte_count: 20,
    +            bytes: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>>
    +          }
    +        },
    +        _
    +      ),
    +      do: ""
    +
    +  def balance_percentage(%Address{fetched_coin_balance: balance}, total_supply) do
    +    if Decimal.compare(total_supply, 0) == :gt do
    +      balance
    +      |> Wei.to(:ether)
    +      |> Decimal.div(Decimal.new(total_supply))
    +      |> Decimal.mult(100)
    +      |> Decimal.round(4)
    +      |> Decimal.to_string(:normal)
    +      |> Kernel.<>("% #{gettext("Market Cap")}")
    +    else
    +      balance
    +      |> Wei.to(:ether)
    +      |> Decimal.to_string(:normal)
    +    end
    +  end
    +
    +  def empty_exchange_rate?(exchange_rate) do
    +    TokenExchangeRate.null?(exchange_rate)
    +  end
    +
    +  def balance_percentage(%Address{fetched_coin_balance: _} = address) do
    +    balance_percentage(address, Chain.total_supply())
    +  end
    +
    +  def balance_block_number(%Address{fetched_coin_balance_block_number: nil}), do: ""
    +
    +  def balance_block_number(%Address{fetched_coin_balance_block_number: fetched_coin_balance_block_number}) do
    +    to_string(fetched_coin_balance_block_number)
    +  end
    +
    +  def contract?(%Address{contract_code: nil}), do: false
    +
    +  def contract?(%Address{contract_code: _}), do: true
    +
    +  def contract?(nil), do: true
    +
    +  def validator?(val) when val > 0, do: true
    +
    +  def validator?(_), do: false
    +
    +  def hash(%Address{hash: hash}) do
    +    to_string(hash)
    +  end
    +
    +  @doc """
    +  Returns the primary name of an address if available. If there is no names on address function performs preload of names association.
    +  """
    +  def primary_name(_, second_time? \\ false)
    +
    +  def primary_name(%Address{names: [_ | _] = address_names}, _second_time?) do
    +    case Enum.find(address_names, &(&1.primary == true)) do
    +      nil ->
    +        %Address.Name{name: name} = Enum.at(address_names, 0)
    +        name
    +
    +      %Address.Name{name: name} ->
    +        name
    +    end
    +  end
    +
    +  def primary_name(%Address{names: _} = address, false) do
    +    primary_name(Repo.preload(address, [:names]), true)
    +  end
    +
    +  def primary_name(%Address{names: _}, true), do: nil
    +
    +  def implementation_name(%Address{smart_contract: %{implementation_name: implementation_name}}),
    +    do: implementation_name
    +
    +  def implementation_name(_), do: nil
    +
    +  def primary_validator_metadata(%Address{names: [_ | _] = address_names}) do
    +    case Enum.find(address_names, &(&1.primary == true)) do
    +      %Address.Name{
    +        metadata:
    +          metadata = %{
    +            "license_id" => _,
    +            "address" => _,
    +            "state" => _,
    +            "zipcode" => _,
    +            "expiration_date" => _,
    +            "created_date" => _
    +          }
    +      } ->
    +        metadata
    +
    +      _ ->
    +        nil
    +    end
    +  end
    +
    +  def primary_validator_metadata(%Address{names: _}), do: nil
    +
    +  def format_datetime_string(unix_date) do
    +    unix_date
    +    |> DateTime.from_unix!()
    +    |> Timex.format!("{M}-{D}-{YYYY}")
    +  end
    +
    +  def qr_code(address_hash) do
    +    address_hash
    +    |> to_string()
    +    |> QRCode.to_png()
    +    |> Base.encode64()
    +  end
    +
    +  def smart_contract_verified?(%Address{smart_contract: %{metadata_from_verified_twin: true}}), do: false
    +
    +  def smart_contract_verified?(%Address{smart_contract: %SmartContract{}}), do: true
    +
    +  def smart_contract_verified?(%Address{smart_contract: nil}), do: false
    +
    +  def smart_contract_with_read_only_functions?(%Address{smart_contract: %SmartContract{}} = address) do
    +    Enum.any?(address.smart_contract.abi, &is_read_function?(&1))
    +  end
    +
    +  def smart_contract_with_read_only_functions?(%Address{smart_contract: nil}), do: false
    +
    +  def is_read_function?(function), do: Helper.queriable_method?(function) || Helper.read_with_wallet_method?(function)
    +
    +  def smart_contract_is_proxy?(%Address{smart_contract: %SmartContract{}} = address) do
    +    Chain.proxy_contract?(address.hash, address.smart_contract.abi)
    +  end
    +
    +  def smart_contract_is_proxy?(%Address{smart_contract: nil}), do: false
    +
    +  def smart_contract_with_write_functions?(%Address{smart_contract: %SmartContract{}} = address) do
    +    Enum.any?(
    +      address.smart_contract.abi,
    +      &Writer.write_function?(&1)
    +    )
    +  end
    +
    +  def smart_contract_with_write_functions?(%Address{smart_contract: nil}), do: false
    +
    +  def has_decompiled_code?(address) do
    +    address.has_decompiled_code? ||
    +      (Ecto.assoc_loaded?(address.decompiled_smart_contracts) && Enum.count(address.decompiled_smart_contracts) > 0)
    +  end
    +
    +  def token_title(%Token{name: nil, contract_address_hash: contract_address_hash}) do
    +    short_hash_left_right(contract_address_hash)
    +  end
    +
    +  def token_title(%Token{name: name, symbol: symbol}), do: "#{name} (#{symbol})"
    +
    +  def trimmed_hash(%Hash{} = hash) do
    +    string_hash = to_string(hash)
    +    trimmed_hash(string_hash)
    +  end
    +
    +  def trimmed_hash(address) when is_binary(address) do
    +    "#{String.slice(address, 0..7)}–#{String.slice(address, -6..-1)}"
    +  end
    +
    +  def trimmed_hash(_), do: ""
    +
    +  def trimmed_verify_link(hash) do
    +    string_hash = to_string(hash)
    +    "#{String.slice(string_hash, 0..21)}..."
    +  end
    +
    +  def transaction_hash(%Address{contracts_creation_internal_transaction: %InternalTransaction{}} = address) do
    +    address.contracts_creation_internal_transaction.transaction_hash
    +  end
    +
    +  def transaction_hash(%Address{contracts_creation_transaction: %Transaction{}} = address) do
    +    address.contracts_creation_transaction.hash
    +  end
    +
    +  def from_address_hash(%Address{contracts_creation_internal_transaction: %InternalTransaction{}} = address) do
    +    address.contracts_creation_internal_transaction.from_address_hash
    +  end
    +
    +  def from_address_hash(%Address{contracts_creation_transaction: %Transaction{}} = address) do
    +    address.contracts_creation_transaction.from_address_hash
    +  end
    +
    +  def from_address_hash(_address), do: nil
    +
    +  def address_link_to_other_explorer(link, address, full) do
    +    if full do
    +      link <> to_string(address)
    +    else
    +      trimmed_verify_link(link <> to_string(address))
    +    end
    +  end
    +
    +  defp matching_address_check(%Address{hash: hash} = current_address, %Address{hash: hash}, contract?, truncate) do
    +    [
    +      view_module: __MODULE__,
    +      partial: "_responsive_hash.html",
    +      address: current_address,
    +      contract: contract?,
    +      truncate: truncate,
    +      use_custom_tooltip: false
    +    ]
    +  end
    +
    +  defp matching_address_check(_current_address, %Address{} = address, contract?, truncate) do
    +    [
    +      view_module: __MODULE__,
    +      partial: "_link.html",
    +      address: address,
    +      contract: contract?,
    +      truncate: truncate,
    +      use_custom_tooltip: false
    +    ]
    +  end
    +
    +  @doc """
    +  Get the current tab name/title from the request path and possible tab names.
    +
    +  The tabs on mobile are represented by a dropdown list, which has a title. This title is the
    +  currently selected tab name. This function returns that name, properly gettext'ed.
    +
    +  The list of possible tab names for this page is represented by the attribute @tab.
    +
    +  Raises error if there is no match, so a developer of a new tab must include it in the list.
    +  """
    +  def current_tab_name(request_path) do
    +    @tabs
    +    |> Enum.filter(&tab_active?(&1, request_path))
    +    |> tab_name()
    +  end
    +
    +  defp tab_name(["tokens"]), do: gettext("Tokens")
    +  defp tab_name(["internal-transactions"]), do: gettext("Internal Transactions")
    +  defp tab_name(["transactions"]), do: gettext("Transactions")
    +  defp tab_name(["token-transfers"]), do: gettext("Token Transfers")
    +  defp tab_name(["contracts"]), do: gettext("Code")
    +  defp tab_name(["decompiled-contracts"]), do: gettext("Decompiled Code")
    +  defp tab_name(["read-contract"]), do: gettext("Read Contract")
    +  defp tab_name(["read-proxy"]), do: gettext("Read Proxy")
    +  defp tab_name(["write-contract"]), do: gettext("Write Contract")
    +  defp tab_name(["write-proxy"]), do: gettext("Write Proxy")
    +  defp tab_name(["coin-balances"]), do: gettext("Coin Balance History")
    +  defp tab_name(["validations"]), do: gettext("Blocks Validated")
    +  defp tab_name(["logs"]), do: gettext("Logs")
    +
    +  def short_hash(%Address{hash: hash}) do
    +    <<
    +      "0x",
    +      short_address::binary-size(6),
    +      _rest::binary
    +    >> = to_string(hash)
    +
    +    "0x" <> short_address
    +  end
    +
    +  def short_hash_left_right(hash) when not is_nil(hash) do
    +    case hash do
    +      "0x" <> rest ->
    +        shortify_hash_string(rest)
    +
    +      %Chain.Hash{
    +        byte_count: _,
    +        bytes: bytes
    +      } ->
    +        shortify_hash_string(Base.encode16(bytes, case: :lower))
    +
    +      hash ->
    +        shortify_hash_string(hash)
    +    end
    +  end
    +
    +  def short_hash_left_right(hash) when is_nil(hash), do: ""
    +
    +  defp shortify_hash_string(hash) do
    +    <<
    +      left::binary-size(6),
    +      _middle::binary-size(28),
    +      right::binary-size(6)
    +    >> = to_string(hash)
    +
    +    "0x" <> left <> "-" <> right
    +  end
    +
    +  def short_contract_name(name, max_length) do
    +    short_string(name, max_length)
    +  end
    +
    +  def short_token_id(%Decimal{} = token_id, max_length) do
    +    token_id
    +    |> Decimal.to_string()
    +    |> short_string(max_length)
    +  end
    +
    +  def short_token_id(token_id, max_length) do
    +    short_string(token_id, max_length)
    +  end
    +
    +  def short_string(nil, _max_length), do: ""
    +
    +  def short_string(name, max_length) do
    +    part_length = Kernel.trunc(max_length / 4)
    +
    +    if String.length(name) <= max_length,
    +      do: name,
    +      else: "#{String.slice(name, 0, max_length - part_length)}..#{String.slice(name, -part_length, part_length)}"
    +  end
    +
    +  def address_page_title(address) do
    +    cond do
    +      smart_contract_verified?(address) -> "#{address.smart_contract.name} (#{to_string(address)})"
    +      contract?(address) -> "Contract #{to_string(address)}"
    +      true -> "#{to_string(address)}"
    +    end
    +  end
    +
    +  def smart_contract_is_gnosis_safe_proxy?(%Address{smart_contract: %SmartContract{}} = address) do
    +    address.smart_contract.name == "GnosisSafeProxy" && Chain.gnosis_safe_contract?(address.smart_contract.abi)
    +  end
    +
    +  def smart_contract_is_gnosis_safe_proxy?(_address), do: false
    +
    +  def tag_name_to_label(tag_name) do
    +    tag_name
    +    |> String.replace(" ", "-")
    +  end
    +
    +  def fetch_custom_abi(conn, address_hash) do
    +    if current_user = current_user(conn) do
    +      CustomABI.get_custom_abi_by_identity_id_and_address_hash(address_hash, current_user.id)
    +    end
    +  end
    +
    +  def has_address_custom_abi_with_read_functions?(conn, address_hash) do
    +    custom_abi = fetch_custom_abi(conn, address_hash)
    +
    +    check_custom_abi_for_having_read_functions(custom_abi)
    +  end
    +
    +  def check_custom_abi_for_having_read_functions(custom_abi),
    +    do: !is_nil(custom_abi) && Enum.any?(custom_abi.abi, &is_read_function?(&1))
    +
    +  def has_address_custom_abi_with_write_functions?(conn, address_hash) do
    +    custom_abi = fetch_custom_abi(conn, address_hash)
    +
    +    check_custom_abi_for_having_write_functions(custom_abi)
    +  end
    +
    +  def check_custom_abi_for_having_write_functions(custom_abi),
    +    do: !is_nil(custom_abi) && Enum.any?(custom_abi.abi, &Writer.write_function?(&1))
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex
    new file mode 100644
    index 0000000..c21e1f5
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_write_contract_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.AddressWriteContractView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex
    new file mode 100644
    index 0000000..17634e9
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/address_write_proxy_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.AddressWriteProxyView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/admin/dashboard_view.ex b/apps/block_scout_web/lib/block_scout_web/views/admin/dashboard_view.ex
    new file mode 100644
    index 0000000..536ef37
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/admin/dashboard_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.Admin.DashboardView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/admin/session_view.ex b/apps/block_scout_web/lib/block_scout_web/views/admin/session_view.ex
    new file mode 100644
    index 0000000..6e99ff6
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/admin/session_view.ex
    @@ -0,0 +1,7 @@
    +defmodule BlockScoutWeb.Admin.SessionView do
    +  use BlockScoutWeb, :view
    +
    +  import BlockScoutWeb.AdminRouter.Helpers
    +
    +  alias BlockScoutWeb.FormView
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/admin/setup_view.ex b/apps/block_scout_web/lib/block_scout_web/views/admin/setup_view.ex
    new file mode 100644
    index 0000000..75f8a36
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/admin/setup_view.ex
    @@ -0,0 +1,7 @@
    +defmodule BlockScoutWeb.Admin.SetupView do
    +  use BlockScoutWeb, :view
    +
    +  import BlockScoutWeb.AdminRouter.Helpers
    +
    +  alias BlockScoutWeb.FormView
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/advertisement/banners_ad_view.ex b/apps/block_scout_web/lib/block_scout_web/views/advertisement/banners_ad_view.ex
    new file mode 100644
    index 0000000..47f461f
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/advertisement/banners_ad_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.Advertisement.BannersAdView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/advertisement/text_ad_view.ex b/apps/block_scout_web/lib/block_scout_web/views/advertisement/text_ad_view.ex
    new file mode 100644
    index 0000000..8d73eb3
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/advertisement/text_ad_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.Advertisement.TextAdView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/eth_rpc/view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/eth_rpc/view.ex
    new file mode 100644
    index 0000000..16f85b3
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/eth_rpc/view.ex
    @@ -0,0 +1,84 @@
    +defmodule BlockScoutWeb.API.EthRPC.View do
    +  @moduledoc """
    +  Views for /eth-rpc API endpoints
    +  """
    +  use BlockScoutWeb, :view
    +
    +  defstruct [:result, :id, :error]
    +
    +  def render("show.json", %{result: result, id: id}) do
    +    %__MODULE__{
    +      result: result,
    +      id: id
    +    }
    +  end
    +
    +  def render("error.json", %{error: message, id: id}) do
    +    %__MODULE__{
    +      error: message,
    +      id: id
    +    }
    +  end
    +
    +  def render("response.json", %{response: %{error: error, id: id}}) do
    +    %__MODULE__{
    +      error: error,
    +      id: id
    +    }
    +  end
    +
    +  def render("response.json", %{response: %{result: result, id: id}}) do
    +    %__MODULE__{
    +      result: result,
    +      id: id
    +    }
    +  end
    +
    +  def render("responses.json", %{responses: responses}) do
    +    Enum.map(responses, fn
    +      %{error: error, id: id} ->
    +        %__MODULE__{
    +          error: error,
    +          id: id
    +        }
    +
    +      %{result: result, id: id} ->
    +        %__MODULE__{
    +          result: result,
    +          id: id
    +        }
    +    end)
    +  end
    +
    +  defimpl Poison.Encoder, for: BlockScoutWeb.API.EthRPC.View do
    +    def encode(%BlockScoutWeb.API.EthRPC.View{result: result, id: id, error: error}, _options) when is_nil(error) do
    +      result = Poison.encode!(result)
    +
    +      """
    +      {"jsonrpc":"2.0","result":#{result},"id":#{id}}
    +      """
    +    end
    +
    +    def encode(%BlockScoutWeb.API.EthRPC.View{id: id, error: error}, _options) do
    +      """
    +      {"jsonrpc":"2.0","error": "#{error}","id": #{id}}
    +      """
    +    end
    +  end
    +
    +  defimpl Jason.Encoder, for: BlockScoutWeb.API.EthRPC.View do
    +    def encode(%BlockScoutWeb.API.EthRPC.View{result: result, id: id, error: error}, _options) when is_nil(error) do
    +      result = Jason.encode!(result)
    +
    +      """
    +      {"jsonrpc":"2.0","result":#{result},"id":#{id}}
    +      """
    +    end
    +
    +    def encode(%BlockScoutWeb.API.EthRPC.View{id: id, error: error}, _options) do
    +      """
    +      {"jsonrpc":"2.0","error": "#{error}","id": #{id}}
    +      """
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex
    new file mode 100644
    index 0000000..1d407c4
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex
    @@ -0,0 +1,215 @@
    +defmodule BlockScoutWeb.API.RPC.AddressView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.API.EthRPC.View, as: EthRPCView
    +  alias BlockScoutWeb.API.RPC.RPCView
    +
    +  def render("listaccounts.json", %{accounts: accounts}) do
    +    accounts = Enum.map(accounts, &prepare_account/1)
    +    RPCView.render("show.json", data: accounts)
    +  end
    +
    +  def render("balance.json", %{addresses: [address]}) do
    +    RPCView.render("show.json", data: balance(address))
    +  end
    +
    +  def render("balance.json", assigns) do
    +    render("balancemulti.json", assigns)
    +  end
    +
    +  def render("balancemulti.json", %{addresses: addresses}) do
    +    data = Enum.map(addresses, &render_address/1)
    +
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("pendingtxlist.json", %{transactions: transactions}) do
    +    data = Enum.map(transactions, &prepare_pending_transaction/1)
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("txlist.json", %{transactions: transactions}) do
    +    data = Enum.map(transactions, &prepare_transaction/1)
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("txlistinternal.json", %{internal_transactions: internal_transactions}) do
    +    data = Enum.map(internal_transactions, &prepare_internal_transaction/1)
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("tokentx.json", %{token_transfers: token_transfers}) do
    +    data = Enum.map(token_transfers, &prepare_token_transfer/1)
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("tokenbalance.json", %{token_balance: token_balance}) do
    +    RPCView.render("show.json", data: to_string(token_balance))
    +  end
    +
    +  def render("token_list.json", %{token_list: token_list}) do
    +    data = Enum.map(token_list, &prepare_token/1)
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("getminedblocks.json", %{blocks: blocks}) do
    +    data = Enum.map(blocks, &prepare_block/1)
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("eth_get_balance.json", %{balance: balance}) do
    +    EthRPCView.render("show.json", %{result: balance, id: 0})
    +  end
    +
    +  def render("eth_get_balance_error.json", %{error: message}) do
    +    EthRPCView.render("error.json", %{error: message, id: 0})
    +  end
    +
    +  def render("error.json", assigns) do
    +    RPCView.render("error.json", assigns)
    +  end
    +
    +  defp render_address(address) do
    +    %{
    +      "account" => "#{address.hash}",
    +      "balance" => balance(address),
    +      "stale" => address.stale? || false
    +    }
    +  end
    +
    +  defp prepare_account(address) do
    +    %{
    +      "balance" => to_string(address.fetched_coin_balance && address.fetched_coin_balance.value),
    +      "address" => to_string(address.hash),
    +      "stale" => address.stale? || false
    +    }
    +  end
    +
    +  defp prepare_pending_transaction(transaction) do
    +    %{
    +      "hash" => "#{transaction.hash}",
    +      "nonce" => "#{transaction.nonce}",
    +      "from" => "#{transaction.from_address_hash}",
    +      "to" => "#{transaction.to_address_hash}",
    +      "value" => "#{transaction.value.value}",
    +      "gas" => "#{transaction.gas}",
    +      "gasPrice" => "#{transaction.gas_price.value}",
    +      "input" => "#{transaction.input}",
    +      "contractAddress" => "#{transaction.created_contract_address_hash}",
    +      "cumulativeGasUsed" => "#{transaction.cumulative_gas_used}",
    +      "gasUsed" => "#{transaction.gas_used}"
    +    }
    +  end
    +
    +  defp prepare_transaction(transaction) do
    +    %{
    +      "blockNumber" => "#{transaction.block_number}",
    +      "timeStamp" => "#{DateTime.to_unix(transaction.block_timestamp)}",
    +      "hash" => "#{transaction.hash}",
    +      "nonce" => "#{transaction.nonce}",
    +      "blockHash" => "#{transaction.block_hash}",
    +      "transactionIndex" => "#{transaction.index}",
    +      "from" => "#{transaction.from_address_hash}",
    +      "to" => "#{transaction.to_address_hash}",
    +      "value" => "#{transaction.value.value}",
    +      "gas" => "#{transaction.gas}",
    +      "gasPrice" => "#{transaction.gas_price.value}",
    +      "isError" => if(transaction.status == :ok, do: "0", else: "1"),
    +      "txreceipt_status" => if(transaction.status == :ok, do: "1", else: "0"),
    +      "input" => "#{transaction.input}",
    +      "contractAddress" => "#{transaction.created_contract_address_hash}",
    +      "cumulativeGasUsed" => "#{transaction.cumulative_gas_used}",
    +      "gasUsed" => "#{transaction.gas_used}",
    +      "confirmations" => "#{transaction.confirmations}"
    +    }
    +  end
    +
    +  defp prepare_internal_transaction(internal_transaction) do
    +    %{
    +      "blockNumber" => "#{internal_transaction.block_number}",
    +      "timeStamp" => "#{DateTime.to_unix(internal_transaction.block_timestamp)}",
    +      "from" => "#{internal_transaction.from_address_hash}",
    +      "to" => "#{internal_transaction.to_address_hash}",
    +      "value" => "#{internal_transaction.value.value}",
    +      "contractAddress" => "#{internal_transaction.created_contract_address_hash}",
    +      "transactionHash" => to_string(internal_transaction.transaction_hash),
    +      "index" => to_string(internal_transaction.index),
    +      "input" => "#{internal_transaction.input}",
    +      "type" => "#{internal_transaction.type}",
    +      "callType" => "#{internal_transaction.call_type}",
    +      "gas" => "#{internal_transaction.gas}",
    +      "gasUsed" => "#{internal_transaction.gas_used}",
    +      "isError" => if(internal_transaction.error, do: "1", else: "0"),
    +      "errCode" => "#{internal_transaction.error}"
    +    }
    +  end
    +
    +  defp prepare_common_token_transfer(token_transfer) do
    +    %{
    +      "blockNumber" => to_string(token_transfer.block_number),
    +      "timeStamp" => to_string(DateTime.to_unix(token_transfer.block_timestamp)),
    +      "hash" => to_string(token_transfer.transaction_hash),
    +      "nonce" => to_string(token_transfer.transaction_nonce),
    +      "blockHash" => to_string(token_transfer.block_hash),
    +      "from" => to_string(token_transfer.from_address_hash),
    +      "contractAddress" => to_string(token_transfer.token_contract_address_hash),
    +      "to" => to_string(token_transfer.to_address_hash),
    +      "logIndex" => to_string(token_transfer.token_log_index),
    +      "tokenName" => token_transfer.token_name,
    +      "tokenSymbol" => token_transfer.token_symbol,
    +      "tokenDecimal" => to_string(token_transfer.token_decimals),
    +      "transactionIndex" => to_string(token_transfer.transaction_index),
    +      "gas" => to_string(token_transfer.transaction_gas),
    +      "gasPrice" => to_string(token_transfer.transaction_gas_price.value),
    +      "gasUsed" => to_string(token_transfer.transaction_gas_used),
    +      "cumulativeGasUsed" => to_string(token_transfer.transaction_cumulative_gas_used),
    +      "input" => to_string(token_transfer.transaction_input),
    +      "confirmations" => to_string(token_transfer.confirmations)
    +    }
    +  end
    +
    +  defp prepare_token_transfer(%{token_type: "ERC-721"} = token_transfer) do
    +    token_transfer
    +    |> prepare_common_token_transfer()
    +    |> Map.put_new(:tokenID, token_transfer.token_id)
    +  end
    +
    +  defp prepare_token_transfer(%{token_type: "ERC-1155"} = token_transfer) do
    +    token_transfer
    +    |> prepare_common_token_transfer()
    +    |> Map.put_new(:tokenID, token_transfer.token_id)
    +  end
    +
    +  defp prepare_token_transfer(%{token_type: "ERC-20"} = token_transfer) do
    +    token_transfer
    +    |> prepare_common_token_transfer()
    +    |> Map.put_new(:value, to_string(token_transfer.amount))
    +  end
    +
    +  defp prepare_token_transfer(token_transfer) do
    +    prepare_common_token_transfer(token_transfer)
    +  end
    +
    +  defp prepare_block(block) do
    +    %{
    +      "blockNumber" => to_string(block.number),
    +      "timeStamp" => to_string(block.timestamp)
    +    }
    +  end
    +
    +  defp prepare_token(token) do
    +    %{
    +      "balance" => to_string(token.balance),
    +      "contractAddress" => to_string(token.contract_address_hash),
    +      "name" => token.name,
    +      "decimals" => to_string(token.decimals),
    +      "symbol" => token.symbol,
    +      "type" => token.type
    +    }
    +    |> (&if(is_nil(token.id), do: &1, else: Map.put(&1, "id", token.id))).()
    +  end
    +
    +  defp balance(address) do
    +    address.fetched_coin_balance && address.fetched_coin_balance.value && "#{address.fetched_coin_balance.value}"
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex
    new file mode 100644
    index 0000000..70d5f0b
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex
    @@ -0,0 +1,44 @@
    +defmodule BlockScoutWeb.API.RPC.BlockView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.API.EthRPC.View, as: EthRPCView
    +  alias BlockScoutWeb.API.RPC.RPCView
    +  alias Explorer.Chain.{Hash, Wei}
    +  alias Explorer.EthRPC, as: EthRPC
    +
    +  def render("block_reward.json", %{block: block, reward: reward}) do
    +    reward_as_string =
    +      reward
    +      |> Wei.to(:wei)
    +      |> Decimal.to_string(:normal)
    +
    +    data = %{
    +      "blockNumber" => to_string(block.number),
    +      "timeStamp" => DateTime.to_unix(block.timestamp),
    +      "blockMiner" => Hash.to_string(block.miner_hash),
    +      "blockReward" => reward_as_string,
    +      "uncles" => nil,
    +      "uncleInclusionReward" => nil
    +    }
    +
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("getblocknobytime.json", %{block_number: block_number}) do
    +    data = %{
    +      "blockNumber" => to_string(block_number)
    +    }
    +
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("eth_block_number.json", %{number: number, id: id}) do
    +    result = EthRPC.encode_quantity(number)
    +
    +    EthRPCView.render("show.json", %{result: result, id: id})
    +  end
    +
    +  def render("error.json", %{error: error}) do
    +    RPCView.render("error.json", error: error)
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
    new file mode 100644
    index 0000000..ace6af7
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
    @@ -0,0 +1,221 @@
    +defmodule BlockScoutWeb.API.RPC.ContractView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.AddressView
    +  alias BlockScoutWeb.API.RPC.RPCView
    +  alias Ecto.Association.NotLoaded
    +  alias Explorer.Chain
    +  alias Explorer.Chain.{Address, DecompiledSmartContract, SmartContract}
    +
    +  defguardp is_empty_string(input) when input == "" or input == nil
    +
    +  def render("listcontracts.json", %{contracts: contracts}) do
    +    contracts = Enum.map(contracts, &prepare_contract/1)
    +
    +    RPCView.render("show.json", data: contracts)
    +  end
    +
    +  def render("getabi.json", %{abi: abi}) do
    +    RPCView.render("show.json", data: Jason.encode!(abi))
    +  end
    +
    +  def render("getsourcecode.json", %{contract: contract}) do
    +    RPCView.render("show.json", data: [prepare_source_code_contract(contract)])
    +  end
    +
    +  def render("error.json", assigns) do
    +    RPCView.render("error.json", assigns)
    +  end
    +
    +  def render("verify.json", %{contract: contract}) do
    +    RPCView.render("show.json", data: prepare_source_code_contract(contract))
    +  end
    +
    +  def render("show.json", %{result: result}) do
    +    RPCView.render("show.json", data: result)
    +  end
    +
    +  defp prepare_source_code_contract(address) do
    +    decompiled_smart_contract = latest_decompiled_smart_contract(address.decompiled_smart_contracts)
    +    contract = address.smart_contract || %{}
    +
    +    optimization = Map.get(contract, :optimization, "")
    +
    +    contract_output = %{
    +      "Address" => to_string(address.hash)
    +    }
    +
    +    contract_output
    +    |> set_decompiled_contract_data(decompiled_smart_contract)
    +    |> set_optimization_runs(contract, optimization)
    +    |> set_constructor_arguments(contract)
    +    |> set_external_libraries(contract)
    +    |> set_verified_contract_data(contract, address, optimization)
    +    |> set_proxy_info(contract)
    +  end
    +
    +  defp set_proxy_info(contract_output, contract) when contract == %{} do
    +    contract_output
    +  end
    +
    +  defp set_proxy_info(contract_output, contract) do
    +    result =
    +      if contract.is_proxy do
    +        contract_output
    +        |> Map.put_new(:ImplementationAddress, contract.implementation_address_hash_string)
    +      else
    +        contract_output
    +      end
    +
    +    is_proxy_string = if contract.is_proxy, do: "true", else: "false"
    +
    +    result
    +    |> Map.put_new(:IsProxy, is_proxy_string)
    +  end
    +
    +  defp set_decompiled_contract_data(contract_output, decompiled_smart_contract) do
    +    if decompiled_smart_contract do
    +      contract_output
    +      |> Map.put_new(:DecompiledSourceCode, decompiled_source_code(decompiled_smart_contract))
    +      |> Map.put_new(:DecompilerVersion, decompiler_version(decompiled_smart_contract))
    +    else
    +      contract_output
    +    end
    +  end
    +
    +  defp set_optimization_runs(contract_output, contract, optimization) do
    +    optimization_runs = Map.get(contract, :optimization_runs, "")
    +
    +    if optimization && optimization != "" do
    +      contract_output
    +      |> Map.put_new(:OptimizationRuns, optimization_runs)
    +    else
    +      contract_output
    +    end
    +  end
    +
    +  defp set_constructor_arguments(contract_output, %{constructor_arguments: arguments}) when is_empty_string(arguments),
    +    do: contract_output
    +
    +  defp set_constructor_arguments(contract_output, %{constructor_arguments: arguments}) do
    +    contract_output
    +    |> Map.put_new(:ConstructorArguments, arguments)
    +  end
    +
    +  defp set_constructor_arguments(contract_output, _), do: contract_output
    +
    +  defp set_external_libraries(contract_output, contract) do
    +    external_libraries = Map.get(contract, :external_libraries, [])
    +
    +    if Enum.count(external_libraries) > 0 do
    +      external_libraries_without_id =
    +        Enum.map(external_libraries, fn %{name: name, address_hash: address_hash} ->
    +          %{"name" => name, "address_hash" => address_hash}
    +        end)
    +
    +      contract_output
    +      |> Map.put_new(:ExternalLibraries, external_libraries_without_id)
    +    else
    +      contract_output
    +    end
    +  end
    +
    +  defp set_verified_contract_data(contract_output, contract, address, optimization) do
    +    contract_abi =
    +      if is_nil(address.smart_contract) do
    +        "Contract source code not verified"
    +      else
    +        Jason.encode!(contract.abi)
    +      end
    +
    +    contract_optimization =
    +      case optimization do
    +        true ->
    +          "true"
    +
    +        false ->
    +          "false"
    +
    +        "" ->
    +          ""
    +      end
    +
    +    if Map.equal?(contract, %{}) do
    +      contract_output
    +    else
    +      contract_output
    +      |> Map.put_new(:SourceCode, Map.get(contract, :contract_source_code, ""))
    +      |> Map.put_new(:ABI, contract_abi)
    +      |> Map.put_new(:ContractName, Map.get(contract, :name, ""))
    +      |> Map.put_new(:CompilerVersion, Map.get(contract, :compiler_version, ""))
    +      |> Map.put_new(:OptimizationUsed, contract_optimization)
    +      |> Map.put_new(:EVMVersion, Map.get(contract, :evm_version, ""))
    +      |> Map.put_new(:FileName, Map.get(contract, :file_path, "") || "")
    +      |> insert_additional_sources(address)
    +    end
    +  end
    +
    +  defp insert_additional_sources(output, address) do
    +    additional_sources_from_twin = Chain.get_address_verified_twin_contract(address.hash).additional_sources
    +
    +    additional_sources =
    +      if AddressView.smart_contract_verified?(address),
    +        do: address.smart_contract_additional_sources,
    +        else: additional_sources_from_twin
    +
    +    additional_sources_array =
    +      if additional_sources,
    +        do:
    +          Enum.map(additional_sources, fn src ->
    +            %{
    +              Filename: src.file_name,
    +              SourceCode: src.contract_source_code
    +            }
    +          end),
    +        else: []
    +
    +    if additional_sources_array == [],
    +      do: output,
    +      else: Map.put_new(output, :AdditionalSources, additional_sources_array)
    +  end
    +
    +  defp prepare_contract(%Address{
    +         hash: hash,
    +         smart_contract: nil
    +       }) do
    +    %{
    +      "Address" => to_string(hash),
    +      "ABI" => "Contract source code not verified"
    +    }
    +  end
    +
    +  defp prepare_contract(%Address{
    +         hash: hash,
    +         smart_contract: %SmartContract{} = contract
    +       }) do
    +    %{
    +      "Address" => to_string(hash),
    +      "ABI" => Jason.encode!(contract.abi),
    +      "ContractName" => contract.name,
    +      "CompilerVersion" => contract.compiler_version,
    +      "OptimizationUsed" => if(contract.optimization, do: "1", else: "0")
    +    }
    +  end
    +
    +  defp latest_decompiled_smart_contract(%NotLoaded{}), do: nil
    +
    +  defp latest_decompiled_smart_contract([]), do: nil
    +
    +  defp latest_decompiled_smart_contract(contracts) do
    +    Enum.max_by(contracts, fn contract -> DateTime.to_unix(contract.inserted_at) end)
    +  end
    +
    +  defp decompiled_source_code(nil), do: "Contract source code not decompiled."
    +
    +  defp decompiled_source_code(%DecompiledSmartContract{decompiled_source_code: decompiled_source_code}) do
    +    decompiled_source_code
    +  end
    +
    +  defp decompiler_version(nil), do: ""
    +  defp decompiler_version(%DecompiledSmartContract{decompiler_version: decompiler_version}), do: decompiler_version
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/logs_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/logs_view.ex
    new file mode 100644
    index 0000000..6f2933d
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/logs_view.ex
    @@ -0,0 +1,52 @@
    +defmodule BlockScoutWeb.API.RPC.LogsView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.API.RPC.RPCView
    +
    +  def render("getlogs.json", %{logs: logs}) do
    +    data = Enum.map(logs, &prepare_log/1)
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("error.json", assigns) do
    +    RPCView.render("error.json", assigns)
    +  end
    +
    +  defp prepare_log(log) do
    +    %{
    +      "address" => "#{log.address_hash}",
    +      "topics" => get_topics(log),
    +      "data" => "#{log.data}",
    +      "blockNumber" => integer_to_hex(log.block_number),
    +      "timeStamp" => datetime_to_hex(log.block_timestamp),
    +      "gasPrice" => decimal_to_hex(log.gas_price.value),
    +      "gasUsed" => decimal_to_hex(log.gas_used),
    +      "logIndex" => integer_to_hex(log.index),
    +      "transactionHash" => "#{log.transaction_hash}",
    +      "transactionIndex" => integer_to_hex(log.transaction_index)
    +    }
    +  end
    +
    +  defp get_topics(%{
    +         first_topic: first_topic,
    +         second_topic: second_topic,
    +         third_topic: third_topic,
    +         fourth_topic: fourth_topic
    +       }) do
    +    [first_topic, second_topic, third_topic, fourth_topic]
    +  end
    +
    +  defp integer_to_hex(integer), do: "0x" <> String.downcase(Integer.to_string(integer, 16))
    +
    +  defp decimal_to_hex(decimal) do
    +    decimal
    +    |> Decimal.to_integer()
    +    |> integer_to_hex()
    +  end
    +
    +  defp datetime_to_hex(datetime) do
    +    datetime
    +    |> DateTime.to_unix()
    +    |> integer_to_hex()
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/rpc_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/rpc_view.ex
    new file mode 100644
    index 0000000..f877d94
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/rpc_view.ex
    @@ -0,0 +1,27 @@
    +defmodule BlockScoutWeb.API.RPC.RPCView do
    +  use BlockScoutWeb, :view
    +
    +  def render("show.json", %{data: data}) do
    +    %{
    +      "status" => "1",
    +      "message" => "OK",
    +      "result" => data
    +    }
    +  end
    +
    +  def render("show_value.json", %{data: data}) do
    +    {value, _} =
    +      data
    +      |> Float.parse()
    +
    +    value
    +  end
    +
    +  def render("error.json", %{error: message} = assigns) do
    +    %{
    +      "status" => "0",
    +      "message" => message,
    +      "result" => Map.get(assigns, :data)
    +    }
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/stats_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/stats_view.ex
    new file mode 100644
    index 0000000..91f6967
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/stats_view.ex
    @@ -0,0 +1,53 @@
    +defmodule BlockScoutWeb.API.RPC.StatsView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.API.RPC.RPCView
    +
    +  def render("tokensupply.json", %{total_supply: token_supply}) do
    +    RPCView.render("show.json", data: token_supply)
    +  end
    +
    +  def render("ethsupplyexchange.json", %{total_supply: total_supply}) do
    +    RPCView.render("show.json", data: total_supply)
    +  end
    +
    +  def render("ethsupply.json", %{total_supply: total_supply}) do
    +    RPCView.render("show.json", data: total_supply)
    +  end
    +
    +  def render("coinsupply.json", %{total_supply: total_supply}) do
    +    RPCView.render("show_value.json", data: total_supply)
    +  end
    +
    +  def render("coinprice.json", %{rates: rates}) do
    +    RPCView.render("show.json", data: prepare_rates(rates))
    +  end
    +
    +  def render("totalfees.json", %{total_fees: total_fees}) do
    +    RPCView.render("show.json", data: total_fees)
    +  end
    +
    +  def render("error.json", assigns) do
    +    RPCView.render("error.json", assigns)
    +  end
    +
    +  defp prepare_rates(rates) do
    +    if rates do
    +      timestamp = rates.last_updated |> DateTime.to_unix() |> to_string()
    +
    +      %{
    +        "coin_btc" => to_string(rates.btc_value),
    +        "coin_btc_timestamp" => timestamp,
    +        "coin_usd" => to_string(rates.usd_value),
    +        "coin_usd_timestamp" => timestamp
    +      }
    +    else
    +      %{
    +        "coin_btc" => nil,
    +        "coin_btc_timestamp" => nil,
    +        "coin_usd" => nil,
    +        "coin_usd_timestamp" => nil
    +      }
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/token_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/token_view.ex
    new file mode 100644
    index 0000000..9ccab7c
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/token_view.ex
    @@ -0,0 +1,37 @@
    +defmodule BlockScoutWeb.API.RPC.TokenView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.API.RPC.RPCView
    +
    +  def render("gettoken.json", %{token: token}) do
    +    RPCView.render("show.json", data: prepare_token(token))
    +  end
    +
    +  def render("gettokenholders.json", %{token_holders: token_holders}) do
    +    data = Enum.map(token_holders, &prepare_token_holder/1)
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("error.json", assigns) do
    +    RPCView.render("error.json", assigns)
    +  end
    +
    +  defp prepare_token(token) do
    +    %{
    +      "type" => token.type,
    +      "name" => token.name,
    +      "symbol" => token.symbol,
    +      "totalSupply" => to_string(token.total_supply),
    +      "decimals" => to_string(token.decimals),
    +      "contractAddress" => to_string(token.contract_address_hash),
    +      "cataloged" => token.cataloged
    +    }
    +  end
    +
    +  defp prepare_token_holder(token_holder) do
    +    %{
    +      "address" => to_string(token_holder.address_hash),
    +      "value" => token_holder.value
    +    }
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex
    new file mode 100644
    index 0000000..4b81b11
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex
    @@ -0,0 +1,90 @@
    +defmodule BlockScoutWeb.API.RPC.TransactionView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.API.RPC.RPCView
    +
    +  def render("gettxinfo.json", %{
    +        transaction: transaction,
    +        block_height: block_height,
    +        logs: logs,
    +        next_page_params: next_page_params
    +      }) do
    +    data = prepare_transaction(transaction, block_height, logs, next_page_params)
    +    RPCView.render("show.json", data: data)
    +  end
    +
    +  def render("gettxreceiptstatus.json", %{status: status}) do
    +    prepared_status = prepare_tx_receipt_status(status)
    +    RPCView.render("show.json", data: %{"status" => prepared_status})
    +  end
    +
    +  def render("getstatus.json", %{error: error}) do
    +    RPCView.render("show.json", data: prepare_error(error))
    +  end
    +
    +  def render("error.json", assigns) do
    +    RPCView.render("error.json", assigns)
    +  end
    +
    +  defp prepare_tx_receipt_status(""), do: ""
    +
    +  defp prepare_tx_receipt_status(nil), do: ""
    +
    +  defp prepare_tx_receipt_status(:ok), do: "1"
    +
    +  defp prepare_tx_receipt_status(_), do: "0"
    +
    +  defp prepare_error("") do
    +    %{
    +      "isError" => "0",
    +      "errDescription" => ""
    +    }
    +  end
    +
    +  defp prepare_error(error) when is_binary(error) do
    +    %{
    +      "isError" => "1",
    +      "errDescription" => error
    +    }
    +  end
    +
    +  defp prepare_error(error) when is_atom(error) do
    +    %{
    +      "isError" => "1",
    +      "errDescription" => error |> Atom.to_string() |> String.replace("_", " ")
    +    }
    +  end
    +
    +  defp prepare_transaction(transaction, block_height, logs, next_page_params) do
    +    %{
    +      "hash" => "#{transaction.hash}",
    +      "timeStamp" => "#{DateTime.to_unix(transaction.block.timestamp)}",
    +      "blockNumber" => "#{transaction.block_number}",
    +      "confirmations" => "#{block_height - transaction.block_number}",
    +      "success" => if(transaction.status == :ok, do: true, else: false),
    +      "from" => "#{transaction.from_address_hash}",
    +      "to" => "#{transaction.to_address_hash}",
    +      "value" => "#{transaction.value.value}",
    +      "input" => "#{transaction.input}",
    +      "gasLimit" => "#{transaction.gas}",
    +      "gasUsed" => "#{transaction.gas_used}",
    +      "gasPrice" => "#{transaction.gas_price.value}",
    +      "logs" => Enum.map(logs, &prepare_log/1),
    +      "revertReason" => "#{transaction.revert_reason}",
    +      "next_page_params" => next_page_params
    +    }
    +  end
    +
    +  defp prepare_log(log) do
    +    %{
    +      "address" => "#{log.address_hash}",
    +      "topics" => get_topics(log),
    +      "data" => "#{log.data}",
    +      "index" => "#{log.index}"
    +    }
    +  end
    +
    +  defp get_topics(log) do
    +    [log.first_topic, log.second_topic, log.third_topic, log.fourth_topic]
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v1/supply_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v1/supply_view.ex
    new file mode 100644
    index 0000000..df76632
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v1/supply_view.ex
    @@ -0,0 +1,10 @@
    +defmodule BlockScoutWeb.API.V1.SupplyView do
    +  use BlockScoutWeb, :view
    +
    +  def render("supply.json", %{total: total_supply, circulating: circulating_supply}) do
    +    %{
    +      "total_supply" => total_supply,
    +      "circulating_supply" => circulating_supply
    +    }
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex
    new file mode 100644
    index 0000000..54511ab
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/address_view.ex
    @@ -0,0 +1,59 @@
    +defmodule BlockScoutWeb.API.V2.AddressView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.API.V2.{ApiView, Helper, TokenView}
    +  alias BlockScoutWeb.API.V2.Helper
    +
    +  def render("message.json", assigns) do
    +    ApiView.render("message.json", assigns)
    +  end
    +
    +  def render("address.json", %{address: address, conn: conn}) do
    +    prepare_address(address, conn)
    +  end
    +
    +  def render("token_balances.json", %{token_balances: token_balances}) do
    +    Enum.map(token_balances, &prepare_token_balance/1)
    +  end
    +
    +  def render("coin_balance.json", %{coin_balance: coin_balance}) do
    +    prepare_coin_balance_history_entry(coin_balance)
    +  end
    +
    +  def render("coin_balances.json", %{coin_balances: coin_balances, next_page_params: next_page_params}) do
    +    %{"items" => Enum.map(coin_balances, &prepare_coin_balance_history_entry/1), "next_page_params" => next_page_params}
    +  end
    +
    +  def render("coin_balances_by_day.json", %{coin_balances_by_day: coin_balances_by_day}) do
    +    Enum.map(coin_balances_by_day, &prepare_coin_balance_history_by_day_entry/1)
    +  end
    +
    +  def prepare_address(address, conn \\ nil) do
    +    Helper.address_with_info(conn, address, address.hash)
    +  end
    +
    +  def prepare_token_balance({token_balance, token}) do
    +    %{
    +      "value" => token_balance.value,
    +      "token" => TokenView.render("token.json", %{token: token}),
    +      "token_id" => token_balance.token_id
    +    }
    +  end
    +
    +  def prepare_coin_balance_history_entry(coin_balance) do
    +    %{
    +      "transaction_hash" => coin_balance.transaction_hash,
    +      "block_number" => coin_balance.block_number,
    +      "delta" => coin_balance.delta,
    +      "value" => coin_balance.value,
    +      "block_timestamp" => coin_balance.block_timestamp
    +    }
    +  end
    +
    +  def prepare_coin_balance_history_by_day_entry(coin_balance_by_day) do
    +    %{
    +      "date" => coin_balance_by_day.date,
    +      "value" => coin_balance_by_day.value
    +    }
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/api_v2.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/api_v2.ex
    new file mode 100644
    index 0000000..6bd12a4
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/api_v2.ex
    @@ -0,0 +1,9 @@
    +defmodule BlockScoutWeb.API.V2 do
    +  @moduledoc """
    +    API V2 context
    +  """
    +
    +  def enabled? do
    +    Application.get_env(:block_scout_web, __MODULE__)[:enabled]
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/api_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/api_view.ex
    new file mode 100644
    index 0000000..1fde984
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/api_view.ex
    @@ -0,0 +1,7 @@
    +defmodule BlockScoutWeb.API.V2.ApiView do
    +  def render("message.json", %{message: message}) do
    +    %{
    +      "message" => message
    +    }
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex
    new file mode 100644
    index 0000000..bddcddf
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex
    @@ -0,0 +1,106 @@
    +defmodule BlockScoutWeb.API.V2.BlockView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.BlockView
    +  alias BlockScoutWeb.API.V2.{ApiView, Helper}
    +  alias Explorer.Chain
    +  alias Explorer.Chain.Block
    +  alias Explorer.Counters.BlockPriorityFeeCounter
    +
    +  def render("message.json", assigns) do
    +    ApiView.render("message.json", assigns)
    +  end
    +
    +  def render("blocks.json", %{blocks: blocks, next_page_params: next_page_params}) do
    +    %{"items" => Enum.map(blocks, &prepare_block(&1, nil)), "next_page_params" => next_page_params}
    +  end
    +
    +  def render("blocks.json", %{blocks: blocks}) do
    +    Enum.map(blocks, &prepare_block(&1, nil))
    +  end
    +
    +  def render("block.json", %{block: block, conn: conn}) do
    +    prepare_block(block, conn, true)
    +  end
    +
    +  def render("block.json", %{block: block, socket: _socket}) do
    +    # single_block? set to true in order to prevent heavy fetching of reward type
    +    prepare_block(block, nil, false)
    +  end
    +
    +  def prepare_block(block, conn, single_block? \\ false) do
    +    burned_fee = Chain.burned_fees(block.transactions, block.base_fee_per_gas)
    +    priority_fee = block.base_fee_per_gas && BlockPriorityFeeCounter.fetch(block.hash)
    +
    +    tx_fees = Chain.txn_fees(block.transactions)
    +
    +    %{
    +      "height" => block.number,
    +      "timestamp" => block.timestamp,
    +      "tx_count" => count_transactions(block),
    +      "miner" => Helper.address_with_info(conn, block.miner, block.miner_hash),
    +      "size" => block.size,
    +      "hash" => block.hash,
    +      "parent_hash" => block.parent_hash,
    +      "difficulty" => block.difficulty,
    +      "total_difficulty" => block.total_difficulty,
    +      "gas_used" => block.gas_used,
    +      "gas_limit" => block.gas_limit,
    +      "nonce" => block.nonce,
    +      "base_fee_per_gas" => block.base_fee_per_gas,
    +      "burnt_fees" => burned_fee,
    +      "priority_fee" => priority_fee,
    +      "extra_data" => "TODO",
    +      "uncles_hashes" => prepare_uncles(block.uncle_relations),
    +      "state_root" => "TODO",
    +      "rewards" => prepare_rewards(block.rewards, block, single_block?),
    +      "gas_target_percentage" => gas_target(block),
    +      "gas_used_percentage" => gas_used_percentage(block),
    +      "burnt_fees_percentage" => burnt_fees_percentage(burned_fee, tx_fees),
    +      "type" => block |> BlockView.block_type() |> String.downcase(),
    +      "tx_fees" => tx_fees
    +    }
    +  end
    +
    +  def prepare_rewards(rewards, block, single_block?) do
    +    Enum.map(rewards, &prepare_reward(&1, block, single_block?))
    +  end
    +
    +  def prepare_reward(reward, block, single_block?) do
    +    %{
    +      "reward" => reward.reward,
    +      "type" => if(single_block?, do: BlockView.block_reward_text(reward, block.miner.hash), else: reward.address_type)
    +    }
    +  end
    +
    +  def prepare_uncles(uncles_relations) when is_list(uncles_relations) do
    +    Enum.map(uncles_relations, &prepare_uncle/1)
    +  end
    +
    +  def prepare_uncles(_), do: []
    +
    +  def prepare_uncle(uncle_relation) do
    +    %{"hash" => uncle_relation.uncle_hash}
    +  end
    +
    +  def gas_target(block) do
    +    elasticity_multiplier = 2
    +    ratio = Decimal.div(block.gas_used, Decimal.div(block.gas_limit, elasticity_multiplier))
    +    ratio |> Decimal.sub(1) |> Decimal.mult(100) |> Decimal.to_float()
    +  end
    +
    +  def gas_used_percentage(block) do
    +    block.gas_used |> Decimal.div(block.gas_limit) |> Decimal.mult(100) |> Decimal.to_float()
    +  end
    +
    +  def burnt_fees_percentage(_, %Decimal{coef: 0}), do: nil
    +
    +  def burnt_fees_percentage(burnt_fees, tx_fees) when not is_nil(tx_fees) and not is_nil(burnt_fees) do
    +    burnt_fees.value |> Decimal.div(tx_fees) |> Decimal.mult(100) |> Decimal.to_float()
    +  end
    +
    +  def burnt_fees_percentage(_, _), do: nil
    +
    +  def count_transactions(%Block{transactions: txs}) when is_list(txs), do: Enum.count(txs)
    +  def count_transactions(_), do: nil
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/config_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/config_view.ex
    new file mode 100644
    index 0000000..be0d9da
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/config_view.ex
    @@ -0,0 +1,7 @@
    +defmodule BlockScoutWeb.API.V2.ConfigView do
    +  def render("json_rpc_url.json", %{url: url}) do
    +    %{
    +      "json_rpc_url" => url
    +    }
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex
    new file mode 100644
    index 0000000..57f914b
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/helper.ex
    @@ -0,0 +1,108 @@
    +defmodule BlockScoutWeb.API.V2.Helper do
    +  @moduledoc """
    +    API V2 helper
    +  """
    +
    +  alias Ecto.Association.NotLoaded
    +  alias Explorer.Chain.Address
    +  alias Explorer.Chain.Transaction.History.TransactionStats
    +
    +  import BlockScoutWeb.Account.AuthController, only: [current_user: 1]
    +  import BlockScoutWeb.Models.GetAddressTags, only: [get_address_tags: 2, get_tags_on_address: 1]
    +
    +  def address_with_info(conn, address, address_hash) do
    +    %{
    +      personal_tags: private_tags,
    +      watchlist_names: watchlist_names
    +    } = get_address_tags(address_hash, current_user(conn))
    +
    +    public_tags = get_tags_on_address(address_hash)
    +
    +    Map.merge(address_with_info(address, address_hash), %{
    +      "private_tags" => private_tags,
    +      "watchlist_names" => watchlist_names,
    +      "public_tags" => public_tags
    +    })
    +  end
    +
    +  def address_with_info(%Address{} = address, _address_hash) do
    +    %{
    +      "hash" => to_string(address),
    +      "is_contract" => is_smart_contract(address),
    +      "name" => address_name(address),
    +      "implementation_name" => implementation_name(address),
    +      "is_verified" => is_verified(address)
    +    }
    +  end
    +
    +  def address_with_info(%NotLoaded{}, address_hash) do
    +    address_with_info(nil, address_hash)
    +  end
    +
    +  def address_with_info(nil, address_hash) do
    +    %{"hash" => address_hash, "is_contract" => false, "name" => nil, "implementation_name" => nil, "is_verified" => nil}
    +  end
    +
    +  def address_name(%Address{names: [_ | _] = address_names}) do
    +    case Enum.find(address_names, &(&1.primary == true)) do
    +      nil ->
    +        %Address.Name{name: name} = Enum.at(address_names, 0)
    +        name
    +
    +      %Address.Name{name: name} ->
    +        name
    +    end
    +  end
    +
    +  def address_name(_), do: nil
    +
    +  def implementation_name(%Address{smart_contract: %{implementation_name: implementation_name}}),
    +    do: implementation_name
    +
    +  def implementation_name(_), do: nil
    +
    +  def is_smart_contract(%Address{contract_code: nil}), do: false
    +  def is_smart_contract(%Address{contract_code: _}), do: true
    +  def is_smart_contract(%NotLoaded{}), do: nil
    +  def is_smart_contract(_), do: false
    +
    +  def is_verified(%Address{smart_contract: nil}), do: false
    +  def is_verified(%Address{smart_contract: %NotLoaded{}}), do: nil
    +  def is_verified(%Address{smart_contract: _}), do: true
    +
    +  def market_cap(:standard, %{available_supply: available_supply, usd_value: usd_value})
    +      when is_nil(available_supply) or is_nil(usd_value) do
    +    Decimal.new(0)
    +  end
    +
    +  def market_cap(:standard, %{available_supply: available_supply, usd_value: usd_value}) do
    +    Decimal.mult(available_supply, usd_value)
    +  end
    +
    +  def market_cap(:standard, exchange_rate) do
    +    exchange_rate.market_cap_usd
    +  end
    +
    +  def market_cap(module, exchange_rate) do
    +    module.market_cap(exchange_rate)
    +  end
    +
    +  def get_transaction_stats do
    +    stats_scale = date_range(1)
    +    transaction_stats = TransactionStats.by_date_range(stats_scale.earliest, stats_scale.latest)
    +
    +    # Need datapoint for legend if none currently available.
    +    if Enum.empty?(transaction_stats) do
    +      [%{number_of_transactions: 0, gas_used: 0}]
    +    else
    +      transaction_stats
    +    end
    +  end
    +
    +  def date_range(num_days) do
    +    today = Date.utc_today()
    +    latest = Date.add(today, -1)
    +    x_days_back = Date.add(latest, -1 * (num_days - 1))
    +    %{earliest: x_days_back, latest: latest}
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex
    new file mode 100644
    index 0000000..434a654
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/search_view.ex
    @@ -0,0 +1,53 @@
    +defmodule BlockScoutWeb.API.V2.SearchView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.Endpoint
    +
    +  def render("search_results.json", %{search_results: search_results, next_page_params: next_page_params}) do
    +    %{"items" => Enum.map(search_results, &prepare_search_result/1), "next_page_params" => next_page_params}
    +  end
    +
    +  def prepare_search_result(%{type: "token"} = search_result) do
    +    %{
    +      "type" => search_result.type,
    +      "name" => search_result.name,
    +      "symbol" => search_result.symbol,
    +      "address" => search_result.address_hash,
    +      "token_url" => token_path(Endpoint, :show, search_result.address_hash),
    +      "address_url" => address_path(Endpoint, :show, search_result.address_hash)
    +    }
    +  end
    +
    +  def prepare_search_result(%{type: address_or_contract} = search_result)
    +      when address_or_contract in ["address", "contract"] do
    +    %{
    +      "type" => search_result.type,
    +      "name" => search_result.name,
    +      "address" => search_result.address_hash,
    +      "url" => address_path(Endpoint, :show, search_result.address_hash)
    +    }
    +  end
    +
    +  def prepare_search_result(%{type: "block"} = search_result) do
    +    block_hash = hash_to_string(search_result.block_hash)
    +
    +    %{
    +      "type" => search_result.type,
    +      "block_number" => search_result.block_number,
    +      "block_hash" => block_hash,
    +      "url" => block_path(Endpoint, :show, block_hash)
    +    }
    +  end
    +
    +  def prepare_search_result(%{type: "transaction"} = search_result) do
    +    tx_hash = hash_to_string(search_result.tx_hash)
    +
    +    %{
    +      "type" => search_result.type,
    +      "tx_hash" => tx_hash,
    +      "url" => transaction_path(Endpoint, :show, tx_hash)
    +    }
    +  end
    +
    +  defp hash_to_string(hash), do: "0x" <> Base.encode16(hash, case: :lower)
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex
    new file mode 100644
    index 0000000..e0bdae9
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/token_view.ex
    @@ -0,0 +1,13 @@
    +defmodule BlockScoutWeb.API.V2.TokenView do
    +  def render("token.json", %{token: token}) do
    +    %{
    +      "address" => token.contract_address_hash,
    +      "symbol" => token.symbol,
    +      "name" => token.name,
    +      "decimals" => token.decimals,
    +      "type" => token.type,
    +      "holders" => to_string(token.holder_count),
    +      "exchange_rate" => token.usd_value && to_string(token.usd_value)
    +    }
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex
    new file mode 100644
    index 0000000..dc1b9ef
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex
    @@ -0,0 +1,447 @@
    +defmodule BlockScoutWeb.API.V2.TransactionView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.API.V2.{ApiView, Helper, TokenView}
    +  alias BlockScoutWeb.{ABIEncodedValueView, TransactionView}
    +  alias BlockScoutWeb.Models.GetTransactionTags
    +  alias BlockScoutWeb.Tokens.Helpers
    +  alias Ecto.Association.NotLoaded
    +  alias Explorer.ExchangeRates.Token, as: TokenRate
    +  alias Explorer.{Chain, Market}
    +  alias Explorer.Chain.{Address, Block, InternalTransaction, Log, Token, Transaction, Wei}
    +  alias Explorer.Chain.Block.Reward
    +  alias Explorer.Counters.AverageBlockTime
    +  alias Timex.Duration
    +
    +  import BlockScoutWeb.Account.AuthController, only: [current_user: 1]
    +
    +  def render("message.json", assigns) do
    +    ApiView.render("message.json", assigns)
    +  end
    +
    +  def render("transactions.json", %{transactions: transactions, next_page_params: next_page_params, conn: conn}) do
    +    %{"items" => Enum.map(transactions, &prepare_transaction(&1, conn, false)), "next_page_params" => next_page_params}
    +  end
    +
    +  def render("transactions.json", %{transactions: transactions, conn: conn}) do
    +    Enum.map(transactions, &prepare_transaction(&1, conn, false))
    +  end
    +
    +  def render("transaction.json", %{transaction: transaction, conn: conn}) do
    +    prepare_transaction(transaction, conn, true)
    +  end
    +
    +  def render("raw_trace.json", %{internal_transactions: internal_transactions}) do
    +    InternalTransaction.internal_transactions_to_raw(internal_transactions)
    +  end
    +
    +  def render("decoded_log_input.json", %{method_id: method_id, text: text, mapping: mapping}) do
    +    %{"method_id" => method_id, "method_call" => text, "parameters" => prepare_log_mapping(mapping)}
    +  end
    +
    +  def render("decoded_input.json", %{method_id: method_id, text: text, mapping: mapping, error?: _error}) do
    +    %{"method_id" => method_id, "method_call" => text, "parameters" => prepare_method_mapping(mapping)}
    +  end
    +
    +  def render("revert_reason.json", %{raw: raw, decoded: decoded}) do
    +    %{"raw" => raw, "decoded" => decoded}
    +  end
    +
    +  def render("token_transfers.json", %{token_transfers: token_transfers, next_page_params: next_page_params, conn: conn}) do
    +    %{"items" => Enum.map(token_transfers, &prepare_token_transfer(&1, conn)), "next_page_params" => next_page_params}
    +  end
    +
    +  def render("token_transfers.json", %{token_transfers: token_transfers, conn: conn}) do
    +    Enum.map(token_transfers, &prepare_token_transfer(&1, conn))
    +  end
    +
    +  def render("token_transfer.json", %{token_transfer: token_transfer, conn: conn}) do
    +    prepare_token_transfer(token_transfer, conn)
    +  end
    +
    +  def render("internal_transactions.json", %{
    +        internal_transactions: internal_transactions,
    +        next_page_params: next_page_params,
    +        conn: conn
    +      }) do
    +    %{
    +      "items" => Enum.map(internal_transactions, &prepare_internal_transaction(&1, conn)),
    +      "next_page_params" => next_page_params
    +    }
    +  end
    +
    +  def render("logs.json", %{logs: logs, next_page_params: next_page_params, tx_hash: tx_hash}) do
    +    %{"items" => Enum.map(logs, fn log -> prepare_log(log, tx_hash) end), "next_page_params" => next_page_params}
    +  end
    +
    +  def render("logs.json", %{logs: logs, next_page_params: next_page_params}) do
    +    %{
    +      "items" => Enum.map(logs, fn log -> prepare_log(log, log.transaction) end),
    +      "next_page_params" => next_page_params
    +    }
    +  end
    +
    +  def prepare_token_transfer(token_transfer, conn) do
    +    %{
    +      "tx_hash" => token_transfer.transaction_hash,
    +      "from" => Helper.address_with_info(conn, token_transfer.from_address, token_transfer.from_address_hash),
    +      "to" => Helper.address_with_info(conn, token_transfer.to_address, token_transfer.to_address_hash),
    +      "total" => prepare_token_transfer_total(token_transfer),
    +      "token" => TokenView.render("token.json", %{token: Market.add_price(token_transfer.token)}),
    +      "type" => Chain.get_token_transfer_type(token_transfer)
    +    }
    +  end
    +
    +  def prepare_token_transfer_total(token_transfer) do
    +    case Helpers.token_transfer_amount_for_api(token_transfer) do
    +      {:ok, :erc721_instance} ->
    +        %{"token_id" => token_transfer.token_id}
    +
    +      {:ok, :erc1155_instance, value, decimals} ->
    +        %{"token_id" => token_transfer.token_id, "value" => value, "decimals" => decimals}
    +
    +      {:ok, :erc1155_instance, values, token_ids, decimals} ->
    +        Enum.map(Enum.zip(values, token_ids), fn {value, token_id} ->
    +          %{"value" => value, "token_id" => token_id, "decimals" => decimals}
    +        end)
    +
    +      {:ok, value, decimals} ->
    +        %{"value" => value, "decimals" => decimals}
    +
    +      _ ->
    +        nil
    +    end
    +  end
    +
    +  def prepare_internal_transaction(internal_transaction, conn) do
    +    %{
    +      "error" => internal_transaction.error,
    +      "success" => is_nil(internal_transaction.error),
    +      "type" => internal_transaction.call_type,
    +      "transaction_hash" => internal_transaction.transaction_hash,
    +      "from" =>
    +        Helper.address_with_info(
    +          conn,
    +          internal_transaction.from_address,
    +          internal_transaction.from_address_hash
    +        ),
    +      "to" => Helper.address_with_info(conn, internal_transaction.to_address, internal_transaction.to_address_hash),
    +      "created_contract" =>
    +        Helper.address_with_info(
    +          conn,
    +          internal_transaction.created_contract_address,
    +          internal_transaction.created_contract_address_hash
    +        ),
    +      "value" => internal_transaction.value,
    +      "block" => internal_transaction.block_number,
    +      "timestamp" => internal_transaction.transaction.block.timestamp,
    +      "index" => internal_transaction.index,
    +      "gas_limit" => internal_transaction.gas
    +    }
    +  end
    +
    +  def prepare_log(log, transaction_or_hash) do
    +    decoded = decode_log(log, transaction_or_hash)
    +
    +    %{
    +      "address" => Helper.address_with_info(log.address, log.address_hash),
    +      "topics" => [
    +        log.first_topic,
    +        log.second_topic,
    +        log.third_topic,
    +        log.fourth_topic
    +      ],
    +      "data" => log.data,
    +      "index" => log.index,
    +      "decoded" => decoded,
    +      "smart_contract" => smart_contract_info(transaction_or_hash)
    +    }
    +  end
    +
    +  defp smart_contract_info(%Transaction{} = tx), do: Helper.address_with_info(tx.to_address, tx.to_address_hash)
    +  defp smart_contract_info(_), do: nil
    +
    +  defp decode_log(log, %Transaction{} = tx) do
    +    case log |> Log.decode(tx) |> format_decoded_log_input() do
    +      {:ok, method_id, text, mapping} ->
    +        render(__MODULE__, "decoded_log_input.json", method_id: method_id, text: text, mapping: mapping)
    +
    +      _ ->
    +        nil
    +    end
    +  end
    +
    +  defp decode_log(log, transaction_hash), do: decode_log(log, %Transaction{hash: transaction_hash})
    +
    +  defp prepare_transaction({%Reward{} = emission_reward, %Reward{} = validator_reward}, conn, _single_tx?) do
    +    %{
    +      "emission_reward" => emission_reward.reward,
    +      "block_hash" => validator_reward.block_hash,
    +      "from" => Helper.address_with_info(conn, emission_reward.address, emission_reward.address_hash),
    +      "to" => Helper.address_with_info(conn, validator_reward.address, validator_reward.address_hash),
    +      "types" => [:reward]
    +    }
    +  end
    +
    +  defp prepare_transaction(%Transaction{} = transaction, conn, single_tx?) do
    +    base_fee_per_gas = transaction.block && transaction.block.base_fee_per_gas
    +    max_priority_fee_per_gas = transaction.max_priority_fee_per_gas
    +    max_fee_per_gas = transaction.max_fee_per_gas
    +
    +    priority_fee_per_gas = priority_fee_per_gas(max_priority_fee_per_gas, base_fee_per_gas, max_fee_per_gas)
    +
    +    burned_fee = burned_fee(transaction, max_fee_per_gas, base_fee_per_gas)
    +
    +    status = transaction |> Chain.transaction_to_status() |> format_status()
    +
    +    revert_reason = revert_reason(status, transaction)
    +
    +    decoded_input = transaction |> Transaction.decoded_input_data() |> format_decoded_input()
    +    decoded_input_data = decoded_input(decoded_input)
    +
    +    %{
    +      "hash" => transaction.hash,
    +      "result" => status,
    +      "status" => transaction.status,
    +      "block" => transaction.block_number,
    +      "timestamp" => transaction.block && transaction.block.timestamp,
    +      "from" => Helper.address_with_info(conn, transaction.from_address, transaction.from_address_hash),
    +      "to" => Helper.address_with_info(conn, transaction.to_address, transaction.to_address_hash),
    +      "created_contract" =>
    +        Helper.address_with_info(conn, transaction.created_contract_address, transaction.created_contract_address_hash),
    +      "confirmations" =>
    +        transaction.block |> Chain.confirmations(block_height: Chain.block_height()) |> format_confirmations(),
    +      "confirmation_duration" => processing_time_duration(transaction),
    +      "value" => transaction.value,
    +      "fee" => transaction |> Chain.fee(:wei) |> format_fee(),
    +      "gas_price" => transaction.gas_price,
    +      "type" => transaction.type,
    +      "gas_used" => transaction.gas_used,
    +      "gas_limit" => transaction.gas,
    +      "max_fee_per_gas" => transaction.max_fee_per_gas,
    +      "max_priority_fee_per_gas" => transaction.max_priority_fee_per_gas,
    +      "base_fee_per_gas" => base_fee_per_gas,
    +      "priority_fee" => priority_fee_per_gas && Wei.mult(priority_fee_per_gas, transaction.gas_used),
    +      "tx_burnt_fee" => burned_fee,
    +      "nonce" => transaction.nonce,
    +      "position" => transaction.index,
    +      "revert_reason" => revert_reason,
    +      "raw_input" => transaction.input,
    +      "decoded_input" => decoded_input_data,
    +      "token_transfers" => token_transfers(transaction.token_transfers, conn, single_tx?),
    +      "token_transfers_overflow" => token_transfers_overflow(transaction.token_transfers, single_tx?),
    +      "exchange_rate" => (Market.get_exchange_rate(Explorer.coin()) || TokenRate.null()).usd_value,
    +      "method" => method_name(transaction, decoded_input),
    +      "tx_types" => tx_types(transaction),
    +      "tx_tag" => GetTransactionTags.get_transaction_tags(transaction.hash, current_user(conn))
    +    }
    +  end
    +
    +  def token_transfers(_, _conn, false), do: nil
    +  def token_transfers(%NotLoaded{}, _conn, _), do: nil
    +
    +  def token_transfers(token_transfers, conn, _) do
    +    render("token_transfers.json", %{
    +      token_transfers: Enum.take(token_transfers, Chain.get_token_transfers_per_transaction_preview_count()),
    +      conn: conn
    +    })
    +  end
    +
    +  def token_transfers_overflow(_, false), do: nil
    +  def token_transfers_overflow(%NotLoaded{}, _), do: false
    +
    +  def token_transfers_overflow(token_transfers, _),
    +    do: Enum.count(token_transfers) > Chain.get_token_transfers_per_transaction_preview_count()
    +
    +  defp priority_fee_per_gas(max_priority_fee_per_gas, base_fee_per_gas, max_fee_per_gas) do
    +    if is_nil(max_priority_fee_per_gas) or is_nil(base_fee_per_gas),
    +      do: nil,
    +      else:
    +        Enum.min_by([max_priority_fee_per_gas, Wei.sub(max_fee_per_gas, base_fee_per_gas)], fn x ->
    +          Wei.to(x, :wei)
    +        end)
    +  end
    +
    +  defp burned_fee(transaction, max_fee_per_gas, base_fee_per_gas) do
    +    if !is_nil(max_fee_per_gas) and !is_nil(transaction.gas_used) and !is_nil(base_fee_per_gas) do
    +      if Decimal.compare(max_fee_per_gas.value, 0) == :eq do
    +        %Wei{value: Decimal.new(0)}
    +      else
    +        Wei.mult(base_fee_per_gas, transaction.gas_used)
    +      end
    +    else
    +      nil
    +    end
    +  end
    +
    +  defp revert_reason(status, transaction) do
    +    if is_binary(status) && status |> String.downcase() |> String.contains?("reverted") do
    +      case TransactionView.transaction_revert_reason(transaction) do
    +        {:error, _contract_not_verified, candidates} when candidates != [] ->
    +          {:ok, method_id, text, mapping} = Enum.at(candidates, 0)
    +          render(__MODULE__, "decoded_input.json", method_id: method_id, text: text, mapping: mapping, error?: true)
    +
    +        {:ok, method_id, text, mapping} ->
    +          render(__MODULE__, "decoded_input.json", method_id: method_id, text: text, mapping: mapping, error?: true)
    +
    +        _ ->
    +          hex = TransactionView.get_pure_transaction_revert_reason(transaction)
    +          utf8 = TransactionView.decoded_revert_reason(transaction)
    +          render(__MODULE__, "revert_reason.json", raw: hex, decoded: utf8)
    +      end
    +    end
    +  end
    +
    +  defp decoded_input(decoded_input) do
    +    case decoded_input do
    +      {:ok, method_id, text, mapping} ->
    +        render(__MODULE__, "decoded_input.json", method_id: method_id, text: text, mapping: mapping, error?: false)
    +
    +      _ ->
    +        nil
    +    end
    +  end
    +
    +  def prepare_method_mapping(mapping) do
    +    Enum.map(mapping, fn {name, type, value} ->
    +      %{"name" => name, "type" => type, "value" => ABIEncodedValueView.value_json(type, value)}
    +    end)
    +  end
    +
    +  def prepare_log_mapping(mapping) do
    +    Enum.map(mapping, fn {name, type, indexed?, value} ->
    +      %{"name" => name, "type" => type, "indexed" => indexed?, "value" => ABIEncodedValueView.value_json(type, value)}
    +    end)
    +  end
    +
    +  defp format_status({:error, reason}), do: reason
    +  defp format_status(status), do: status
    +
    +  defp format_decoded_input({:error, _, []}), do: nil
    +  defp format_decoded_input({:error, _, candidates}), do: Enum.at(candidates, 0)
    +  defp format_decoded_input({:ok, _identifier, _text, _mapping} = decoded), do: decoded
    +  defp format_decoded_input(_), do: nil
    +
    +  defp format_decoded_log_input({:error, :could_not_decode}), do: nil
    +  defp format_decoded_log_input({:error, :no_matching_function}), do: nil
    +  defp format_decoded_log_input({:ok, _method_id, _text, _mapping} = decoded), do: decoded
    +  defp format_decoded_log_input({:error, _, candidates}), do: Enum.at(candidates, 0)
    +
    +  def format_confirmations({:ok, confirmations}), do: confirmations
    +  def format_confirmations(_), do: 0
    +
    +  def format_fee({type, value}), do: %{"type" => type, "value" => value}
    +
    +  def processing_time_duration(%Transaction{block: nil}) do
    +    []
    +  end
    +
    +  def processing_time_duration(%Transaction{earliest_processing_start: nil}) do
    +    avg_time = AverageBlockTime.average_block_time()
    +
    +    if avg_time == {:error, :disabled} do
    +      []
    +    else
    +      [
    +        0,
    +        avg_time
    +        |> Duration.to_milliseconds()
    +      ]
    +    end
    +  end
    +
    +  def processing_time_duration(%Transaction{
    +        block: %Block{timestamp: end_time},
    +        earliest_processing_start: earliest_processing_start,
    +        inserted_at: inserted_at
    +      }) do
    +    long_interval = abs(diff(earliest_processing_start, end_time))
    +    short_interval = abs(diff(inserted_at, end_time))
    +    merge_intervals(short_interval, long_interval)
    +  end
    +
    +  def merge_intervals(short, long) when short == long, do: [short]
    +
    +  def merge_intervals(short, long) do
    +    [short, long]
    +  end
    +
    +  def diff(left, right) do
    +    left
    +    |> Timex.diff(right, :milliseconds)
    +  end
    +
    +  defp method_name(_, {:ok, _method_id, text, _mapping}) do
    +    Transaction.parse_method_name(text, false)
    +  end
    +
    +  defp method_name(%Transaction{to_address: to_address, input: %{bytes: <>}}, _) do
    +    if Helper.is_smart_contract(to_address) do
    +      "0x" <> Base.encode16(method_id, case: :lower)
    +    else
    +      nil
    +    end
    +  end
    +
    +  defp method_name(_, _) do
    +    nil
    +  end
    +
    +  defp tx_types(tx, types \\ [], stage \\ :token_transfer)
    +
    +  defp tx_types(%Transaction{token_transfers: token_transfers} = tx, types, :token_transfer) do
    +    types =
    +      if !is_nil(token_transfers) && token_transfers != [] && !match?(%NotLoaded{}, token_transfers) do
    +        [:token_transfer | types]
    +      else
    +        types
    +      end
    +
    +    tx_types(tx, types, :token_creation)
    +  end
    +
    +  defp tx_types(%Transaction{created_contract_address: created_contract_address} = tx, types, :token_creation) do
    +    types =
    +      if match?(%Address{}, created_contract_address) && match?(%Token{}, created_contract_address.token) do
    +        [:token_creation | types]
    +      else
    +        types
    +      end
    +
    +    tx_types(tx, types, :contract_creation)
    +  end
    +
    +  defp tx_types(
    +         %Transaction{created_contract_address_hash: created_contract_address_hash} = tx,
    +         types,
    +         :contract_creation
    +       ) do
    +    types =
    +      if is_nil(created_contract_address_hash) do
    +        types
    +      else
    +        [:contract_creation | types]
    +      end
    +
    +    tx_types(tx, types, :contract_call)
    +  end
    +
    +  defp tx_types(%Transaction{to_address: to_address} = tx, types, :contract_call) do
    +    types =
    +      if Helper.is_smart_contract(to_address) do
    +        [:contract_call | types]
    +      else
    +        types
    +      end
    +
    +    tx_types(tx, types, :coin_transfer)
    +  end
    +
    +  defp tx_types(%Transaction{value: value}, types, :coin_transfer) do
    +    if Decimal.compare(value.value, 0) == :gt do
    +      [:coin_transfer | types]
    +    else
    +      types
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/api_docs_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api_docs_view.ex
    new file mode 100644
    index 0000000..2026024
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/api_docs_view.ex
    @@ -0,0 +1,90 @@
    +defmodule BlockScoutWeb.APIDocsView do
    +  use BlockScoutWeb, :view
    +
    +  alias BlockScoutWeb.LayoutView
    +  alias Explorer
    +
    +  def action_tile_id(module, action) do
    +    "#{module}-#{action}"
    +  end
    +
    +  def query_params(module, action) do
    +    module_and_action(module, action) <> Enum.join(required_params(action))
    +  end
    +
    +  def input_placeholder(param) do
    +    "#{param.key} - #{param.description}"
    +  end
    +
    +  def model_type_definition(definition) when is_binary(definition) do
    +    definition
    +  end
    +
    +  def model_type_definition(definition_func) when is_function(definition_func, 1) do
    +    coin = Explorer.coin()
    +    definition_func.(coin)
    +  end
    +
    +  defp module_and_action(module, action) do
    +    "?module=#{module}&action=#{action.name}"
    +  end
    +
    +  defp required_params(action) do
    +    Enum.map(action.required_params, fn param ->
    +      "&#{param.key}=" <> "{#{param.placeholder}}"
    +    end)
    +  end
    +
    +  def blockscout_url(set_path) when set_path == false do
    +    url_params = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url]
    +    host = url_params[:host]
    +
    +    scheme = Keyword.get(url_params, :scheme, "http")
    +
    +    if host != "localhost" do
    +      "#{scheme}://#{host}"
    +    else
    +      port = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:http][:port]
    +      "#{scheme}://#{host}:#{to_string(port)}"
    +    end
    +  end
    +
    +  def blockscout_url(set_path, is_api) when set_path == true do
    +    url_params = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url]
    +    host = url_params[:host]
    +
    +    path =
    +      if is_api do
    +        url_params[:api_path]
    +      else
    +        url_params[:path]
    +      end
    +
    +    scheme = Keyword.get(url_params, :scheme, "http")
    +
    +    if host != "localhost" do
    +      "#{scheme}://#{host}#{path}"
    +    else
    +      port = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:http][:port]
    +      "#{scheme}://#{host}:#{to_string(port)}"
    +    end
    +  end
    +
    +  def api_url do
    +    is_api = true
    +    set_path = true
    +
    +    set_path
    +    |> blockscout_url(is_api)
    +    |> Path.join("api")
    +  end
    +
    +  def eth_rpc_api_url do
    +    is_api = true
    +    set_path = true
    +
    +    set_path
    +    |> blockscout_url(is_api)
    +    |> Path.join("api/eth-rpc")
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/block_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/block_transaction_view.ex
    new file mode 100644
    index 0000000..aef7f41
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/block_transaction_view.ex
    @@ -0,0 +1,17 @@
    +defmodule BlockScoutWeb.BlockTransactionView do
    +  use BlockScoutWeb, :view
    +
    +  import BlockScoutWeb.Gettext, only: [gettext: 1]
    +
    +  def block_not_found_message({:ok, true}) do
    +    gettext("Easy Cowboy! This block does not exist yet!")
    +  end
    +
    +  def block_not_found_message({:ok, false}) do
    +    gettext("This block has not been processed yet.")
    +  end
    +
    +  def block_not_found_message({:error, :hash}) do
    +    gettext("Block not found, please try again later.")
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/block_view.ex b/apps/block_scout_web/lib/block_scout_web/views/block_view.ex
    new file mode 100644
    index 0000000..7fdabe4
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/block_view.ex
    @@ -0,0 +1,85 @@
    +defmodule BlockScoutWeb.BlockView do
    +  use BlockScoutWeb, :view
    +
    +  import Math.Enum, only: [mean: 1]
    +
    +  alias Ecto.Association.NotLoaded
    +  alias Explorer.Chain
    +  alias Explorer.Chain.{Block, Wei}
    +  alias Explorer.Chain.Block.Reward
    +  alias Explorer.Counters.{BlockBurnedFeeCounter, BlockPriorityFeeCounter}
    +
    +  @dialyzer :no_match
    +
    +  def average_gas_price(%Block{transactions: transactions}) do
    +    average =
    +      transactions
    +      |> Enum.map(&Decimal.to_float(Wei.to(&1.gas_price, :gwei)))
    +      |> mean()
    +      |> Kernel.||(0)
    +      |> BlockScoutWeb.Cldr.Number.to_string!()
    +
    +    unit_text = gettext("Gwei")
    +
    +    "#{average} #{unit_text}"
    +  end
    +
    +  def block_type(%Block{consensus: false, nephews: %NotLoaded{}}), do: "Reorg"
    +  def block_type(%Block{consensus: false, nephews: []}), do: "Reorg"
    +  def block_type(%Block{consensus: false}), do: "Uncle"
    +  def block_type(_block), do: "Block"
    +
    +  @doc """
    +  Work-around for spec issue in `Cldr.Unit.to_string!/1`
    +  """
    +  def cldr_unit_to_string!(unit) do
    +    # We do this to trick Dialyzer to not complain about non-local returns caused by bug in Cldr.Unit.to_string! spec
    +    case :erlang.phash2(1, 1) do
    +      0 ->
    +        BlockScoutWeb.Cldr.Unit.to_string!(unit)
    +
    +      1 ->
    +        # does not occur
    +        ""
    +    end
    +  end
    +
    +  def formatted_gas(gas, format \\ []) do
    +    BlockScoutWeb.Cldr.Number.to_string!(gas, format)
    +  end
    +
    +  def formatted_timestamp(%Block{timestamp: timestamp}) do
    +    Timex.format!(timestamp, "%b-%d-%Y %H:%M:%S %p %Z", :strftime)
    +  end
    +
    +  def show_reward?([]), do: false
    +  def show_reward?(_), do: true
    +
    +  def block_reward_text(%Reward{address_hash: beneficiary_address, address_type: :validator}, block_miner_address) do
    +    if Application.get_env(:explorer, Explorer.Chain.Block.Reward, %{})[:keys_manager_contract_address] do
    +      %{payout_key: block_miner_payout_address} = Reward.get_validator_payout_key_by_mining(block_miner_address)
    +
    +      if beneficiary_address == block_miner_payout_address do
    +        gettext("Miner Reward")
    +      else
    +        gettext("Chore Reward")
    +      end
    +    else
    +      gettext("Miner Reward")
    +    end
    +  end
    +
    +  def block_reward_text(%Reward{address_type: :emission_funds}, _block_miner_address) do
    +    gettext("Emission Reward")
    +  end
    +
    +  def block_reward_text(%Reward{address_type: :uncle}, _block_miner_address) do
    +    gettext("Uncle Reward")
    +  end
    +
    +  def combined_rewards_value(block) do
    +    block
    +    |> Chain.block_combined_rewards()
    +    |> format_wei_value(:ether)
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/chain_view.ex b/apps/block_scout_web/lib/block_scout_web/views/chain_view.ex
    new file mode 100644
    index 0000000..43d66a2
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/chain_view.ex
    @@ -0,0 +1,72 @@
    +defmodule BlockScoutWeb.ChainView do
    +  use BlockScoutWeb, :view
    +
    +  require Decimal
    +  import Number.Currency, only: [number_to_currency: 2]
    +  import BlockScoutWeb.API.V2.Helper, only: [market_cap: 2]
    +
    +  alias BlockScoutWeb.LayoutView
    +  alias Explorer.Chain.Cache.GasPriceOracle
    +
    +  def format_usd_value(nil), do: ""
    +
    +  def format_usd_value(value) do
    +    if Decimal.is_decimal(value) do
    +      "#{format_currency_value(Decimal.to_float(value))} USD"
    +    else
    +      "#{format_currency_value(value)} USD"
    +    end
    +  end
    +
    +  def format_currency_value(value, symbol \\ "$")
    +
    +  def format_currency_value(nil, _symbol), do: ""
    +
    +  def format_currency_value(%Decimal{} = value, symbol) do
    +    value
    +    |> Decimal.to_float()
    +    |> format_currency_value(symbol)
    +  end
    +
    +  def format_currency_value(value, _symbol) when not is_float(value) do
    +    "N/A"
    +  end
    +
    +  def format_currency_value(value, symbol) when is_float(value) and value < 0 do
    +    "#{symbol}0.00"
    +  end
    +
    +  def format_currency_value(value, symbol) when is_float(value) and value < 0.000001 do
    +    "Less than #{symbol}0.000001"
    +  end
    +
    +  def format_currency_value(value, symbol) when is_float(value) and value < 1 do
    +    "#{number_to_currency(value, unit: symbol, precision: 6)}"
    +  end
    +
    +  def format_currency_value(value, symbol) when is_float(value) and value < 100_000 do
    +    "#{number_to_currency(value, unit: symbol)}"
    +  end
    +
    +  def format_currency_value(value, _symbol) when value >= 1_000_000 and value <= 999_000_000 do
    +    {:ok, value} = Cldr.Number.to_string(value, format: :short, currency: :USD, fractional_digits: 2)
    +    value
    +  end
    +
    +  def format_currency_value(value, symbol) when is_float(value) do
    +    "#{number_to_currency(value, unit: symbol, precision: 0)}"
    +  end
    +
    +  defp gas_prices do
    +    case GasPriceOracle.get_gas_prices() do
    +      {:ok, gas_prices} ->
    +        gas_prices
    +
    +      nil ->
    +        nil
    +
    +      _ ->
    +        nil
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/cldr_helper/number.ex b/apps/block_scout_web/lib/block_scout_web/views/cldr_helper/number.ex
    new file mode 100644
    index 0000000..e5b946e
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/cldr_helper/number.ex
    @@ -0,0 +1,41 @@
    +defmodule BlockScoutWeb.CldrHelper.Number do
    +  @moduledoc """
    +  Work-arounds for `Cldr.Number` bugs
    +  """
    +
    +  def to_string(decimal, options) do
    +    # We do this to trick Dialyzer to not complain about non-local returns caused by bug in Cldr.Number.to_string spec
    +    case :erlang.phash2(1, 1) do
    +      0 ->
    +        BlockScoutWeb.Cldr.Number.to_string(decimal, options)
    +
    +      1 ->
    +        # does not occur
    +        ""
    +    end
    +  end
    +
    +  def to_string!(decimal) do
    +    # We do this to trick Dialyzer to not complain about non-local returns caused by bug in Cldr.Number.to_string! spec
    +    case :erlang.phash2(1, 1) do
    +      0 ->
    +        BlockScoutWeb.Cldr.Number.to_string!(decimal)
    +
    +      1 ->
    +        # does not occur
    +        ""
    +    end
    +  end
    +
    +  def to_string!(decimal, options) do
    +    # We do this to trick Dialyzer to not complain about non-local returns caused by bug in Cldr.Number.to_string! spec
    +    case :erlang.phash2(1, 1) do
    +      0 ->
    +        BlockScoutWeb.Cldr.Number.to_string!(decimal, options)
    +
    +      1 ->
    +        # does not occur
    +        ""
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/common_components_view.ex b/apps/block_scout_web/lib/block_scout_web/views/common_components_view.ex
    new file mode 100644
    index 0000000..f950e79
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/common_components_view.ex
    @@ -0,0 +1,7 @@
    +defmodule BlockScoutWeb.CommonComponentsView do
    +  use BlockScoutWeb, :view
    +
    +  def balance_percentage_enabled?(total_supply) do
    +    Application.get_env(:block_scout_web, :show_percentage) && total_supply > 0
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/csv_export.ex b/apps/block_scout_web/lib/block_scout_web/views/csv_export.ex
    new file mode 100644
    index 0000000..07b9564
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/csv_export.ex
    @@ -0,0 +1,44 @@
    +defmodule BlockScoutWeb.CsvExportView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.Chain
    +  alias Explorer.Chain.Address
    +
    +  defp type_display_name(type) do
    +    case type do
    +      "internal-transactions" -> "internal transactions"
    +      "transactions" -> "transactions"
    +      "token-transfers" -> "token transfers"
    +      "logs" -> "logs"
    +      _ -> ""
    +    end
    +  end
    +
    +  defp type_download_path(type) do
    +    case type do
    +      "internal-transactions" -> :internal_transactions_csv
    +      "transactions" -> :transactions_csv
    +      "token-transfers" -> :token_transfers_csv
    +      "logs" -> :logs_csv
    +      _ -> ""
    +    end
    +  end
    +
    +  defp address_checksum(address_hash_string) do
    +    with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string) do
    +      address_hash
    +      |> Address.checksum()
    +    end
    +  end
    +
    +  defp default_period_start do
    +    DateTime.utc_now()
    +    |> Timex.shift(months: -1)
    +    |> Timex.format!("{YYYY}-{0M}-{0D}")
    +  end
    +
    +  defp default_period_end do
    +    DateTime.utc_now()
    +    |> Timex.format!("{YYYY}-{0M}-{0D}")
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/currency_helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/currency_helpers.ex
    new file mode 100644
    index 0000000..cd11a42
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/currency_helpers.ex
    @@ -0,0 +1,95 @@
    +defmodule BlockScoutWeb.CurrencyHelpers do
    +  @moduledoc """
    +  Helper functions for interacting with `t:BlockScoutWeb.ExchangeRates.USD.t/0` values.
    +  """
    +
    +  alias BlockScoutWeb.CldrHelper.Number
    +
    +  @doc """
    +  Formats the given integer value to a currency format.
    +
    +  ## Examples
    +
    +      iex> BlockScoutWeb.CurrencyHelpers.format_integer_to_currency(1000000)
    +      "1,000,000"
    +  """
    +  @spec format_integer_to_currency(non_neg_integer()) :: String.t()
    +  def format_integer_to_currency(value) do
    +    {:ok, formatted} = Number.to_string(value, format: "#,##0")
    +
    +    formatted
    +  end
    +
    +  @doc """
    +  Formats the given amount according to given decimals.
    +
    +  ## Examples
    +
    +      iex> format_according_to_decimals(nil, Decimal.new(5))
    +      "-"
    +
    +      iex> format_according_to_decimals(Decimal.new(20500000), Decimal.new(5))
    +      "205"
    +
    +      iex> format_according_to_decimals(Decimal.new(20500000), Decimal.new(7))
    +      "2.05"
    +
    +      iex> format_according_to_decimals(Decimal.new(205000), Decimal.new(12))
    +      "0.000000205"
    +
    +      iex> format_according_to_decimals(Decimal.new(205000), Decimal.new(2))
    +      "2,050"
    +
    +      iex> format_according_to_decimals(205000, Decimal.new(2))
    +      "2,050"
    +
    +      iex> format_according_to_decimals(105000, Decimal.new(0))
    +      "105,000"
    +
    +      iex> format_according_to_decimals(105000000000000000000, Decimal.new(100500))
    +      "105"
    +
    +      iex> format_according_to_decimals(105000000000000000000, nil)
    +      "105,000,000,000,000,000,000"
    +  """
    +  @spec format_according_to_decimals(non_neg_integer() | nil, nil) :: String.t()
    +  def format_according_to_decimals(nil, _) do
    +    "-"
    +  end
    +
    +  def format_according_to_decimals(value, nil) do
    +    format_according_to_decimals(value, Decimal.new(0))
    +  end
    +
    +  def format_according_to_decimals(value, decimals) when is_integer(value) do
    +    value
    +    |> Decimal.new()
    +    |> format_according_to_decimals(decimals)
    +  end
    +
    +  @spec format_according_to_decimals(Decimal.t(), Decimal.t()) :: String.t()
    +  def format_according_to_decimals(value, decimals) do
    +    if Decimal.compare(decimals, 24) == :gt do
    +      format_according_to_decimals(value, Decimal.new(18))
    +    else
    +      value
    +      |> divide_decimals(decimals)
    +      |> thousands_separator()
    +    end
    +  end
    +
    +  defp thousands_separator(value) do
    +    if Decimal.to_float(value) > 999 do
    +      Number.to_string!(value)
    +    else
    +      Decimal.to_string(value, :normal)
    +    end
    +  end
    +
    +  @spec divide_decimals(Decimal.t(), Decimal.t()) :: Decimal.t()
    +  def divide_decimals(%{sign: sign, coef: coef, exp: exp}, decimals) do
    +    sign
    +    |> Decimal.new(coef, exp - Decimal.to_integer(decimals))
    +    |> Decimal.normalize()
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/error_422.ex b/apps/block_scout_web/lib/block_scout_web/views/error_422.ex
    new file mode 100644
    index 0000000..b813dc9
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/error_422.ex
    @@ -0,0 +1,5 @@
    +defmodule BlockScoutWeb.Error422View do
    +  use BlockScoutWeb, :view
    +
    +  @dialyzer :no_match
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/error_helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/error_helpers.ex
    new file mode 100644
    index 0000000..ff3e632
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/error_helpers.ex
    @@ -0,0 +1,59 @@
    +defmodule BlockScoutWeb.ErrorHelpers do
    +  @moduledoc """
    +  Conveniences for translating and building error messages.
    +  """
    +
    +  use Phoenix.HTML
    +
    +  alias Ecto.Changeset
    +  alias Phoenix.HTML.Form
    +  alias Plug.Conn
    +
    +  @doc """
    +  Generates tag for inlined form input errors.
    +  """
    +  def error_tag(form, field, opts \\ []) do
    +    Enum.map(Keyword.get_values(form.errors, field), fn error ->
    +      content_tag(:span, translate_error(error), Keyword.merge([class: "has-error"], opts))
    +    end)
    +  end
    +
    +  @doc """
    +  Gets the errors for a form's input.
    +  """
    +  def errors_for_field(%Form{source: %Conn{}}, _), do: []
    +
    +  def errors_for_field(%Form{source: %Changeset{action: nil}}, _), do: []
    +
    +  def errors_for_field(%Form{source: %Changeset{action: :ignore}}, _), do: []
    +
    +  def errors_for_field(%Form{source: %Changeset{errors: errors}}, field) do
    +    for error <- Keyword.get_values(errors, field) do
    +      translate_error(error)
    +    end
    +  end
    +
    +  @doc """
    +  Translates an error message using gettext.
    +  """
    +  def translate_error({msg, opts}) do
    +    # Because error messages were defined within Ecto, we must
    +    # call the Gettext module passing our Gettext backend. We
    +    # also use the "errors" domain as translations are placed
    +    # in the errors.po file.
    +    # Ecto will pass the :count keyword if the error message is
    +    # meant to be pluralized.
    +    # On your own code and templates, depending on whether you
    +    # need the message to be pluralized or not, this could be
    +    # written simply as:
    +    #
    +    #     dngettext "errors", "1 file", "%{count} files", count
    +    #     dgettext "errors", "is invalid"
    +    #
    +    if count = opts[:count] do
    +      Gettext.dngettext(BlockScoutWeb.Gettext, "errors", msg, msg, count, opts)
    +    else
    +      Gettext.dgettext(BlockScoutWeb.Gettext, "errors", msg, opts)
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/error_view.ex b/apps/block_scout_web/lib/block_scout_web/views/error_view.ex
    new file mode 100644
    index 0000000..090159d
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/error_view.ex
    @@ -0,0 +1,34 @@
    +defmodule BlockScoutWeb.ErrorView do
    +  use BlockScoutWeb, :view
    +
    +  # when type in ["json", "html"]
    +  def render("404." <> _type, _assigns) do
    +    "Page not found"
    +  end
    +
    +  def render("400." <> _type, _assigns) do
    +    "Bad request"
    +  end
    +
    +  def render("401." <> _type, _assigns) do
    +    "Unauthorized"
    +  end
    +
    +  def render("403." <> _type, _assigns) do
    +    "Forbidden"
    +  end
    +
    +  def render("422." <> _type, _assigns) do
    +    "Unprocessable entity"
    +  end
    +
    +  def render("500." <> _type, _assigns) do
    +    "Internal server error"
    +  end
    +
    +  # In case no render clause matches or no
    +  # template is found, let's render it as 500
    +  def template_not_found(_template, assigns) do
    +    render("500.html", assigns)
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/form_view.ex b/apps/block_scout_web/lib/block_scout_web/views/form_view.ex
    new file mode 100644
    index 0000000..06144b1
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/form_view.ex
    @@ -0,0 +1,66 @@
    +defmodule BlockScoutWeb.FormView do
    +  use BlockScoutWeb, :view
    +
    +  alias Phoenix.HTML.Form
    +
    +  @type text_input_type :: :email | :hidden | :password | :text
    +  @type text_field_option ::
    +          {:default_value, String.t()}
    +          | {:id, String.t()}
    +          | {:label, String.t()}
    +          | {:placeholder, String.t()}
    +          | {:required, boolean()}
    +  defguard is_text_input(type) when type in ~w(email hidden password text)a
    +
    +  @doc """
    +  Renders a text input field with certain properties.
    +
    +  ## Supported Options
    +
    +  * `:label` - Label for the input field
    +
    +  ## Options as HTML 5 Attriutes
    +
    +  The following options will be applied as HTML 5 attributes on the
    +  `` element:
    +
    +  * `:default_value` - Default value to attach to the input field
    +  * `:id` - ID to attatch to the input field
    +  * `:placeholder` - Placeholder text for the input field
    +  * `:required` - Mark the input field as required
    +  * `:type` - Input field type
    +  """
    +  @spec text_field(Form.t(), atom(), text_input_type(), [text_field_option()]) :: Phoenix.HTML.safe()
    +  def text_field(%Form{} = form, form_key, input_type, opts \\ [])
    +      when is_text_input(input_type) and is_atom(form_key) do
    +    errors = errors_for_field(form, form_key)
    +    label = Keyword.get(opts, :label)
    +    id = Keyword.get(opts, :id)
    +
    +    supported_input_field_attrs = ~w(default_value id placeholder required)a
    +    base_input_field_opts = Keyword.take(opts, supported_input_field_attrs)
    +
    +    input_field_class =
    +      case errors do
    +        [_ | _] -> "form-control is-invalid"
    +        _ -> "form-control"
    +      end
    +
    +    input_field_opts = Keyword.put(base_input_field_opts, :class, input_field_class)
    +    input_field = input_for_type(input_type).(form, form_key, input_field_opts)
    +
    +    render_opts = [
    +      errors: errors,
    +      id: id,
    +      input_field: input_field,
    +      label: label
    +    ]
    +
    +    render("text_field.html", render_opts)
    +  end
    +
    +  defp input_for_type(:email), do: &email_input/3
    +  defp input_for_type(:text), do: &text_input/3
    +  defp input_for_type(:hidden), do: &hidden_input/3
    +  defp input_for_type(:password), do: &password_input/3
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/icons_view.ex b/apps/block_scout_web/lib/block_scout_web/views/icons_view.ex
    new file mode 100644
    index 0000000..40c5ee3
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/icons_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.IconsView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex
    new file mode 100644
    index 0000000..46252f7
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex
    @@ -0,0 +1,29 @@
    +defmodule BlockScoutWeb.InternalTransactionView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.Chain.InternalTransaction
    +
    +  import BlockScoutWeb.Gettext
    +
    +  @doc """
    +  Returns the formatted string for the type of the internal transaction.
    +
    +  When the type is `call`, we return the formatted string for the call type.
    +
    +  Examples:
    +
    +  iex> BlockScoutWeb.InternalTransactionView.type(%Explorer.Chain.InternalTransaction{type: :reward})
    +  "Reward"
    +
    +  iex> BlockScoutWeb.InternalTransactionView.type(%Explorer.Chain.InternalTransaction{type: :call, call_type: :delegatecall})
    +  "Delegate Call"
    +  """
    +  def type(%InternalTransaction{type: :call, call_type: :call}), do: gettext("Call")
    +  def type(%InternalTransaction{type: :call, call_type: :callcode}), do: gettext("Call Code")
    +  def type(%InternalTransaction{type: :call, call_type: :delegatecall}), do: gettext("Delegate Call")
    +  def type(%InternalTransaction{type: :call, call_type: :staticcall}), do: gettext("Static Call")
    +  def type(%InternalTransaction{type: :create}), do: gettext("Create")
    +  def type(%InternalTransaction{type: :create2}), do: gettext("Create2")
    +  def type(%InternalTransaction{type: :selfdestruct}), do: gettext("Self-Destruct")
    +  def type(%InternalTransaction{type: :reward}), do: gettext("Reward")
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/layout_view.ex b/apps/block_scout_web/lib/block_scout_web/views/layout_view.ex
    new file mode 100644
    index 0000000..e94cbc0
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/layout_view.ex
    @@ -0,0 +1,281 @@
    +defmodule BlockScoutWeb.LayoutView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.{Chain, CustomContractsHelpers}
    +  alias Plug.Conn
    +  alias Poison.Parser
    +
    +  import BlockScoutWeb.AddressView, only: [from_address_hash: 1]
    +
    +  @default_other_networks [
    +    %{
    +      title: "POA",
    +      url: "https://blockscout.com/poa/core"
    +    },
    +    %{
    +      title: "Sokol",
    +      url: "https://blockscout.com/poa/sokol",
    +      test_net?: true
    +    },
    +    %{
    +      title: "Gnosis Chain",
    +      url: "https://blockscout.com/xdai/mainnet"
    +    },
    +    %{
    +      title: "Ethereum Classic",
    +      url: "https://blockscout.com/etc/mainnet",
    +      other?: true
    +    },
    +    %{
    +      title: "RSK",
    +      url: "https://blockscout.com/rsk/mainnet",
    +      other?: true
    +    }
    +  ]
    +
    +  alias BlockScoutWeb.SocialMedia
    +
    +  def logo do
    +    Keyword.get(application_config(), :logo)
    +  end
    +
    +  def logo_footer do
    +    Keyword.get(application_config(), :logo_footer) || Keyword.get(application_config(), :logo)
    +  end
    +
    +  def logo_text do
    +    Keyword.get(application_config(), :logo_text) || nil
    +  end
    +
    +  def subnetwork_title do
    +    Keyword.get(application_config(), :subnetwork) || "Sokol"
    +  end
    +
    +  def network_title do
    +    Keyword.get(application_config(), :network) || "POA"
    +  end
    +
    +  defp application_config do
    +    Application.get_env(:block_scout_web, BlockScoutWeb.Chain)
    +  end
    +
    +  def configured_social_media_services do
    +    SocialMedia.links()
    +  end
    +
    +  def issue_link(conn) do
    +    params = [
    +      labels: "BlockScout",
    +      body: issue_body(conn),
    +      title: subnetwork_title() <> ": "
    +    ]
    +
    +    issue_url = "#{Application.get_env(:block_scout_web, :footer)[:github_link]}/issues/new"
    +
    +    [issue_url, "?", URI.encode_query(params)]
    +  end
    +
    +  defp issue_body(conn) do
    +    user_agent =
    +      case Conn.get_req_header(conn, "user-agent") do
    +        [] -> "unknown"
    +        [user_agent] -> if String.valid?(user_agent), do: user_agent, else: "unknown"
    +        _other -> "unknown"
    +      end
    +
    +    """
    +    *Describe your issue here.*
    +
    +    ### Environment
    +    * Elixir Version: #{System.version()}
    +    * Erlang Version: #{System.otp_release()}
    +    * BlockScout Version: #{version()}
    +
    +    * User Agent: `#{user_agent}`
    +
    +    ### Steps to reproduce
    +
    +    *Tell us how to reproduce this issue. If possible, push up a branch to your fork with a regression test we can run to reproduce locally.*
    +
    +    ### Expected Behaviour
    +
    +    *Tell us what should happen.*
    +
    +    ### Actual Behaviour
    +
    +    *Tell us what happens instead.*
    +    """
    +  end
    +
    +  def version do
    +    BlockScoutWeb.version()
    +  end
    +
    +  def release_link(version) do
    +    release_link_env_var = Application.get_env(:block_scout_web, :release_link)
    +
    +    release_link =
    +      cond do
    +        version == "" || version == nil ->
    +          nil
    +
    +        release_link_env_var == "" || release_link_env_var == nil ->
    +          "https://github.com/blockscout/blockscout/releases/tag/" <> version
    +
    +        true ->
    +          release_link_env_var
    +      end
    +
    +    if release_link == nil do
    +      ""
    +    else
    +      html_escape({:safe, "#{version}"})
    +    end
    +  end
    +
    +  def ignore_version?("unknown"), do: true
    +  def ignore_version?(_), do: false
    +
    +  def other_networks do
    +    get_other_networks =
    +      if Application.get_env(:block_scout_web, :other_networks) do
    +        try do
    +          :block_scout_web
    +          |> Application.get_env(:other_networks)
    +          |> Parser.parse!(%{keys: :atoms!})
    +        rescue
    +          _ ->
    +            []
    +        end
    +      else
    +        @default_other_networks
    +      end
    +
    +    get_other_networks
    +    |> Enum.reject(fn %{title: title} ->
    +      title == subnetwork_title()
    +    end)
    +    |> Enum.sort()
    +  end
    +
    +  def main_nets(nets) do
    +    nets
    +    |> Enum.reject(&Map.get(&1, :test_net?))
    +  end
    +
    +  def test_nets(nets) do
    +    nets
    +    |> Enum.filter(&Map.get(&1, :test_net?))
    +  end
    +
    +  def dropdown_nets do
    +    other_networks()
    +    |> Enum.reject(&Map.get(&1, :hide_in_dropdown?))
    +  end
    +
    +  def dropdown_main_nets do
    +    dropdown_nets()
    +    |> main_nets()
    +  end
    +
    +  def dropdown_test_nets do
    +    dropdown_nets()
    +    |> test_nets()
    +  end
    +
    +  def dropdown_head_main_nets do
    +    dropdown_nets()
    +    |> main_nets()
    +    |> Enum.reject(&Map.get(&1, :other?))
    +  end
    +
    +  def dropdown_other_nets do
    +    dropdown_nets()
    +    |> main_nets()
    +    |> Enum.filter(&Map.get(&1, :other?))
    +  end
    +
    +  def other_explorers do
    +    if Application.get_env(:block_scout_web, :link_to_other_explorers) do
    +      decode_other_explorers_json(Application.get_env(:block_scout_web, :other_explorers, []))
    +    else
    +      []
    +    end
    +  end
    +
    +  defp decode_other_explorers_json(data) do
    +    Jason.decode!(~s(#{data}))
    +  rescue
    +    _ -> []
    +  end
    +
    +  def webapp_url(conn) do
    +    :block_scout_web
    +    |> Application.get_env(:webapp_url)
    +    |> validate_url()
    +    |> case do
    +      :error -> chain_path(conn, :show)
    +      {:ok, url} -> url
    +    end
    +  end
    +
    +  def api_url do
    +    :block_scout_web
    +    |> Application.get_env(:api_url)
    +    |> validate_url()
    +    |> case do
    +      :error -> ""
    +      {:ok, url} -> url
    +    end
    +  end
    +
    +  def apps_list do
    +    apps = Application.get_env(:block_scout_web, :apps)
    +
    +    if apps do
    +      try do
    +        apps
    +        |> Parser.parse!(%{keys: :atoms!})
    +      rescue
    +        _ ->
    +          []
    +      end
    +    else
    +      []
    +    end
    +  end
    +
    +  defp validate_url(url) when is_binary(url) do
    +    case URI.parse(url) do
    +      %URI{host: nil} -> :error
    +      _ -> {:ok, url}
    +    end
    +  end
    +
    +  defp validate_url(_), do: :error
    +
    +  def sign_in_link do
    +    if Mix.env() == :test do
    +      "/auth/auth0"
    +    else
    +      Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url][:path] <> "auth/auth0"
    +    end
    +  end
    +
    +  def sign_out_link do
    +    client_id = Application.get_env(:ueberauth, Ueberauth.Strategy.Auth0.OAuth)[:client_id]
    +    return_to = Application.get_env(:ueberauth, Ueberauth)[:logout_return_to_url]
    +    logout_url = Application.get_env(:ueberauth, Ueberauth)[:logout_url]
    +
    +    if client_id && return_to && logout_url do
    +      params = [
    +        client_id: client_id,
    +        returnTo: return_to
    +      ]
    +
    +      [logout_url, "?", URI.encode_query(params)]
    +    else
    +      []
    +    end
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/log_view.ex b/apps/block_scout_web/lib/block_scout_web/views/log_view.ex
    new file mode 100644
    index 0000000..fe12ba9
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/log_view.ex
    @@ -0,0 +1,3 @@
    +defmodule BlockScoutWeb.LogView do
    +  use BlockScoutWeb, :view
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/page_not_found.ex b/apps/block_scout_web/lib/block_scout_web/views/page_not_found.ex
    new file mode 100644
    index 0000000..b5a18f0
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/page_not_found.ex
    @@ -0,0 +1,5 @@
    +defmodule BlockScoutWeb.PageNotFoundView do
    +  use BlockScoutWeb, :view
    +
    +  @dialyzer :no_match
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/pending_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/pending_transaction_view.ex
    new file mode 100644
    index 0000000..12ba2fe
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/pending_transaction_view.ex
    @@ -0,0 +1,5 @@
    +defmodule BlockScoutWeb.PendingTransactionView do
    +  use BlockScoutWeb, :view
    +
    +  @dialyzer :no_match
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/render_helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/render_helpers.ex
    new file mode 100644
    index 0000000..20c35fa
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/render_helpers.ex
    @@ -0,0 +1,21 @@
    +defmodule BlockScoutWeb.RenderHelpers do
    +  @moduledoc """
    +  Helper functions to render partials from view modules
    +  """
    +  use BlockScoutWeb, :view
    +
    +  @doc """
    +  Renders html using:
    +  * A list of args including `:view_module` and `:partial` to render a partial with the required keyword list.
    +  * Text that will pass directly through to the template
    +  """
    +  def render_partial(args) when is_list(args) do
    +    render(
    +      Keyword.get(args, :view_module),
    +      Keyword.get(args, :partial),
    +      args
    +    )
    +  end
    +
    +  def render_partial(text), do: text
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/script_helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/script_helpers.ex
    new file mode 100644
    index 0000000..730365c
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/script_helpers.ex
    @@ -0,0 +1,28 @@
    +defmodule BlockScoutWeb.Views.ScriptHelpers do
    +  @moduledoc """
    +  Helpers for rendering view specific script tags.
    +  """
    +
    +  import Phoenix.HTML, only: [sigil_E: 2]
    +  import BlockScoutWeb.Router.Helpers, only: [static_path: 2]
    +
    +  def render_scripts(conn, file_names) do
    +    conn
    +    |> files(file_names)
    +    |> Enum.map(fn file ->
    +      ~E"""
    +        
    +      """
    +    end)
    +  end
    +
    +  defp files(conn, file_names) do
    +    file_names
    +    |> List.wrap()
    +    |> Enum.map(fn file ->
    +      path = "/" <> Path.join("js", file)
    +
    +      static_path(conn, path)
    +    end)
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/search_view.ex b/apps/block_scout_web/lib/block_scout_web/views/search_view.ex
    new file mode 100644
    index 0000000..51bf1b8
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/search_view.ex
    @@ -0,0 +1,19 @@
    +defmodule BlockScoutWeb.SearchView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.Chain
    +  alias Floki
    +
    +  def highlight_search_result(result, query) do
    +    re = ~r/#{query}/i
    +
    +    safe_result =
    +      result
    +      |> html_escape()
    +      |> safe_to_string()
    +
    +    re
    +    |> Regex.replace(safe_result, "\\g{0}", global: true)
    +    |> raw()
    +  end
    +end
    diff --git a/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex
    new file mode 100644
    index 0000000..e5bbb11
    --- /dev/null
    +++ b/apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex
    @@ -0,0 +1,288 @@
    +defmodule BlockScoutWeb.SmartContractView do
    +  use BlockScoutWeb, :view
    +
    +  alias Explorer.Chain
    +  alias Explorer.Chain.{Address, Transaction}
    +  alias Explorer.Chain.Hash.Address, as: HashAddress
    +  alias Explorer.SmartContract.Helper
    +
    +  def queryable?(inputs) when not is_nil(inputs), do: Enum.any?(inputs)
    +
    +  def queryable?(inputs) when is_nil(inputs), do: false
    +
    +  def writable?(function) when not is_nil(function),
    +    do:
    +      !Helper.constructor?(function) && !Helper.event?(function) &&
    +        (Helper.payable?(function) || Helper.nonpayable?(function))
    +
    +  def writable?(function) when is_nil(function), do: false
    +
    +  def outputs?(outputs) when not is_nil(outputs) do
    +    case outputs do
    +      {:error, _} -> false
    +      _ -> Enum.any?(outputs)
    +    end
    +  end
    +
    +  def outputs?(outputs) when is_nil(outputs), do: false
    +
    +  def error?(outputs) when not is_nil(outputs) do
    +    case outputs do
    +      {:error, _} -> true
    +      _ -> false
    +    end
    +  end
    +
    +  def error?(outputs) when is_nil(outputs), do: false
    +
    +  def address?(type), do: type in ["address", "address payable"]
    +  def int?(type), do: String.contains?(type, "int") && !String.contains?(type, "[")
    +
    +  def named_argument?(%{"name" => ""}), do: false
    +  def named_argument?(%{"name" => nil}), do: false
    +  def named_argument?(%{"name" => _}), do: true
    +  def named_argument?(_), do: false
    +
    +  def values_with_type(value, type, names, index, components \\ nil)
    +
    +  def values_with_type(value, type, names, index, components) when is_list(value) do
    +    cond do
    +      String.starts_with?(type, "tuple") ->
    +        tuple_types =
    +          type
    +          |> String.slice(0..-3)
    +          |> supplement_type_with_components(components)
    +
    +        values =
    +          value
    +          |> tuple_array_to_array(tuple_types, fetch_name(names, index + 1))
    +          |> Enum.join("),\n(")
    +
    +        render_array_type_value(type, "(\n" <> values <> ")", fetch_name(names, index))
    +
    +      String.starts_with?(type, "address") ->
    +        values =
    +          value
    +          |> Enum.map_join(", ", &binary_to_utf_string(&1))
    +
    +        render_array_type_value(type, values, fetch_name(names, index))
    +
    +      String.starts_with?(type, "bytes") ->
    +        values =
    +          value
    +          |> Enum.map_join(", ", &binary_to_utf_string(&1))
    +
    +        render_array_type_value(type, values, fetch_name(names, index))
    +
    +      true ->
    +        values =
    +          value
    +          |> Enum.join("),\n(")
    +
    +        render_array_type_value(type, "(\n" <> values <> ")", fetch_name(names, index))
    +    end
    +  end
    +
    +  def values_with_type(value, type, names, index, _components) when is_tuple(value) do
    +    values =
    +      value
    +      |> tuple_to_array(type, fetch_name(names, index + 1))
    +      |> Enum.join("")
    +
    +    render_type_value(type, values, fetch_name(names, index))
    +  end
    +
    +  def values_with_type(value, type, names, index, _components) when type in [:address, "address", "address payable"] do
    +    case HashAddress.cast(value) do
    +      {:ok, address} ->
    +        render_type_value("address", to_string(address), fetch_name(names, index))
    +
    +      _ ->
    +        ""
    +    end
    +  end
    +
    +  def values_with_type(value, "string", names, index, _components),
    +    do: render_type_value("string", value |> Helper.sanitize_input(), fetch_name(names, index))
    +
    +  def values_with_type(value, :string, names, index, _components),
    +    do: render_type_value("string", value |> Helper.sanitize_input(), fetch_name(names, index))
    +
    +  def values_with_type(value, :bytes, names, index, _components),
    +    do: render_type_value("bytes", value |> Helper.sanitize_input(), fetch_name(names, index))
    +
    +  def values_with_type(value, "bool", names, index, _components),
    +    do: render_type_value("bool", to_string(value), fetch_name(names, index))
    +
    +  def values_with_type(value, :bool, names, index, _components),
    +    do: render_type_value("bool", to_string(value), fetch_name(names, index))
    +
    +  def values_with_type(value, type, names, index, _components),
    +    do: render_type_value(type, binary_to_utf_string(value), fetch_name(names, index))
    +
    +  def values_with_type(value, :error, _components), do: render_type_value("error", value, "error")
    +
    +  defp fetch_name(nil, _index), do: nil
    +
    +  defp fetch_name([], _index), do: nil
    +
    +  defp fetch_name(names, index) when is_list(names) do
    +    Enum.at(names, index)
    +  end
    +
    +  defp fetch_name(name, _index) when is_binary(name) do
    +    name
    +  end
    +
    +  def wrap_output(value, is_too_long \\ false) do
    +    if is_too_long do
    +      "
    Click to view#{value}
    " + else + "#{value}" + end + end + + defp tuple_array_to_array(value, type, names) do + value + |> Enum.map(fn item -> + tuple_to_array(item, type, names) + end) + end + + defp tuple_to_array(value, type, names) do + types_string = + type + |> String.slice(6..-2) + + types = + if String.trim(types_string) == "" do + [] + else + types_string + |> String.split(",") + end + + {tuple_types, _} = + types + |> Enum.reduce({[], nil}, fn val, acc -> + {arr, to_merge} = acc + + if to_merge do + if count_string_symbols(val)["]"] > count_string_symbols(val)["["] do + updated_arr = update_last_list_item(arr, val) + {updated_arr, !to_merge} + else + updated_arr = update_last_list_item(arr, val) + {updated_arr, to_merge} + end + else + if count_string_symbols(val)["["] > count_string_symbols(val)["]"] do + # credo:disable-for-next-line + {arr ++ [val], !to_merge} + else + # credo:disable-for-next-line + {arr ++ [val], to_merge} + end + end + end) + + values_list = + value + |> Tuple.to_list() + + values_types_list = Enum.zip(tuple_types, values_list) + + values_types_list + |> Enum.with_index() + |> Enum.map(fn {{type, value}, index} -> + values_with_type(value, type, fetch_name(names, index), 0) + end) + end + + defp update_last_list_item(arr, new_val) do + arr + |> Enum.with_index() + |> Enum.map(fn {item, index} -> + if index == Enum.count(arr) - 1 do + item <> "," <> new_val + else + item + end + end) + end + + defp count_string_symbols(str) do + str + |> String.graphemes() + |> Enum.reduce(%{"[" => 0, "]" => 0}, fn char, acc -> + Map.update(acc, char, 1, &(&1 + 1)) + end) + end + + defp binary_to_utf_string(item) do + case Integer.parse(to_string(item)) do + {item_integer, ""} -> + to_string(item_integer) + + _ -> + if is_binary(item) do + if String.starts_with?(item, "0x") do + item + else + "0x" <> Base.encode16(item, case: :lower) + end + else + to_string(item) + end + end + end + + defp render_type_value(type, value, type) do + "
    (#{type}) : #{value}
    " + end + + defp render_type_value(type, value, name) do + "
    #{name} (#{type}) : #{value}
    " + end + + defp render_array_type_value(type, values, name) do + value_to_display = "[" <> values <> "]" + + render_type_value(type, value_to_display, name) + end + + defp supplement_type_with_components(type, components) do + if type == "tuple" && components do + types = + components + |> Enum.map_join(",", fn component -> + Map.get(component, "type") + end) + + "tuple[" <> types <> "]" + else + type + end + end + + def decode_revert_reason(to_address, revert_reason) do + smart_contract = Chain.address_hash_to_smart_contract(to_address) + + Transaction.decoded_revert_reason( + %Transaction{to_address: %{smart_contract: smart_contract}, hash: to_address}, + revert_reason + ) + end + + def decode_hex_revert_reason(hex_revert_reason) do + case Integer.parse(hex_revert_reason, 16) do + {number, ""} -> + :binary.encode_unsigned(number) + + _ -> + hex_revert_reason + end + end + + def not_last_element?(length, index), do: length > 1 and index < length - 1 +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tab_helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/tab_helpers.ex new file mode 100644 index 0000000..f07e992 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tab_helpers.ex @@ -0,0 +1,66 @@ +defmodule BlockScoutWeb.TabHelpers do + @moduledoc """ + Helper functions for dealing with tabs, which are very common between pages. + """ + + @doc """ + Get the current status of a tab by its name and the request path. + + A tab is considered active if its name responds true to active?/2. + + * returns the string "active" if the tab active. + * returns nil if the tab is not active. + + ## Examples + + iex> BlockScoutWeb.TabHelpers.tab_status("token", "/page/0xSom3tH1ng/token") + "active" + + iex> BlockScoutWeb.TabHelpers.tab_status("token", "/page/0xSom3tH1ng/token_transfer") + nil + """ + def tab_status(tab_name, request_path, show_token_transfers \\ false) do + if tab_active?(tab_name, request_path) do + "active" + else + case request_path do + "/tx/" <> "0x" <> <<_tx_hash::binary-size(64)>> -> + cond do + tab_name == "token-transfers" && show_token_transfers -> + "active" + + tab_name == "internal-transactions" && !show_token_transfers -> + "active" + + true -> + nil + end + + _ -> + nil + end + end + end + + @doc """ + Check if the given tab is the current tab given the request path. + + It is considered active if there is a substring that exactly matches the tab name in the path. + + * returns true if the tab name is in the path. + * returns nil if the tab name is not in the path. + + ## Examples + + iex> BlockScoutWeb.TabHelpers.tab_active?("token", "/page/0xSom3tH1ng/token") + true + + iex> BlockScoutWeb.TabHelpers.tab_active?("token", "/page/0xSom3tH1ng/token_transfer") + false + """ + def tab_active?("transactions", "/address/" <> "0x" <> <<_address_hash::binary-size(40)>>), do: true + + def tab_active?(tab_name, request_path) do + String.match?(request_path, ~r/\/\b#{tab_name}\b/) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/contract_view.ex new file mode 100644 index 0000000..0b90df1 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/contract_view.ex @@ -0,0 +1,6 @@ +defmodule BlockScoutWeb.Tokens.ContractView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.Tokens.OverviewView + alias Explorer.Chain.Address +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex new file mode 100644 index 0000000..40d5f4e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/helpers.ex @@ -0,0 +1,133 @@ +defmodule BlockScoutWeb.Tokens.Helpers do + @moduledoc """ + Helper functions for interacting with `t:BlockScoutWeb.Chain.Token` attributes. + """ + + alias BlockScoutWeb.{AddressView, CurrencyHelpers} + alias Explorer.Chain.{Address, Token} + + @doc """ + Returns the token transfers' amount according to the token's type and decimals. + + When the token's type is ERC-20, then we are going to format the amount according to the token's + decimals considering 0 when the decimals is nil. Case the amount is nil, this function will + return the symbol `--`. + + When the token's type is ERC-721, the function will return a string with the token_id that + represents the ERC-721 token since this kind of token doesn't have amount and decimals. + """ + def token_transfer_amount(%{token: token, amount: amount, amounts: amounts, token_id: token_id, token_ids: token_ids}) do + do_token_transfer_amount(token, amount, amounts, token_id, token_ids) + end + + def token_transfer_amount(%{token: token, amount: amount, token_id: token_id}) do + do_token_transfer_amount(token, amount, nil, token_id, nil) + end + + defp do_token_transfer_amount(%Token{type: "ERC-20"}, nil, nil, _token_id, _token_ids) do + {:ok, "--"} + end + + defp do_token_transfer_amount(%Token{type: "ERC-20", decimals: nil}, amount, _amounts, _token_id, _token_ids) do + {:ok, CurrencyHelpers.format_according_to_decimals(amount, Decimal.new(0))} + end + + defp do_token_transfer_amount(%Token{type: "ERC-20", decimals: decimals}, amount, _amounts, _token_id, _token_ids) do + {:ok, CurrencyHelpers.format_according_to_decimals(amount, decimals)} + end + + defp do_token_transfer_amount(%Token{type: "ERC-721"}, _amount, _amounts, _token_id, _token_ids) do + {:ok, :erc721_instance} + end + + defp do_token_transfer_amount(%Token{type: "ERC-1155", decimals: decimals}, amount, amounts, _token_id, token_ids) do + if amount do + {:ok, :erc1155_instance, CurrencyHelpers.format_according_to_decimals(amount, decimals)} + else + {:ok, :erc1155_instance, amounts, token_ids, decimals} + end + end + + defp do_token_transfer_amount(_token, _amount, _amounts, _token_id, _token_ids) do + nil + end + + def token_transfer_amount_for_api(%{ + token: token, + amount: amount, + amounts: amounts, + token_id: token_id, + token_ids: token_ids + }) do + do_token_transfer_amount_for_api(token, amount, amounts, token_id, token_ids) + end + + def token_transfer_amount_for_api(%{token: token, amount: amount, token_id: token_id}) do + do_token_transfer_amount_for_api(token, amount, nil, token_id, nil) + end + + defp do_token_transfer_amount_for_api(%Token{type: "ERC-20"}, nil, nil, _token_id, _token_ids) do + {:ok, nil} + end + + defp do_token_transfer_amount_for_api( + %Token{type: "ERC-20", decimals: decimals}, + amount, + _amounts, + _token_id, + _token_ids + ) do + {:ok, amount, decimals} + end + + defp do_token_transfer_amount_for_api(%Token{type: "ERC-721"}, _amount, _amounts, _token_id, _token_ids) do + {:ok, :erc721_instance} + end + + defp do_token_transfer_amount_for_api( + %Token{type: "ERC-1155", decimals: decimals}, + amount, + amounts, + _token_id, + token_ids + ) do + if amount do + {:ok, :erc1155_instance, amount, decimals} + else + {:ok, :erc1155_instance, amounts, token_ids, decimals} + end + end + + defp do_token_transfer_amount_for_api(_token, _amount, _amounts, _token_id, _token_ids) do + nil + end + + @doc """ + Returns the token's symbol. + + When the token's symbol is nil, the function will return the contract address hash. + """ + def token_symbol(%Token{symbol: nil, contract_address_hash: address_hash}) do + AddressView.short_hash_left_right(address_hash) + end + + def token_symbol(%Token{symbol: symbol}) do + symbol + end + + @doc """ + Returns the token's name. + + When the token's name is nil, the function will return the contract address hash. + """ + def token_name(%Token{} = token), do: build_token_name(token) + def token_name(%Address.Token{} = address_token), do: build_token_name(address_token) + + defp build_token_name(%{name: nil, contract_address_hash: address_hash}) do + AddressView.short_hash_left_right(address_hash) + end + + defp build_token_name(%{name: name}) do + name + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex new file mode 100644 index 0000000..2edfd93 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex @@ -0,0 +1,76 @@ +defmodule BlockScoutWeb.Tokens.HolderView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.Tokens.OverviewView + alias Explorer.Chain.Token + + @doc """ + Checks if the total supply percentage must be shown. + + ## Examples + + iex> BlockScoutWeb.Tokens.HolderView.show_total_supply_percentage?(nil) + false + + iex> BlockScoutWeb.Tokens.HolderView.show_total_supply_percentage?(0) + false + + iex> BlockScoutWeb.Tokens.HolderView.show_total_supply_percentage?(100) + true + + """ + def show_total_supply_percentage?(nil), do: false + def show_total_supply_percentage?(total_supply), do: total_supply > 0 + + @doc """ + Calculates the percentage of the value from the given total supply. + + ## Examples + + iex> value = Decimal.new(200) + iex> total_supply = Decimal.new(1000) + iex> BlockScoutWeb.Tokens.HolderView.total_supply_percentage(value, total_supply) + "20.0000%" + + """ + def total_supply_percentage(_, 0), do: "N/A%" + + def total_supply_percentage(_, %Decimal{coef: 0}), do: "N/A%" + + def total_supply_percentage(value, total_supply) do + result = + value + |> Decimal.div(total_supply) + |> Decimal.mult(100) + |> Decimal.round(4) + |> Decimal.to_string() + + result <> "%" + end + + @doc """ + Formats the token balance value according to the Token's type. + + ## Examples + + iex> token = build(:token, type: "ERC-20", decimals: Decimal.new(2)) + iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(100000, nil, token) + "1,000" + + iex> token = build(:token, type: "ERC-721") + iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(1, nil, token) + 1 + + """ + def format_token_balance_value(value, _id, %Token{type: "ERC-20", decimals: decimals}) do + format_according_to_decimals(value, decimals) + end + + def format_token_balance_value(value, id, %Token{type: "ERC-1155", decimals: decimals}) do + to_string(format_according_to_decimals(value, decimals)) <> " TokenID " <> to_string(id) + end + + def format_token_balance_value(value, _id, _token) do + value + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/holder_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/holder_view.ex new file mode 100644 index 0000000..38cf207 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/holder_view.ex @@ -0,0 +1,5 @@ +defmodule BlockScoutWeb.Tokens.Instance.HolderView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.Tokens.Instance.OverviewView +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/metadata_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/metadata_view.ex new file mode 100644 index 0000000..758e6a3 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/metadata_view.ex @@ -0,0 +1,9 @@ +defmodule BlockScoutWeb.Tokens.Instance.MetadataView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.Tokens.Instance.OverviewView + + def format_metadata(nil), do: "" + + def format_metadata(metadata), do: Poison.encode!(metadata, %{pretty: true}) +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex new file mode 100644 index 0000000..4a25a93 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/overview_view.ex @@ -0,0 +1,199 @@ +defmodule BlockScoutWeb.Tokens.Instance.OverviewView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.CurrencyHelpers + alias Explorer.Chain + alias Explorer.Chain.{Address, SmartContract, Token} + alias Explorer.SmartContract.Helper + alias FileInfo + alias MIME + alias Path + + import BlockScoutWeb.APIDocsView, only: [blockscout_url: 1, blockscout_url: 2] + + @tabs ["token-transfers", "metadata"] + @stub_image "/images/controller.svg" + + def token_name?(%Token{name: nil}), do: false + def token_name?(%Token{name: _}), do: true + + def decimals?(%Token{decimals: nil}), do: false + def decimals?(%Token{decimals: _}), do: true + + def total_supply?(%Token{total_supply: nil}), do: false + def total_supply?(%Token{total_supply: _}), do: true + + def media_src(instance, high_quality_media? \\ nil) + def media_src(nil, _), do: @stub_image + + def media_src(instance, high_quality_media?) do + result = get_media_src(instance.metadata, high_quality_media?) + + if String.trim(result) == "", do: media_src(nil), else: result + end + + defp get_media_src(nil, _), do: media_src(nil) + + defp get_media_src(metadata, high_quality_media?) do + cond do + metadata["animation_url"] && high_quality_media? -> + retrieve_image(metadata["animation_url"]) + + metadata["image_url"] -> + retrieve_image(metadata["image_url"]) + + metadata["image"] -> + retrieve_image(metadata["image"]) + + metadata["properties"]["image"]["description"] -> + metadata["properties"]["image"]["description"] + + true -> + media_src(nil) + end + end + + def media_type("data:image/" <> _data) do + "image" + end + + def media_type("data:video/" <> _data) do + "video" + end + + def media_type("data:" <> _data) do + nil + end + + def media_type(media_src) when not is_nil(media_src) do + ext = media_src |> Path.extname() |> String.trim() + + mime_type = + if ext == "" do + case HTTPoison.head(media_src, [], follow_redirect: true) do + {:ok, %HTTPoison.Response{status_code: 200, headers: headers}} -> + headers_map = Map.new(headers, fn {key, value} -> {String.downcase(key), value} end) + headers_map["content-type"] + + _ -> + nil + end + else + ext_with_dot = + media_src + |> Path.extname() + + "." <> ext = ext_with_dot + + ext + |> MIME.type() + end + + if mime_type do + basic_mime_type = mime_type |> String.split("/") |> Enum.at(0) + + basic_mime_type + else + nil + end + end + + def media_type(nil), do: nil + + def external_url(nil), do: nil + + def external_url(instance) do + result = + if instance.metadata && instance.metadata["external_url"] do + instance.metadata["external_url"] + else + external_url(nil) + end + + if !result || (result && String.trim(result)) == "", do: external_url(nil), else: result + end + + def total_supply_usd(token) do + tokens = CurrencyHelpers.divide_decimals(token.total_supply, token.decimals) + price = token.usd_value + Decimal.mult(tokens, price) + end + + def smart_contract_with_read_only_functions?( + %Token{contract_address: %Address{smart_contract: %SmartContract{}}} = token + ) do + Enum.any?(token.contract_address.smart_contract.abi, &Helper.queriable_method?(&1)) + end + + def smart_contract_with_read_only_functions?(%Token{contract_address: %Address{smart_contract: nil}}), do: false + + def qr_code(conn, token_id, hash) do + token_instance_path = token_instance_path(conn, :show, to_string(hash), to_string(token_id)) + + url_params = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint)[:url] + api_path = url_params[:api_path] + path = url_params[:path] + + url_prefix = + if String.length(path) > 0 && path != "/" do + set_path = false + blockscout_url(set_path) + else + if String.length(api_path) > 0 && api_path != "/" do + is_api = true + set_path = true + blockscout_url(set_path, is_api) + else + set_path = false + blockscout_url(set_path) + end + end + + url = Path.join(url_prefix, token_instance_path) + + url + |> QRCode.to_png() + |> Base.encode64() + end + + def current_tab_name(request_path) do + @tabs + |> Enum.filter(&tab_active?(&1, request_path)) + |> tab_name() + end + + defp retrieve_image(image) when is_nil(image), do: @stub_image + + defp retrieve_image(image) when is_map(image) do + image["description"] + end + + defp retrieve_image(image) when is_list(image) do + image_url = image |> Enum.at(0) + retrieve_image(image_url) + end + + defp retrieve_image(image_url) do + image_url + |> URI.encode() + |> compose_ipfs_url() + end + + defp compose_ipfs_url(image_url) do + cond do + image_url =~ ~r/^ipfs:\/\/ipfs/ -> + "ipfs://ipfs" <> ipfs_uid = image_url + "https://ipfs.io/ipfs/" <> ipfs_uid + + image_url =~ ~r/^ipfs:\/\// -> + "ipfs://" <> ipfs_uid = image_url + "https://ipfs.io/ipfs/" <> ipfs_uid + + true -> + image_url + end + end + + defp tab_name(["token-transfers"]), do: gettext("Token Transfers") + defp tab_name(["metadata"]), do: gettext("Metadata") +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex new file mode 100644 index 0000000..2cf314e --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance/transfer_view.ex @@ -0,0 +1,5 @@ +defmodule BlockScoutWeb.Tokens.Instance.TransferView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.Tokens.Instance.OverviewView +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/instance_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance_view.ex new file mode 100644 index 0000000..c18c5b5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/instance_view.ex @@ -0,0 +1,3 @@ +defmodule BlockScoutWeb.Tokens.InstanceView do + use BlockScoutWeb, :view +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/inventory_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/inventory_view.ex new file mode 100644 index 0000000..547d6dd --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/inventory_view.ex @@ -0,0 +1,8 @@ +defmodule BlockScoutWeb.Tokens.InventoryView do + use BlockScoutWeb, :view + + import BlockScoutWeb.Tokens.Instance.OverviewView, only: [media_src: 1, media_type: 1] + + alias BlockScoutWeb.Tokens.OverviewView + alias Explorer.Chain +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex new file mode 100644 index 0000000..e258938 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex @@ -0,0 +1,85 @@ +defmodule BlockScoutWeb.Tokens.OverviewView do + use BlockScoutWeb, :view + + alias Explorer.{Chain, CustomContractsHelpers} + alias Explorer.Chain.{Address, SmartContract, Token} + alias Explorer.SmartContract.{Helper, Writer} + + alias BlockScoutWeb.{AccessHelpers, CurrencyHelpers, LayoutView} + + import BlockScoutWeb.AddressView, only: [from_address_hash: 1] + + @tabs ["token-transfers", "token-holders", "read-contract", "inventory"] + + def decimals?(%Token{decimals: nil}), do: false + def decimals?(%Token{decimals: _}), do: true + + def token_name?(%Token{name: nil}), do: false + def token_name?(%Token{name: _}), do: true + + def total_supply?(%Token{total_supply: nil}), do: false + def total_supply?(%Token{total_supply: _}), do: true + + @doc """ + Get the current tab name/title from the request path and possible tab names. + + The tabs on mobile are represented by a dropdown list, which has a title. This title is the + currently selected tab name. This function returns that name, properly gettext'ed. + + The list of possible tab names for this page is represented by the attribute @tab. + + Raises error if there is no match, so a developer of a new tab must include it in the list. + """ + def current_tab_name(request_path) do + @tabs + |> Enum.filter(&tab_active?(&1, request_path)) + |> tab_name() + end + + defp tab_name(["token-transfers"]), do: gettext("Token Transfers") + defp tab_name(["token-holders"]), do: gettext("Token Holders") + defp tab_name(["read-contract"]), do: gettext("Read Contract") + defp tab_name(["inventory"]), do: gettext("Inventory") + + def display_inventory?(%Token{type: "ERC-721"}), do: true + def display_inventory?(%Token{type: "ERC-1155"}), do: true + def display_inventory?(_), do: false + + def smart_contract_with_read_only_functions?( + %Token{contract_address: %Address{smart_contract: %SmartContract{}}} = token + ) do + Enum.any?(token.contract_address.smart_contract.abi, &Helper.queriable_method?(&1)) + end + + def smart_contract_with_read_only_functions?(%Token{contract_address: %Address{smart_contract: nil}}), do: false + + def smart_contract_is_proxy?(%Token{contract_address: %Address{smart_contract: %SmartContract{}} = address}) do + Chain.proxy_contract?(address.hash, address.smart_contract.abi) + end + + def smart_contract_is_proxy?(%Token{contract_address: %Address{smart_contract: nil}}), do: false + + def smart_contract_with_write_functions?(%Token{ + contract_address: %Address{smart_contract: %SmartContract{}} = address + }) do + Enum.any?( + address.smart_contract.abi, + &Writer.write_function?(&1) + ) + end + + def smart_contract_with_write_functions?(%Token{contract_address: %Address{smart_contract: nil}}), do: false + + @doc """ + Get the total value of the token supply in USD. + """ + def total_supply_usd(token) do + if Map.has_key?(token, :custom_cap) && token.custom_cap do + token.custom_cap + else + tokens = CurrencyHelpers.divide_decimals(token.total_supply, token.decimals) + price = token.usd_value + Decimal.mult(tokens, price) + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex new file mode 100644 index 0000000..3ea5d84 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/transfer_view.ex @@ -0,0 +1,7 @@ +defmodule BlockScoutWeb.Tokens.TransferView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.Tokens.OverviewView + alias Explorer.Chain + alias Explorer.Chain.Address +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens_view.ex new file mode 100644 index 0000000..704d7a9 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/tokens_view.ex @@ -0,0 +1,22 @@ +defmodule BlockScoutWeb.TokensView do + use BlockScoutWeb, :view + + alias Explorer.Chain.{Address, Token} + + def decimals?(%Token{decimals: nil}), do: false + def decimals?(%Token{decimals: _}), do: true + + def token_display_name(%Token{name: nil, symbol: nil}), do: "" + + def token_display_name(%Token{name: "", symbol: ""}), do: "" + + def token_display_name(%Token{name: name, symbol: nil}), do: name + + def token_display_name(%Token{name: name, symbol: ""}), do: name + + def token_display_name(%Token{name: nil, symbol: symbol}), do: symbol + + def token_display_name(%Token{name: "", symbol: symbol}), do: symbol + + def token_display_name(%Token{name: name, symbol: symbol}), do: "#{name} (#{symbol})" +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_internal_transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_internal_transaction_view.ex new file mode 100644 index 0000000..74ff604 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_internal_transaction_view.ex @@ -0,0 +1,4 @@ +defmodule BlockScoutWeb.TransactionInternalTransactionView do + use BlockScoutWeb, :view + @dialyzer :no_match +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_log_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_log_view.ex new file mode 100644 index 0000000..e494bbd --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_log_view.ex @@ -0,0 +1,11 @@ +defmodule BlockScoutWeb.TransactionLogView do + use BlockScoutWeb, :view + @dialyzer :no_match + + alias Explorer.Chain.Log + import BlockScoutWeb.AddressView, only: [implementation_name: 1, primary_name: 1] + + def decode(log, transaction) do + Log.decode(log, transaction) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_raw_trace_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_raw_trace_view.ex new file mode 100644 index 0000000..3adab16 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_raw_trace_view.ex @@ -0,0 +1,18 @@ +defmodule BlockScoutWeb.TransactionRawTraceView do + use BlockScoutWeb, :view + @dialyzer :no_match + + alias Explorer.Chain.InternalTransaction + + def render("scripts.html", %{conn: conn}) do + render_scripts(conn, "raw-trace/code_highlighting.js") + end + + def raw_traces_with_lines(internal_transactions) do + internal_transactions + |> InternalTransaction.internal_transactions_to_raw() + |> Jason.encode!(pretty: true) + |> String.split("\n") + |> Enum.with_index(1) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex new file mode 100644 index 0000000..27b5950 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_state_view.ex @@ -0,0 +1,48 @@ +defmodule BlockScoutWeb.TransactionStateView do + use BlockScoutWeb, :view + + alias Explorer.Chain + alias Explorer.Chain.Wei + + import BlockScoutWeb.TransactionStateController, only: [from_loss: 1, to_profit: 1] + + def has_diff?(%Wei{value: val}) do + not Decimal.eq?(val, Decimal.new(0)) + end + + def has_diff?(val) do + not Decimal.eq?(val, Decimal.new(0)) + end + + def not_negative?(%Wei{value: val}) do + not Decimal.negative?(val) + end + + def not_negative?(val) do + not Decimal.negative?(val) + end + + def absolute_value_of(%Wei{value: val}) do + %Wei{value: Decimal.abs(val)} + end + + def absolute_value_of(val) do + Decimal.abs(val) + end + + def has_state_changes?(tx) do + has_diff?(from_loss(tx)) or has_diff?(to_profit(tx)) + end + + def display_value(balance, :coin) do + format_wei_value(balance, :ether) + end + + def display_value(balance, token_transfer) do + render("_token_balance.html", transfer: token_transfer, balance: balance) + end + + def display_nft(token_transfer) do + render(BlockScoutWeb.TransactionView, "_total_transfers.html", transfer: token_transfer) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex new file mode 100644 index 0000000..66999ad --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_token_transfer_view.ex @@ -0,0 +1,5 @@ +defmodule BlockScoutWeb.TransactionTokenTransferView do + use BlockScoutWeb, :view + + alias Explorer.Chain +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex new file mode 100644 index 0000000..e5e7f99 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex @@ -0,0 +1,599 @@ +defmodule BlockScoutWeb.TransactionView do + use BlockScoutWeb, :view + + alias BlockScoutWeb.{AccessHelpers, AddressView, BlockView, TabHelpers} + alias BlockScoutWeb.Account.AuthController + alias BlockScoutWeb.Cldr.Number + alias Explorer.{Chain, CustomContractsHelpers, Repo} + alias Explorer.Chain.Block.Reward + alias Explorer.Chain.{Address, Block, InternalTransaction, Transaction, Wei} + alias Explorer.Counters.AverageBlockTime + alias Explorer.ExchangeRates.Token + alias Timex.Duration + + import BlockScoutWeb.Gettext + import BlockScoutWeb.AddressView, only: [from_address_hash: 1, short_token_id: 2, tag_name_to_label: 1] + import BlockScoutWeb.Tokens.Helpers + + @tabs ["token-transfers", "internal-transactions", "logs", "raw-trace", "state"] + + @token_burning_title "Token Burning" + @token_minting_title "Token Minting" + @token_transfer_title "Token Transfer" + @token_creation_title "Token Creation" + + @token_burning_type :token_burning + @token_minting_type :token_minting + @token_creation_type :token_spawning + @token_transfer_type :token_transfer + + defguardp is_transaction_type(mod) when mod in [InternalTransaction, Transaction] + + defdelegate formatted_timestamp(block), to: BlockView + + def block_number(%Transaction{block_number: nil}), do: gettext("Block Pending") + def block_number(%Transaction{block: block}), do: [view_module: BlockView, partial: "_link.html", block: block] + def block_number(%Reward{block: block}), do: [view_module: BlockView, partial: "_link.html", block: block] + + def block_timestamp(%Transaction{block_number: nil, inserted_at: time}), do: time + def block_timestamp(%Transaction{block: %Block{timestamp: time}}), do: time + def block_timestamp(%Reward{block: %Block{timestamp: time}}), do: time + + def value_transfer?(%Transaction{input: %{bytes: bytes}}) when bytes in [<<>>, nil] do + true + end + + def value_transfer?(_), do: false + + def token_transfer_type(transaction) do + transaction_with_transfers = Repo.preload(transaction, token_transfers: :token) + + token_transfers_filtered_by_block_hash = + transaction_with_transfers + |> Map.get(:token_transfers, []) + |> Enum.filter(fn token_transfer -> + token_transfer.block_hash == transaction.block_hash + end) + + transaction_with_transfers_filtered = + Map.put(transaction_with_transfers, :token_transfers, token_transfers_filtered_by_block_hash) + + type = Chain.transaction_token_transfer_type(transaction) + if type, do: {type, transaction_with_transfers_filtered}, else: {nil, transaction_with_transfers_filtered} + end + + def aggregate_token_transfers(token_transfers) do + %{ + transfers: {ft_transfers, nft_transfers}, + mintings: {ft_mintings, nft_mintings}, + burnings: {ft_burnings, nft_burnings}, + creations: {ft_creations, nft_creations} + } = + token_transfers + |> Enum.reduce( + %{ + transfers: {[], []}, + mintings: {[], []}, + burnings: {[], []}, + creations: {[], []} + }, + fn token_transfer, acc -> + token_transfer_type = Chain.get_token_transfer_type(token_transfer) + + case token_transfer_type do + :token_transfer -> + transfers = aggregate_reducer(token_transfer, acc.transfers) + + %{ + transfers: transfers, + mintings: acc.mintings, + burnings: acc.burnings, + creations: acc.creations + } + + :token_burning -> + burnings = aggregate_reducer(token_transfer, acc.burnings) + + %{ + transfers: acc.transfers, + mintings: acc.mintings, + burnings: burnings, + creations: acc.creations + } + + :token_minting -> + mintings = aggregate_reducer(token_transfer, acc.mintings) + + %{ + transfers: acc.transfers, + mintings: mintings, + burnings: acc.burnings, + creations: acc.creations + } + + :token_spawning -> + creations = aggregate_reducer(token_transfer, acc.creations) + + %{ + transfers: acc.transfers, + mintings: acc.mintings, + burnings: acc.burnings, + creations: creations + } + end + end + ) + + transfers = ft_transfers ++ nft_transfers + + mintings = ft_mintings ++ nft_mintings + + burnings = ft_burnings ++ nft_burnings + + creations = ft_creations ++ nft_creations + + %{transfers: transfers, mintings: mintings, burnings: burnings, creations: creations} + end + + defp aggregate_reducer(%{amount: amount, amounts: amounts} = token_transfer, {acc1, acc2}) + when is_nil(amount) and is_nil(amounts) do + new_entry = %{ + token: token_transfer.token, + amount: nil, + amounts: [], + token_id: token_transfer.token_id, + token_ids: [], + to_address_hash: token_transfer.to_address_hash, + from_address_hash: token_transfer.from_address_hash + } + + {acc1, [new_entry | acc2]} + end + + defp aggregate_reducer(%{amount: amount, amounts: amounts} = token_transfer, {acc1, acc2}) + when is_nil(amount) and not is_nil(amounts) do + new_entry = %{ + token: token_transfer.token, + amount: nil, + amounts: amounts, + token_id: nil, + token_ids: token_transfer.token_ids, + to_address_hash: token_transfer.to_address_hash, + from_address_hash: token_transfer.from_address_hash + } + + {acc1, [new_entry | acc2]} + end + + defp aggregate_reducer(token_transfer, {acc1, acc2}) do + new_entry = %{ + token: token_transfer.token, + amount: token_transfer.amount, + amounts: [], + token_id: token_transfer.token_id, + token_ids: [], + to_address_hash: token_transfer.to_address_hash, + from_address_hash: token_transfer.from_address_hash + } + + existing_entry = + acc1 + |> Enum.find(fn entry -> + entry.to_address_hash == token_transfer.to_address_hash && + entry.from_address_hash == token_transfer.from_address_hash && + entry.token == token_transfer.token + end) + + new_acc1 = + if existing_entry do + acc1 + |> Enum.map(fn entry -> + if entry.to_address_hash == token_transfer.to_address_hash && + entry.from_address_hash == token_transfer.from_address_hash && + entry.token == token_transfer.token do + updated_entry = %{ + entry + | amount: Decimal.add(new_entry.amount, entry.amount) + } + + updated_entry + else + entry + end + end) + else + [new_entry | acc1] + end + + {new_acc1, acc2} + end + + def token_type_name(type) do + case type do + :erc20 -> gettext("ERC-20 ") + :erc721 -> gettext("ERC-721 ") + :erc1155 -> gettext("ERC-1155 ") + _ -> "" + end + end + + def processing_time_duration(%Transaction{block: nil}) do + :pending + end + + def processing_time_duration(%Transaction{earliest_processing_start: nil}) do + avg_time = AverageBlockTime.average_block_time() + + if avg_time == {:error, :disabled} do + :unknown + else + avg_time_in_secs = + avg_time + |> Duration.to_seconds() + + {:ok, "<= #{avg_time_in_secs} seconds"} + end + end + + def processing_time_duration(%Transaction{ + block: %Block{timestamp: end_time}, + earliest_processing_start: earliest_processing_start, + inserted_at: inserted_at + }) do + with {:ok, long_interval} <- humanized_diff(earliest_processing_start, end_time), + {:ok, short_interval} <- humanized_diff(inserted_at, end_time) do + {:ok, merge_intervals(short_interval, long_interval)} + else + _ -> + :ignore + end + end + + defp merge_intervals(short, long) when short == long, do: short + + defp merge_intervals(short, long) do + [short_time, short_unit] = String.split(short, " ") + [long_time, long_unit] = String.split(long, " ") + + if short_unit == long_unit do + short_time <> "-" <> long_time <> " " <> short_unit + else + short <> " - " <> long + end + end + + defp humanized_diff(left, right) do + left + |> Timex.diff(right, :milliseconds) + |> Duration.from_milliseconds() + |> Timex.format_duration(Explorer.Counters.AverageBlockTimeDurationFormat) + |> case do + {:error, _} = error -> error + duration -> {:ok, duration} + end + end + + def confirmations(%Transaction{block: block}, named_arguments) when is_list(named_arguments) do + case block do + %Block{consensus: true} -> + {:ok, confirmations} = Chain.confirmations(block, named_arguments) + BlockScoutWeb.Cldr.Number.to_string!(confirmations, format: "#,###") + + _ -> + 0 + end + end + + def confirmations_ds_name(blocks_amount_str) do + case Integer.parse(blocks_amount_str) do + {blocks_amount, ""} -> + if rem(blocks_amount, 10) == 1 do + "block" + else + "blocks" + end + + _ -> + "" + end + end + + def contract_creation?(%Transaction{to_address: nil}), do: true + + def contract_creation?(_), do: false + + def fee(%Transaction{} = transaction) do + {_, value} = Chain.fee(transaction, :wei) + value + end + + def format_gas_limit(gas) do + Number.to_string!(gas) + end + + def formatted_fee(%Transaction{} = transaction, opts) do + transaction + |> Chain.fee(:wei) + |> fee_to_denomination(opts) + |> case do + {:actual, value} -> value + {:maximum, value} -> "#{gettext("Max of")} #{value}" + end + end + + def transaction_status(transaction) do + Chain.transaction_to_status(transaction) + end + + def transaction_revert_reason(transaction) do + transaction |> Chain.transaction_to_revert_reason() |> decoded_revert_reason(transaction) + end + + def get_pure_transaction_revert_reason(nil), do: nil + + def get_pure_transaction_revert_reason(transaction), do: Chain.transaction_to_revert_reason(transaction) + + def empty_exchange_rate?(exchange_rate) do + Token.null?(exchange_rate) + end + + def formatted_status(status) do + case status do + :pending -> gettext("Unconfirmed") + _ -> gettext("Confirmed") + end + end + + def formatted_result(status) do + case status do + :pending -> gettext("Pending") + :awaiting_internal_transactions -> gettext("(Awaiting internal transactions for status)") + :success -> gettext("Success") + {:error, :awaiting_internal_transactions} -> gettext("Error: (Awaiting internal transactions for reason)") + # The pool of possible error reasons is unknown or even if it is enumerable, so we can't translate them + {:error, reason} when is_binary(reason) -> gettext("Error: %{reason}", reason: reason) + end + end + + def from_or_to_address?(_token_transfer, nil), do: false + + def from_or_to_address?(%{from_address_hash: from_hash, to_address_hash: to_hash}, %Address{hash: hash}) do + from_hash == hash || to_hash == hash + end + + def gas(%type{gas: gas}) when is_transaction_type(type) do + BlockScoutWeb.Cldr.Number.to_string!(gas) + end + + def skip_decoding?(transaction) do + contract_creation?(transaction) || value_transfer?(transaction) + end + + def decoded_input_data(transaction) do + Transaction.decoded_input_data(transaction) + end + + def decoded_revert_reason(revert_reason, transaction) do + Transaction.decoded_revert_reason(transaction, revert_reason) + end + + @doc """ + Converts a transaction's gas price to a displayable value. + """ + def gas_price(%Transaction{gas_price: gas_price}, unit) when unit in ~w(wei gwei ether)a do + format_wei_value(gas_price, unit) + end + + def gas_used(%Transaction{gas_used: nil}), do: gettext("Pending") + + def gas_used(%Transaction{gas_used: gas_used}) do + Number.to_string!(gas_used) + end + + def gas_used_perc(%Transaction{gas_used: nil}), do: nil + + def gas_used_perc(%Transaction{gas_used: gas_used, gas: gas}) do + if Decimal.compare(gas, 0) == :gt do + gas_used + |> Decimal.div(gas) + |> Decimal.mult(100) + |> Decimal.round(2) + |> Number.to_string!() + else + nil + end + end + + def hash(%Transaction{hash: hash}) do + to_string(hash) + end + + def involves_contract?(%Transaction{from_address: from_address, to_address: to_address}) do + AddressView.contract?(from_address) || AddressView.contract?(to_address) + end + + def involves_token_transfers?(%Transaction{token_transfers: []}), do: false + def involves_token_transfers?(%Transaction{token_transfers: transfers}) when is_list(transfers), do: true + def involves_token_transfers?(_), do: false + + def qr_code(%Transaction{hash: hash}) do + hash + |> to_string() + |> QRCode.to_png() + |> Base.encode64() + end + + def status_class(transaction) do + case Chain.transaction_to_status(transaction) do + :pending -> "tile-status--pending" + :awaiting_internal_transactions -> "tile-status--awaiting-internal-transactions" + :success -> "tile-status--success" + {:error, :awaiting_internal_transactions} -> "tile-status--error--awaiting-internal-transactions" + {:error, reason} when is_binary(reason) -> "tile-status--error--reason" + end + end + + # This is the address to be shown in the to field + def to_address_hash(%Transaction{to_address_hash: nil, created_contract_address_hash: address_hash}), + do: address_hash + + def to_address_hash(%Transaction{to_address_hash: address_hash}), do: address_hash + + def transaction_display_type(%Transaction{} = transaction) do + cond do + involves_token_transfers?(transaction) -> + token_transfer_type = get_transaction_type_from_token_transfers(transaction.token_transfers) + + case token_transfer_type do + @token_minting_type -> gettext(@token_minting_title) + @token_burning_type -> gettext(@token_burning_title) + @token_creation_type -> gettext(@token_creation_title) + @token_transfer_type -> gettext(@token_transfer_title) + end + + contract_creation?(transaction) -> + gettext("Contract Creation") + + involves_contract?(transaction) -> + gettext("Contract Call") + + true -> + gettext("Transaction") + end + end + + def type_suffix(%Transaction{} = transaction) do + cond do + involves_token_transfers?(transaction) -> "token-transfer" + contract_creation?(transaction) -> "contract-creation" + involves_contract?(transaction) -> "contract-call" + true -> "transaction" + end + end + + @doc """ + Converts a transaction's Wei value to Ether and returns a formatted display value. + + ## Options + + * `:include_label` - Boolean. Defaults to true. Flag for displaying unit with value. + """ + def value(%mod{value: value}, opts \\ []) when is_transaction_type(mod) do + include_label? = Keyword.get(opts, :include_label, true) + format_wei_value(value, :ether, include_unit_label: include_label?) + end + + def format_wei_value(value) do + format_wei_value(value, :ether, include_unit_label: false) + end + + defp fee_to_denomination({fee_type, fee}, opts) do + denomination = Keyword.get(opts, :denomination) + include_label? = Keyword.get(opts, :include_label, true) + {fee_type, format_wei_value(Wei.from(fee, :wei), denomination, include_unit_label: include_label?)} + end + + @doc """ + Get the current tab name/title from the request path and possible tab names. + + The tabs on mobile are represented by a dropdown list, which has a title. This title is the currently selected tab name. This function returns that name, properly gettext'ed. + + The list of possible tab names for this page is represented by the attribute @tab. + + Raises an error if there is no match, so a developer of a new tab must include it in the list. + + """ + def current_tab_name(request_path) do + @tabs + |> Enum.filter(&TabHelpers.tab_active?(&1, request_path)) + |> tab_name() + end + + defp tab_name(["token-transfers"]), do: gettext("Token Transfers") + defp tab_name(["internal-transactions"]), do: gettext("Internal Transactions") + defp tab_name(["logs"]), do: gettext("Logs") + defp tab_name(["raw-trace"]), do: gettext("Raw Trace") + defp tab_name(["state"]), do: gettext("State changes") + + defp get_transaction_type_from_token_transfers(token_transfers) do + token_transfers_types = + token_transfers + |> Enum.map(fn token_transfer -> + Chain.get_token_transfer_type(token_transfer) + end) + + burnings_count = + Enum.count(token_transfers_types, fn token_transfers_type -> token_transfers_type == @token_burning_type end) + + mintings_count = + Enum.count(token_transfers_types, fn token_transfers_type -> token_transfers_type == @token_minting_type end) + + creations_count = + Enum.count(token_transfers_types, fn token_transfers_type -> token_transfers_type == @token_creation_type end) + + cond do + Enum.count(token_transfers_types) == burnings_count -> @token_burning_type + Enum.count(token_transfers_types) == mintings_count -> @token_minting_type + Enum.count(token_transfers_types) == creations_count -> @token_creation_type + true -> @token_transfer_type + end + end + + defp show_tenderly_link? do + System.get_env("SHOW_TENDERLY_LINK") == "true" + end + + defp tenderly_chain_path do + System.get_env("TENDERLY_CHAIN_PATH") || "/" + end + + def get_max_length do + string_value = Application.get_env(:block_scout_web, :max_length_to_show_string_without_trimming) + + case Integer.parse(string_value) do + {integer, ""} -> integer + _ -> 2040 + end + end + + def trim(length, string) do + %{show: String.slice(string, 0..length), hide: String.slice(string, (length + 1)..String.length(string))} + end + + defp template_to_string(template) when is_list(template) do + template_to_string(Enum.at(template, 1)) + end + + defp template_to_string(template) when is_tuple(template) do + safe_to_string(template) + end + + # Function decodes revert reason of the transaction + @spec decoded_revert_reason(Transaction.t() | nil) :: binary() | nil + def decoded_revert_reason(transaction) do + revert_reason = get_pure_transaction_revert_reason(transaction) + + case revert_reason do + "0x" <> hex_part -> + proccess_hex_revert_reason(hex_part) + + hex_part -> + proccess_hex_revert_reason(hex_part) + end + end + + # Function converts hex revert reason to the binary + @spec proccess_hex_revert_reason(nil) :: nil + defp proccess_hex_revert_reason(nil), do: nil + + @spec proccess_hex_revert_reason(binary()) :: binary() + defp proccess_hex_revert_reason(hex_revert_reason) do + case Integer.parse(hex_revert_reason, 16) do + {number, ""} -> + :binary.encode_unsigned(number) + + _ -> + hex_revert_reason + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/verified_contracts_view.ex b/apps/block_scout_web/lib/block_scout_web/views/verified_contracts_view.ex new file mode 100644 index 0000000..e541791 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/verified_contracts_view.ex @@ -0,0 +1,14 @@ +defmodule BlockScoutWeb.VerifiedContractsView do + use BlockScoutWeb, :view + + import BlockScoutWeb.AddressView, only: [balance: 1] + alias BlockScoutWeb.WebRouter.Helpers + + def format_current_filter(filter) do + case filter do + "solidity" -> gettext("Solidity") + "vyper" -> gettext("Vyper") + _ -> gettext("All") + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/wei_helpers.ex b/apps/block_scout_web/lib/block_scout_web/views/wei_helpers.ex new file mode 100644 index 0000000..1239f32 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/wei_helpers.ex @@ -0,0 +1,79 @@ +defmodule BlockScoutWeb.WeiHelpers do + @moduledoc """ + Helper functions for interacting with `t:Explorer.Chain.Wei.t/0` values. + """ + + import BlockScoutWeb.Gettext + + alias BlockScoutWeb.CldrHelper + alias Explorer.Chain.Wei + + @valid_units ~w(wei gwei ether)a + + @type format_option :: {:include_unit_label, boolean()} + + @type format_options :: [format_option()] + + @doc """ + Converts a `t:Explorer.Wei.t/0` value to the specified unit including a + translated unit label. + + ## Supported Formatting Options + + The third argument allows for keyword options to be passed for formatting the + converted number. + + * `:include_unit_label` - Boolean (Defaults to `true`). Flag for if the unit + label should be included in the returned string + + ## Examples + + iex> format_wei_value(%Wei{value: Decimal.new(1)}, :wei) + "1 Wei" + + iex> format_wei_value(%Wei{value: Decimal.new(1, 10, 12)}, :gwei) + "10,000 Gwei" + + iex> format_wei_value(%Wei{value: Decimal.new(1, 10, 21)}, :ether) + "10,000 ETH" + + # With formatting options + + iex> format_wei_value( + ...> %Wei{value: Decimal.new(1000500000000000000)}, + ...> :ether + ...> ) + "1.0005 ETH" + + iex> format_wei_value( + ...> %Wei{value: Decimal.new(10)}, + ...> :wei, + ...> include_unit_label: false + ...> ) + "10" + """ + @spec format_wei_value(Wei.t(), Wei.unit(), format_options()) :: String.t() + def format_wei_value(%Wei{} = wei, unit, options \\ []) when unit in @valid_units do + converted_value = + wei + |> Wei.to(unit) + + formatted_value = + if Decimal.compare(converted_value, 1_000_000_000_000) == :gt do + CldrHelper.Number.to_string!(converted_value, format: "0.###E+0") + else + CldrHelper.Number.to_string!(converted_value, format: "#,##0.##################") + end + + if Keyword.get(options, :include_unit_label, true) do + display_unit = display_unit(unit) + "#{formatted_value} #{display_unit}" + else + formatted_value + end + end + + defp display_unit(:wei), do: gettext("Wei") + defp display_unit(:gwei), do: gettext("Gwei") + defp display_unit(:ether), do: Explorer.coin_name() +end diff --git a/apps/block_scout_web/lib/block_scout_web/web_router.ex b/apps/block_scout_web/lib/block_scout_web/web_router.ex new file mode 100644 index 0000000..e03bd10 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/web_router.ex @@ -0,0 +1,499 @@ +defmodule BlockScoutWeb.WebRouter do + @moduledoc """ + Router for web app + """ + use BlockScoutWeb, :router + require Ueberauth + + alias BlockScoutWeb.Plug.CheckAccountWeb + + pipeline :browser do + plug(:accepts, ["html"]) + plug(:fetch_session) + plug(:fetch_flash) + plug(:protect_from_forgery) + plug(BlockScoutWeb.CSPHeader) + plug(BlockScoutWeb.ChecksumAddress) + end + + pipeline :account do + plug(:accepts, ["html"]) + plug(:fetch_session) + plug(:fetch_flash) + plug(CheckAccountWeb) + plug(:protect_from_forgery) + plug(BlockScoutWeb.CSPHeader) + plug(BlockScoutWeb.ChecksumAddress) + end + + if Mix.env() == :dev do + forward("/sent_emails", Bamboo.SentEmailViewerPlug) + end + + scope "/auth", BlockScoutWeb do + pipe_through(:account) + + get("/profile", Account.AuthController, :profile) + get("/logout", Account.AuthController, :logout) + get("/:provider", Account.AuthController, :request) + get("/:provider/callback", Account.AuthController, :callback) + end + + scope "/account", BlockScoutWeb do + pipe_through(:account) + + resources("/tag_address", Account.TagAddressController, + only: [:index, :new, :create, :delete], + as: :tag_address + ) + + resources("/tag_transaction", Account.TagTransactionController, + only: [:index, :new, :create, :delete], + as: :tag_transaction + ) + + resources("/watchlist", Account.WatchlistController, + only: [:show], + singleton: true, + as: :watchlist + ) + + resources("/watchlist_address", Account.WatchlistAddressController, + only: [:new, :create, :edit, :update, :delete], + as: :watchlist_address + ) + + resources("/api_key", Account.ApiKeyController, + only: [:new, :create, :edit, :update, :delete, :index], + as: :api_key + ) + + resources("/custom_abi", Account.CustomABIController, + only: [:new, :create, :edit, :update, :delete, :index], + as: :custom_abi + ) + + resources("/public_tags_request", Account.PublicTagsRequestController, + only: [:new, :create, :edit, :update, :delete, :index], + as: :public_tags_request + ) + end + + # Disallows Iframes (write routes) + scope "/", BlockScoutWeb do + pipe_through(:browser) + end + + # Allows Iframes (read-only routes) + scope "/", BlockScoutWeb do + pipe_through([:browser, BlockScoutWeb.Plug.AllowIframe]) + + resources("/", ChainController, only: [:show], singleton: true, as: :chain) + + resources("/market-history-chart", Chain.MarketHistoryChartController, + only: [:show], + singleton: true + ) + + resources("/transaction-history-chart", Chain.TransactionHistoryChartController, + only: [:show], + singleton: true + ) + + resources "/block", BlockController, only: [:show], param: "hash_or_number" do + resources("/transactions", BlockTransactionController, only: [:index], as: :transaction) + end + + resources("/blocks", BlockController, as: :blocks, only: [:index]) + + resources "/blocks", BlockController, + as: :block_secondary, + only: [:show], + param: "hash_or_number" do + resources("/transactions", BlockTransactionController, only: [:index], as: :transaction) + end + + get("/reorgs", BlockController, :reorg, as: :reorg) + + get("/uncles", BlockController, :uncle, as: :uncle) + + resources("/pending-transactions", PendingTransactionController, only: [:index]) + + resources("/recent-transactions", RecentTransactionsController, only: [:index]) + + resources("/verified-contracts", VerifiedContractsController, only: [:index]) + + get("/txs", TransactionController, :index) + + resources "/tx", TransactionController, only: [:show] do + resources( + "/internal-transactions", + TransactionInternalTransactionController, + only: [:index], + as: :internal_transaction + ) + + resources( + "/raw-trace", + TransactionRawTraceController, + only: [:index], + as: :raw_trace + ) + + resources("/logs", TransactionLogController, only: [:index], as: :log) + + resources("/token-transfers", TransactionTokenTransferController, + only: [:index], + as: :token_transfer + ) + + resources("/state", TransactionStateController, + only: [:index], + as: :state + ) + end + + resources("/accounts", AddressController, only: [:index]) + + resources("/tokens", TokensController, only: [:index]) + + resources "/address", AddressController, only: [:show] do + resources("/transactions", AddressTransactionController, only: [:index], as: :transaction) + + resources( + "/internal-transactions", + AddressInternalTransactionController, + only: [:index], + as: :internal_transaction + ) + + resources( + "/validations", + AddressValidationController, + only: [:index], + as: :validation + ) + + resources( + "/contracts", + AddressContractController, + only: [:index], + as: :contract + ) + + resources( + "/decompiled-contracts", + AddressDecompiledContractController, + only: [:index], + as: :decompiled_contract + ) + + resources( + "/logs", + AddressLogsController, + only: [:index], + as: :logs + ) + + resources( + "/contract_verifications", + AddressContractVerificationController, + only: [:new], + as: :verify_contract + ) + + resources( + "/verify-via-flattened-code", + AddressContractVerificationViaFlattenedCodeController, + only: [:new], + as: :verify_contract_via_flattened_code + ) + + resources( + "/verify-via-metadata-json", + AddressContractVerificationViaJsonController, + only: [:new], + as: :verify_contract_via_json + ) + + resources( + "/verify-via-standard-json-input", + AddressContractVerificationViaStandardJsonInputController, + only: [:new], + as: :verify_contract_via_standard_json_input + ) + + resources( + "/verify-via-multi-part-files", + AddressContractVerificationViaMultiPartFilesController, + only: [:new], + as: :verify_contract_via_multi_part_files + ) + + resources( + "/verify-vyper-contract", + AddressContractVerificationVyperController, + only: [:new], + as: :verify_vyper_contract + ) + + resources( + "/read-contract", + AddressReadContractController, + only: [:index, :show], + as: :read_contract + ) + + resources( + "/read-proxy", + AddressReadProxyController, + only: [:index, :show], + as: :read_proxy + ) + + resources( + "/write-contract", + AddressWriteContractController, + only: [:index, :show], + as: :write_contract + ) + + resources( + "/write-proxy", + AddressWriteProxyController, + only: [:index, :show], + as: :write_proxy + ) + + resources( + "/token-transfers", + AddressTokenTransferController, + only: [:index], + as: :token_transfers + ) + + resources("/tokens", AddressTokenController, only: [:index], as: :token) do + resources( + "/token-transfers", + AddressTokenTransferController, + only: [:index], + as: :transfers + ) + end + + resources( + "/token-balances", + AddressTokenBalanceController, + only: [:index], + as: :token_balance + ) + + resources( + "/coin-balances", + AddressCoinBalanceController, + only: [:index], + as: :coin_balance + ) + + resources( + "/coin-balances/by-day", + AddressCoinBalanceByDayController, + only: [:index], + as: :coin_balance_by_day + ) + end + + resources "/token", Tokens.TokenController, only: [:show], as: :token do + resources( + "/token-transfers", + Tokens.TransferController, + only: [:index], + as: :transfer + ) + + resources( + "/read-contract", + Tokens.ContractController, + only: [:index], + as: :read_contract + ) + + resources( + "/write-contract", + Tokens.ContractController, + only: [:index], + as: :write_contract + ) + + resources( + "/read-proxy", + Tokens.ContractController, + only: [:index], + as: :read_proxy + ) + + resources( + "/write-proxy", + Tokens.ContractController, + only: [:index], + as: :write_proxy + ) + + resources( + "/token-holders", + Tokens.HolderController, + only: [:index], + as: :holder + ) + + resources( + "/inventory", + Tokens.InventoryController, + only: [:index], + as: :inventory + ) + + resources( + "/instance", + Tokens.InstanceController, + only: [:show], + as: :instance + ) do + resources( + "/token-transfers", + Tokens.Instance.TransferController, + only: [:index], + as: :transfer + ) + + resources( + "/metadata", + Tokens.Instance.MetadataController, + only: [:index], + as: :metadata + ) + + resources( + "/token-holders", + Tokens.Instance.HolderController, + only: [:index], + as: :holder + ) + end + end + + resources "/tokens", Tokens.TokenController, only: [:show], as: :token_secondary do + resources( + "/token-transfers", + Tokens.TransferController, + only: [:index], + as: :transfer + ) + + resources( + "/read-contract", + Tokens.ContractController, + only: [:index], + as: :read_contract + ) + + resources( + "/write-contract", + Tokens.ContractController, + only: [:index], + as: :write_contract + ) + + resources( + "/read-proxy", + Tokens.ContractController, + only: [:index], + as: :read_proxy + ) + + resources( + "/write-proxy", + Tokens.ContractController, + only: [:index], + as: :write_proxy + ) + + resources( + "/token-holders", + Tokens.HolderController, + only: [:index], + as: :holder + ) + + resources( + "/inventory", + Tokens.InventoryController, + only: [:index], + as: :inventory + ) + + resources( + "/instance", + Tokens.InstanceController, + only: [:show], + as: :instance + ) do + resources( + "/token-transfers", + Tokens.Instance.TransferController, + only: [:index], + as: :transfer + ) + + resources( + "/metadata", + Tokens.Instance.MetadataController, + only: [:index], + as: :metadata + ) + + resources( + "/token-holders", + Tokens.Instance.HolderController, + only: [:index], + as: :holder + ) + end + end + + resources( + "/smart-contracts", + SmartContractController, + only: [:index, :show], + as: :smart_contract + ) + + get("/address-counters", AddressController, :address_counters) + + get("/search", ChainController, :search) + + get("/search-logs", AddressLogsController, :search_logs) + + get("/search-results", SearchController, :search_results) + + get("/search-verified-contracts", VerifiedContractsController, :search_verified_contracts) + + get("/csv-export", CsvExportController, :index) + + get("/transactions-csv", AddressTransactionController, :transactions_csv) + + get("/token-autocomplete", ChainController, :token_autocomplete) + + get("/token-transfers-csv", AddressTransactionController, :token_transfers_csv) + + get("/internal-transactions-csv", AddressTransactionController, :internal_transactions_csv) + + get("/logs-csv", AddressTransactionController, :logs_csv) + + get("/chain-blocks", ChainController, :chain_blocks, as: :chain_blocks) + + get("/token-counters", Tokens.TokenController, :token_counters) + + get("/*path", PageNotFoundController, :index) + end +end diff --git a/apps/block_scout_web/lib/phoenix/html/safe.ex b/apps/block_scout_web/lib/phoenix/html/safe.ex new file mode 100644 index 0000000..cddc3d5 --- /dev/null +++ b/apps/block_scout_web/lib/phoenix/html/safe.ex @@ -0,0 +1,32 @@ +alias Explorer.Chain +alias Explorer.Chain.{Address, Block, Data, Hash, Transaction} + +defimpl Phoenix.HTML.Safe, for: Address do + def to_iodata(%@for{} = address) do + @for.checksum(address, true) + end +end + +defimpl Phoenix.HTML.Safe, for: Transaction do + def to_iodata(%@for{hash: hash}) do + @protocol.to_iodata(hash) + end +end + +defimpl Phoenix.HTML.Safe, for: Block do + def to_iodata(%@for{number: number}) do + @protocol.to_iodata(number) + end +end + +defimpl Phoenix.HTML.Safe, for: Data do + def to_iodata(data) do + Chain.data_to_iodata(data) + end +end + +defimpl Phoenix.HTML.Safe, for: Hash do + def to_iodata(hash) do + Chain.hash_to_iodata(hash) + end +end diff --git a/apps/block_scout_web/lib/phoenix/param.ex b/apps/block_scout_web/lib/phoenix/param.ex new file mode 100644 index 0000000..e22fcac --- /dev/null +++ b/apps/block_scout_web/lib/phoenix/param.ex @@ -0,0 +1,29 @@ +alias Explorer.Chain.{Address, Block, Hash, Transaction} + +defimpl Phoenix.Param, for: Transaction do + def to_param(%@for{hash: hash}) do + @protocol.to_param(hash) + end +end + +defimpl Phoenix.Param, for: Address do + def to_param(%@for{} = address) do + @for.checksum(address) + end +end + +defimpl Phoenix.Param, for: Block do + def to_param(%@for{consensus: true, number: number}) do + to_string(number) + end + + def to_param(%@for{consensus: false, hash: hash}) do + to_string(hash) + end +end + +defimpl Phoenix.Param, for: Hash do + def to_param(hash) do + to_string(hash) + end +end diff --git a/apps/block_scout_web/mix.exs b/apps/block_scout_web/mix.exs new file mode 100644 index 0000000..a166d2a --- /dev/null +++ b/apps/block_scout_web/mix.exs @@ -0,0 +1,167 @@ +defmodule BlockScoutWeb.Mixfile do + use Mix.Project + + def project do + [ + aliases: aliases(), + app: :block_scout_web, + build_path: "../../_build", + config_path: "../../config/config.exs", + deps: deps(), + deps_path: "../../deps", + description: "Web interface for BlockScout.", + dialyzer: [ + plt_add_deps: :transitive, + ignore_warnings: "../../.dialyzer-ignore" + ], + elixir: "~> 1.13", + elixirc_paths: elixirc_paths(Mix.env()), + lockfile: "../../mix.lock", + package: package(), + preferred_cli_env: [ + credo: :test, + dialyzer: :test + ], + start_permanent: Mix.env() == :prod, + version: "4.1.8" + ] + end + + # Configuration for the OTP application. + # + # Type `mix help compile.app` for more information. + def application do + [ + mod: {BlockScoutWeb.Application, []}, + extra_applications: extra_applications() + ] + end + + # Specifies which paths to compile per environment. + defp elixirc_paths(:test), do: ["test/support", "test/block_scout_web/features/pages"] ++ elixirc_paths() + defp elixirc_paths(_), do: elixirc_paths() + defp elixirc_paths, do: ["lib"] + + defp extra_applications, + do: [ + :ueberauth_auth0, + :logger, + :runtime_tools + ] + + # Specifies your project dependencies. + # + # Type `mix help deps` for examples and options. + defp deps do + [ + # GraphQL toolkit + {:absinthe, "~> 1.5"}, + # Integrates Absinthe subscriptions with Phoenix + {:absinthe_phoenix, "~> 2.0.0"}, + # Plug support for Absinthe + {:absinthe_plug, git: "https://github.com/blockscout/absinthe_plug.git", tag: "1.5.3", override: true}, + # Absinthe support for the Relay framework + {:absinthe_relay, "~> 1.5"}, + {:bypass, "~> 2.1", only: :test}, + # To add (CORS)(https://www.w3.org/TR/cors/) + {:cors_plug, "~> 3.0"}, + {:credo, "~> 1.5", only: :test, runtime: false}, + # For Absinthe to load data in batches + {:dataloader, "~> 1.0.0"}, + {:dialyxir, "~> 1.1", only: [:dev, :test], runtime: false}, + # Need until https://github.com/absinthe-graphql/absinthe_relay/pull/125 is released, then can be removed + # The current `absinthe_relay` is compatible though as shown from that PR + {:ecto, "~> 3.3", override: true}, + {:ex_cldr, "~> 2.7"}, + {:ex_cldr_numbers, "~> 2.6"}, + {:ex_cldr_units, "~> 3.13"}, + {:cldr_utils, "~> 2.3"}, + {:ex_machina, "~> 2.1", only: [:test]}, + {:explorer, in_umbrella: true}, + {:exvcr, "~> 0.10", only: :test}, + {:file_info, "~> 0.0.4"}, + # HTML CSS selectors for Phoenix controller tests + {:floki, "~> 0.31"}, + {:flow, "~> 1.2"}, + {:gettext, "~> 0.20.0"}, + {:hammer, "~> 6.0"}, + {:httpoison, "~> 1.6"}, + {:indexer, in_umbrella: true, runtime: false}, + # JSON parser and generator + {:jason, "~> 1.3"}, + {:junit_formatter, ">= 0.0.0", only: [:test], runtime: false}, + # Log errors and application output to separate files + {:logger_file_backend, "~> 0.0.10"}, + {:math, "~> 0.7.0"}, + {:mock, "~> 0.3.0", only: [:test], runtime: false}, + {:number, "~> 1.0.1"}, + {:phoenix, "== 1.5.13"}, + {:phoenix_ecto, "~> 4.1"}, + {:phoenix_html, "== 3.0.4"}, + {:phoenix_live_reload, "~> 1.2", only: [:dev]}, + {:phoenix_pubsub, "~> 2.0"}, + {:prometheus_ex, git: "https://github.com/lanodan/prometheus.ex", branch: "fix/elixir-1.14", override: true}, + # use `:cowboy` for WebServer with `:plug` + {:plug_cowboy, "~> 2.2"}, + # Waiting for the Pretty Print to be implemented at the Jason lib + # https://github.com/michalmuskala/jason/issues/15 + {:poison, "~> 4.0.1"}, + {:postgrex, ">= 0.0.0"}, + # For compatibility with `prometheus_process_collector`, which hasn't been updated yet + {:prometheus, "~> 4.0", override: true}, + # Gather methods for Phoenix requests + {:prometheus_phoenix, "~> 1.2"}, + # Expose metrics from URL Prometheus server can scrape + {:prometheus_plugs, "~> 1.1"}, + # OS process metrics for Prometheus + {:prometheus_process_collector, "~> 1.3"}, + {:remote_ip, "~> 1.0"}, + {:qrcode, "~> 0.1.0"}, + {:sobelow, ">= 0.7.0", only: [:dev, :test], runtime: false}, + # Tracing + {:spandex, "~> 3.0"}, + # `:spandex` integration with Datadog + {:spandex_datadog, "~> 1.0"}, + # `:spandex` tracing of `:phoenix` + {:spandex_phoenix, "~> 1.0"}, + {:timex, "~> 3.7.1"}, + {:wallaby, "~> 0.30", only: :test, runtime: false}, + # `:cowboy` `~> 2.0` and Phoenix 1.4 compatibility + {:websocket_client, "~> 1.3"}, + {:wobserver, "~> 0.2.0", github: "poanetwork/wobserver", branch: "support-https"}, + {:ex_json_schema, "~> 0.9.1"}, + {:ueberauth, "~> 0.7"}, + {:ueberauth_auth0, "~> 2.0"}, + {:bureaucrat, "~> 0.2.9", only: :test} + ] + end + + # Aliases are shortcuts or tasks specific to the current project. + # For example, to create, migrate and run the seeds file at once: + # + # $ mix ecto.setup + # + # See the documentation for `Mix` for more info on aliases. + defp aliases do + [ + compile: "compile --warnings-as-errors", + "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], + "ecto.reset": ["ecto.drop", "ecto.setup"], + test: [ + "ecto.create --quiet", + "ecto.migrate", + # to match behavior of `mix test` from project root, which needs to not start applications for `indexer` to + # prevent its supervision tree from starting, which is undesirable in test + "test --no-start" + ] + ] + end + + defp package do + [ + maintainers: ["Blockscout"], + licenses: ["GPL 3.0"], + links: %{"GitHub" => "https://github.com/blockscout/blockscout"} + ] + end +end diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot new file mode 100644 index 0000000..c2cd456 --- /dev/null +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -0,0 +1,3426 @@ +#: lib/block_scout_web/views/address_token_balance_view.ex:10 +#, elixir-autogen, elixir-format +msgid "%{count} token" +msgid_plural "%{count} tokens" +msgstr[0] "" +msgstr[1] "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:29 +#, elixir-autogen, elixir-format +msgid "%{count} transaction" +msgid_plural "%{count} transactions" +msgstr[0] "" +msgstr[1] "" + +#: lib/block_scout_web/templates/common_components/_minimal_proxy_pattern.html.eex:9 +#, elixir-autogen, elixir-format +msgid " - minimal bytecode implementation that delegates all calls to a known address" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:14 +#, elixir-autogen, elixir-format +msgid " is recommended." +msgstr "" + +#: lib/block_scout_web/templates/address/_metatags.html.eex:3 +#, elixir-autogen, elixir-format +msgid "%{address} - %{subnetwork} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:12 +#, elixir-autogen, elixir-format +msgid "%{block_type} Details" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:55 +#, elixir-autogen, elixir-format +msgid "%{block_type} Height" +msgstr "" + +#: lib/block_scout_web/templates/block/index.html.eex:7 +#, elixir-autogen, elixir-format +msgid "%{block_type}s" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:85 +#, elixir-autogen, elixir-format +msgid "%{count} Transaction" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:87 +#: lib/block_scout_web/templates/chain/_block.html.eex:11 +#, elixir-autogen, elixir-format +msgid "%{count} Transactions" +msgstr "" + +#: lib/block_scout_web/templates/chain/_metatags.html.eex:2 +#, elixir-autogen, elixir-format +msgid "%{subnetwork} %{network} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/layout/_default_title.html.eex:2 +#, elixir-autogen, elixir-format +msgid "%{subnetwork} Explorer - BlockScout" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:350 +#, elixir-autogen, elixir-format +msgid "(Awaiting internal transactions for status)" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:59 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:70 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:82 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:104 +#, elixir-autogen, elixir-format +msgid "(query)" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:230 +#, elixir-autogen, elixir-format +msgid "- We're indexing this chain right now. Some of the counts may be inaccurate." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:195 +#, elixir-autogen, elixir-format +msgid "64-bit hash of value verifying proof-of-work (note: null for POA chains)." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:97 +#: lib/block_scout_web/templates/transaction_state/_state_change.html.eex:23 +#, elixir-autogen, elixir-format +msgid "A block producer who successfully included the block onto the blockchain." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:73 +#, elixir-autogen, elixir-format +msgid "A string with the name of the action to be invoked." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:62 +#, elixir-autogen, elixir-format +msgid "A string with the name of the module to be invoked." +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:24 +#, elixir-autogen, elixir-format +msgid "ABI" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_constructor_args.html.eex:3 +#, elixir-autogen, elixir-format +msgid "ABI-encoded Constructor Arguments (if required by the contract)" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/index.html.eex:4 +#, elixir-autogen, elixir-format +msgid "API Documentation" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_metatags.html.eex:4 +#, elixir-autogen, elixir-format +msgid "API endpoints for the %{subnetwork}" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_metatags.html.eex:2 +#, elixir-autogen, elixir-format +msgid "API for the %{subnetwork} - BlockScout" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:7 +#: lib/block_scout_web/templates/account/api_key/form.html.eex:13 +#: lib/block_scout_web/templates/account/api_key/form.html.eex:14 +#: lib/block_scout_web/templates/account/api_key/index.html.eex:29 +#, elixir-autogen, elixir-format +msgid "API key" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/index.html.eex:7 +#: lib/block_scout_web/templates/account/common/_nav.html.eex:16 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:17 +#, elixir-autogen, elixir-format +msgid "API keys" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:100 +#, elixir-autogen, elixir-format +msgid "APIs" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:24 +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:24 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:69 +#, elixir-autogen, elixir-format +msgid "Action" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:25 +#, elixir-autogen, elixir-format +msgid "Actions" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:425 +#, elixir-autogen, elixir-format +msgid "Actual gas amount used by the transaction." +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:7 +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:8 +#: lib/block_scout_web/templates/layout/_add_chain_to_mm.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Add" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/index.html.eex:44 +#, elixir-autogen, elixir-format +msgid "Add API key" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:82 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:76 +#, elixir-autogen, elixir-format +msgid "Add Contract Libraries" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:44 +#, elixir-autogen, elixir-format +msgid "Add Custom ABI" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:93 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:87 +#, elixir-autogen, elixir-format +msgid "Add Library" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:38 +#, elixir-autogen, elixir-format +msgid "Add address" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:7 +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:37 +#, elixir-autogen, elixir-format +msgid "Add address tag" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Add address to the Watch list" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:7 +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:37 +#, elixir-autogen, elixir-format +msgid "Add transaction tag" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:11 +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:23 +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:23 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:12 +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_address.html.eex:4 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20 +#: lib/block_scout_web/templates/transaction_state/index.html.eex:29 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:54 +#: lib/block_scout_web/views/address_view.ex:107 +#, elixir-autogen, elixir-format +msgid "Address" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:217 +#, elixir-autogen, elixir-format +msgid "Address (external or contract) receiving the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:199 +#, elixir-autogen, elixir-format +msgid "Address (external or contract) sending the transaction." +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:10 +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:7 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Address Tags" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:150 +#, elixir-autogen, elixir-format +msgid "Address balance in" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:51 +#, elixir-autogen, elixir-format +msgid "Address of the token contract" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/address_field.html.eex:2 +#, elixir-autogen, elixir-format +msgid "Address*" +msgstr "" + +#: lib/block_scout_web/templates/address/index.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Addresses" +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:26 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:28 +#: lib/block_scout_web/templates/address_transaction/index.html.eex:22 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:82 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:20 +#: lib/block_scout_web/views/address_internal_transaction_view.ex:11 +#: lib/block_scout_web/views/address_token_transfer_view.ex:11 +#: lib/block_scout_web/views/address_transaction_view.ex:11 +#: lib/block_scout_web/views/verified_contracts_view.ex:11 +#, elixir-autogen, elixir-format +msgid "All" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13 +#, elixir-autogen, elixir-format +msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:27 +#, elixir-autogen, elixir-format +msgid "All metadata displayed below is from that contract. In order to verify current contract, click" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:175 +#, elixir-autogen, elixir-format +msgid "All tokens in the account and total value." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:411 +#, elixir-autogen, elixir-format +msgid "Amount of" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:236 +#, elixir-autogen, elixir-format +msgid "Amount of distributed reward. Miners receive a static block reward + Tx fees + uncle fees." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Anything not in this list is not supported. Click on the method to be taken to the documentation for that method, and check the notes section for any potential differences." +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:128 +#, elixir-autogen, elixir-format +msgid "Apps" +msgstr "" + +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:21 +#, elixir-autogen, elixir-format +msgid "Average" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:100 +#, elixir-autogen, elixir-format +msgid "Average block time" +msgstr "" + +#: lib/block_scout_web/templates/page_not_found/index.html.eex:9 +#: lib/block_scout_web/templates/transaction/not_found.html.eex:30 +#, elixir-autogen, elixir-format +msgid "Back Home" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:25 +#, elixir-autogen, elixir-format +msgid "Back to API keys (Cancel)" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Back to Address Tags (Cancel)" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:30 +#, elixir-autogen, elixir-format +msgid "Back to Custom ABI (Cancel)" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Back to Transaction Tags (Cancel)" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:81 +#, elixir-autogen, elixir-format +msgid "Back to Watch list (Cancel)" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:24 +#: lib/block_scout_web/templates/address/overview.html.eex:151 +#: lib/block_scout_web/templates/address_token/overview.html.eex:51 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:57 +#, elixir-autogen, elixir-format +msgid "Balance" +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Balances" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:207 +#, elixir-autogen, elixir-format +msgid "Base Fee per Gas" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:5 +#: lib/block_scout_web/templates/api_docs/index.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Base URL:" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:446 +#, elixir-autogen, elixir-format +msgid "Binary data included with the transaction. See input / logs below for additional info." +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/_coin_balances.html.eex:8 +#: lib/block_scout_web/templates/block/overview.html.eex:29 +#: lib/block_scout_web/templates/transaction/overview.html.eex:158 +#, elixir-autogen, elixir-format +msgid "Block" +msgstr "" + +#: lib/block_scout_web/templates/block/_link.html.eex:2 +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:32 +#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:43 +#, elixir-autogen, elixir-format +msgid "Block #%{number}" +msgstr "" + +#: lib/block_scout_web/templates/block/_metatags.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Block %{block_number} - %{subnetwork} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/block_transaction/404.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Block Details" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:53 +#, elixir-autogen, elixir-format +msgid "Block Height" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:43 +#, elixir-autogen, elixir-format +msgid "Block Mined, awaiting import..." +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:34 +#, elixir-autogen, elixir-format +msgid "Block Pending" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:158 +#, elixir-autogen, elixir-format +msgid "Block difficulty for miner, used to calibrate block generation time (Note: constant in POA based networks)." +msgstr "" + +#: lib/block_scout_web/views/block_transaction_view.ex:15 +#, elixir-autogen, elixir-format +msgid "Block not found, please try again later." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:157 +#, elixir-autogen, elixir-format +msgid "Block number containing the transaction." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:258 +#, elixir-autogen, elixir-format +msgid "Block number in which the address was updated." +msgstr "" + +#: lib/block_scout_web/templates/chain/_metatags.html.eex:4 +#, elixir-autogen, elixir-format +msgid "BlockScout provides analytics data, API, and Smart Contract tools for the %{subnetwork}" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:153 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:34 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:38 +#, elixir-autogen, elixir-format +msgid "Blocks" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:42 +#, elixir-autogen, elixir-format +msgid "Blocks Indexed" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:48 +#: lib/block_scout_web/templates/address/overview.html.eex:276 +#: lib/block_scout_web/templates/address_validation/index.html.eex:11 +#: lib/block_scout_web/views/address_view.ex:374 +#, elixir-autogen, elixir-format +msgid "Blocks Validated" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks." +msgstr "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:64 +#: lib/block_scout_web/templates/block/overview.html.eex:216 +#, elixir-autogen, elixir-format +msgid "Burnt Fees" +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:65 +#, elixir-autogen, elixir-format +msgid "CRC Worth" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_csv_export_button.html.eex:2 +#, elixir-autogen, elixir-format +msgid "CSV" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:10 +#: lib/block_scout_web/views/internal_transaction_view.ex:21 +#, elixir-autogen, elixir-format +msgid "Call" +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:22 +#, elixir-autogen, elixir-format +msgid "Call Code" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:62 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:120 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:111 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:41 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:107 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:55 +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:51 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54 +#, elixir-autogen, elixir-format +msgid "Cancel" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:41 +#, elixir-autogen, elixir-format +msgid "Chat (#blockscout)" +msgstr "" + +#: lib/block_scout_web/views/block_view.ex:65 +#, elixir-autogen, elixir-format +msgid "Chore Reward" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:137 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:106 +#, elixir-autogen, elixir-format +msgid "Clear" +msgstr "" + +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37 +#: lib/block_scout_web/templates/common_components/_modal_qr_code.html.eex:6 +#: lib/block_scout_web/templates/common_components/_modal_qr_code.html.eex:14 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:84 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:92 +#, elixir-autogen, elixir-format +msgid "Close" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:58 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:165 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149 +#: lib/block_scout_web/views/address_view.ex:367 +#, elixir-autogen, elixir-format +msgid "Code" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:34 +#: lib/block_scout_web/views/address_view.ex:373 +#, elixir-autogen, elixir-format +msgid "Coin Balance History" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55 +#, elixir-autogen, elixir-format +msgid "Collapse" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Company name" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:32 +#, elixir-autogen, elixir-format +msgid "Company website" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_compiler_field.html.eex:3 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:63 +#, elixir-autogen, elixir-format +msgid "Compiler" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:65 +#, elixir-autogen, elixir-format +msgid "Compiler version" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:343 +#, elixir-autogen, elixir-format +msgid "Confirmed" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:124 +#, elixir-autogen, elixir-format +msgid "Confirmed by " +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:190 +#, elixir-autogen, elixir-format +msgid "Confirmed within" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:2 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:6 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:2 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:4 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:6 +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:4 +#: lib/block_scout_web/templates/tokens/holder/index.html.eex:16 +#, elixir-autogen, elixir-format +msgid "Connection Lost" +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:12 +#: lib/block_scout_web/templates/block/index.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Connection Lost, click to load newer blocks" +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Connection Lost, click to load newer internal transactions" +msgstr "" + +#: lib/block_scout_web/templates/address_transaction/index.html.eex:11 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:16 +#: lib/block_scout_web/templates/transaction/index.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Connection Lost, click to load newer transactions" +msgstr "" + +#: lib/block_scout_web/templates/address_validation/index.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Connection Lost, click to load newer validations" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:90 +#, elixir-autogen, elixir-format +msgid "Constructor Arguments" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:52 +#: lib/block_scout_web/templates/transaction/overview.html.eex:227 +#, elixir-autogen, elixir-format +msgid "Contract" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:122 +#, elixir-autogen, elixir-format +msgid "Contract ABI" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:18 +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:29 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex:3 +#: lib/block_scout_web/views/address_view.ex:105 +#, elixir-autogen, elixir-format +msgid "Contract Address" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:16 +#: lib/block_scout_web/views/address_view.ex:45 +#: lib/block_scout_web/views/address_view.ex:79 +#, elixir-autogen, elixir-format +msgid "Contract Address Pending" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:458 +#, elixir-autogen, elixir-format +msgid "Contract Call" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:455 +#, elixir-autogen, elixir-format +msgid "Contract Creation" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:138 +#: lib/block_scout_web/templates/address_contract/index.html.eex:153 +#, elixir-autogen, elixir-format +msgid "Contract Creation Code" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:86 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:80 +#, elixir-autogen, elixir-format +msgid "Contract Libraries" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:75 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Contract Name" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:25 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:57 +#, elixir-autogen, elixir-format +msgid "Contract name:" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:100 +#, elixir-autogen, elixir-format +msgid "Contract source code" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:144 +#, elixir-autogen, elixir-format +msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified." +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:40 +#, elixir-autogen, elixir-format +msgid "Contribute" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:124 +#, elixir-autogen, elixir-format +msgid "Copy ABI" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/row.html.eex:6 +#: lib/block_scout_web/templates/account/api_key/row.html.eex:6 +#, elixir-autogen, elixir-format +msgid "Copy API key" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/row.html.eex:8 +#: lib/block_scout_web/templates/account/tag_address/row.html.eex:8 +#: lib/block_scout_web/templates/account/tag_transaction/row.html.eex:11 +#: lib/block_scout_web/templates/account/tag_transaction/row.html.eex:11 +#: lib/block_scout_web/templates/account/watchlist_address/row.html.eex:7 +#: lib/block_scout_web/templates/address/overview.html.eex:38 +#: lib/block_scout_web/templates/address/overview.html.eex:39 +#: lib/block_scout_web/templates/block/overview.html.eex:104 +#: lib/block_scout_web/templates/block/overview.html.eex:105 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:43 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:44 +#, elixir-autogen, elixir-format +msgid "Copy Address" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/row.html.eex:6 +#: lib/block_scout_web/templates/account/custom_abi/row.html.eex:6 +#, elixir-autogen, elixir-format +msgid "Copy Contract Address" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:140 +#: lib/block_scout_web/templates/address_contract/index.html.eex:156 +#, elixir-autogen, elixir-format +msgid "Copy Contract Creation Code" +msgstr "" + +#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:19 +#, elixir-autogen, elixir-format +msgid "Copy Decompiled Contract Code" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:177 +#: lib/block_scout_web/templates/address_contract/index.html.eex:187 +#, elixir-autogen, elixir-format +msgid "Copy Deployed ByteCode" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/row.html.eex:7 +#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:17 +#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:18 +#: lib/block_scout_web/templates/transaction/overview.html.eex:207 +#: lib/block_scout_web/templates/transaction/overview.html.eex:208 +#, elixir-autogen, elixir-format +msgid "Copy From Address" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:129 +#: lib/block_scout_web/templates/block/overview.html.eex:130 +#, elixir-autogen, elixir-format +msgid "Copy Hash" +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Copy Metadata" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:149 +#: lib/block_scout_web/templates/block/overview.html.eex:150 +#, elixir-autogen, elixir-format +msgid "Copy Parent Hash" +msgstr "" + +#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Copy Raw Trace" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:102 +#: lib/block_scout_web/templates/address_contract/index.html.eex:113 +#, elixir-autogen, elixir-format +msgid "Copy Source Code" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:34 +#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:35 +#: lib/block_scout_web/templates/transaction/overview.html.eex:234 +#: lib/block_scout_web/templates/transaction/overview.html.eex:235 +#: lib/block_scout_web/templates/transaction/overview.html.eex:242 +#: lib/block_scout_web/templates/transaction/overview.html.eex:243 +#, elixir-autogen, elixir-format +msgid "Copy To Address" +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:32 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:33 +#, elixir-autogen, elixir-format +msgid "Copy Token ID" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:87 +#, elixir-autogen, elixir-format +msgid "Copy Transaction Hash" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:88 +#, elixir-autogen, elixir-format +msgid "Copy Txn Hash" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:472 +#, elixir-autogen, elixir-format +msgid "Copy Txn Hex Input" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:478 +#, elixir-autogen, elixir-format +msgid "Copy Txn UTF-8 Input" +msgstr "" + +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:20 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:41 +#: lib/block_scout_web/templates/transaction/overview.html.eex:471 +#: lib/block_scout_web/templates/transaction/overview.html.eex:477 +#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Copy Value" +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:25 +#, elixir-autogen, elixir-format +msgid "Create" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:12 +#, elixir-autogen, elixir-format +msgid "Create a Custom ABI to interact with contracts." +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/index.html.eex:12 +#, elixir-autogen, elixir-format +msgid "Create an API key to use with your RPC и EthRPC API requests." +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:26 +#, elixir-autogen, elixir-format +msgid "Create2" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:102 +#, elixir-autogen, elixir-format +msgid "Creator" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:146 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:116 +#, elixir-autogen, elixir-format +msgid "Curl" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:97 +#, elixir-autogen, elixir-format +msgid "Current transaction state: Success, Failed (Error), or Pending (In Process)" +msgstr "" + +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:20 +#: lib/block_scout_web/templates/address_write_contract/index.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Custom" +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:19 +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:8 +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:7 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Custom ABI" +msgstr "" + +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:25 +#: lib/block_scout_web/templates/address_write_contract/index.html.eex:23 +#, elixir-autogen, elixir-format +msgid "Custom ABI from account" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:69 +#, elixir-autogen, elixir-format +msgid "Daily Transactions" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:101 +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:23 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:121 +#, elixir-autogen, elixir-format +msgid "Data" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:70 +#, elixir-autogen, elixir-format +msgid "Date & time at which block was produced." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:176 +#, elixir-autogen, elixir-format +msgid "Date & time of transaction inclusion, including length of time for confirmation." +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:52 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:131 +#, elixir-autogen, elixir-format +msgid "Decimals" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:32 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:53 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:34 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:57 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:73 +#, elixir-autogen, elixir-format +msgid "Decoded" +msgstr "" + +#: lib/block_scout_web/views/address_view.ex:368 +#, elixir-autogen, elixir-format +msgid "Decompiled Code" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:75 +#, elixir-autogen, elixir-format +msgid "Decompiled code" +msgstr "" + +#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:17 +#, elixir-autogen, elixir-format +msgid "Decompiled contract code" +msgstr "" + +#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Decompiler version" +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:23 +#, elixir-autogen, elixir-format +msgid "Delegate Call" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:175 +#: lib/block_scout_web/templates/address_contract/index.html.eex:183 +#, elixir-autogen, elixir-format +msgid "Deployed ByteCode" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:53 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:188 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:60 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:150 +#, elixir-autogen, elixir-format +msgid "Description" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:56 +#, elixir-autogen, elixir-format +msgid "Description*" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:30 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:166 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:127 +#, elixir-autogen, elixir-format +msgid "Details" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:159 +#, elixir-autogen, elixir-format +msgid "Difficulty" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:145 +#, elixir-autogen, elixir-format +msgid "Displaying the init data provided of the creating transaction." +msgstr "" + +#: lib/block_scout_web/templates/csv_export/index.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Download" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:72 +#, elixir-autogen, elixir-format +msgid "Drop all Solidity contract source files into the drop zone." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Drop sources and metadata JSON file or click here" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:67 +#, elixir-autogen, elixir-format +msgid "Drop sources or click here" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:28 +#, elixir-autogen, elixir-format +msgid "Drop the standard input JSON file or click here" +msgstr "" + +#: lib/block_scout_web/templates/transaction/not_found.html.eex:22 +#, elixir-autogen, elixir-format +msgid "During times when the network is busy (i.e during ICOs) it can take a while for your transaction to propagate through the network and for us to index it." +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:27 +#, elixir-autogen, elixir-format +msgid "E-mail*" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_minimal_proxy_pattern.html.eex:6 +#, elixir-autogen, elixir-format +msgid "EIP-1167" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:215 +#, elixir-autogen, elixir-format +msgid "ERC-1155 " +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:213 +#, elixir-autogen, elixir-format +msgid "ERC-20 " +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:40 +#, elixir-autogen, elixir-format +msgid "ERC-20 tokens (beta)" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:214 +#, elixir-autogen, elixir-format +msgid "ERC-721 " +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:53 +#, elixir-autogen, elixir-format +msgid "ERC-721, ERC-1155 tokens (NFT) (beta)" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:4 +#, elixir-autogen, elixir-format +msgid "ETH RPC API Documentation" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:76 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:26 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:22 +#, elixir-autogen, elixir-format +msgid "EVM Version" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:30 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:26 +#, elixir-autogen, elixir-format +msgid "EVM version details" +msgstr "" + +#: lib/block_scout_web/views/block_transaction_view.ex:7 +#, elixir-autogen, elixir-format +msgid "Easy Cowboy! This block does not exist yet!" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/row.html.eex:16 +#: lib/block_scout_web/templates/account/custom_abi/row.html.eex:16 +#: lib/block_scout_web/templates/account/watchlist_address/row.html.eex:27 +#, elixir-autogen, elixir-format +msgid "Edit" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Edit Watch list address" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:71 +#, elixir-autogen, elixir-format +msgid "Email notifications" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Emission Contract" +msgstr "" + +#: lib/block_scout_web/views/block_view.ex:73 +#, elixir-autogen, elixir-format +msgid "Emission Reward" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:68 +#, elixir-autogen, elixir-format +msgid "Enter the Solidity Contract Code" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Enter the Vyper Contract Code" +msgstr "" + +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:11 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Error" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_tile.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Error in internal transactions" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:33 +#, elixir-autogen, elixir-format +msgid "Error rendering value" +msgstr "" + +#: lib/block_scout_web/templates/address/_balance_dropdown.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Error trying to fetch balances." +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:354 +#, elixir-autogen, elixir-format +msgid "Error: %{reason}" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:352 +#, elixir-autogen, elixir-format +msgid "Error: (Awaiting internal transactions for reason)" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:120 +#, elixir-autogen, elixir-format +msgid "Error: Could not determine contract creator." +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:114 +#, elixir-autogen, elixir-format +msgid "Eth RPC" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:211 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:164 +#, elixir-autogen, elixir-format +msgid "Example Value" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:128 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:99 +#, elixir-autogen, elixir-format +msgid "Execute" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55 +#, elixir-autogen, elixir-format +msgid "Expand" +msgstr "" + +#: lib/block_scout_web/templates/csv_export/index.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Export Data" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:212 +#, elixir-autogen, elixir-format +msgid "External libraries" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:40 +#, elixir-autogen, elixir-format +msgid "Failed to decode input data." +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:35 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:37 +#, elixir-autogen, elixir-format +msgid "Failed to decode log data." +msgstr "" + +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Fast" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:248 +#, elixir-autogen, elixir-format +msgid "Fetching gas used..." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:112 +#, elixir-autogen, elixir-format +msgid "Fetching holders..." +msgstr "" + +#: lib/block_scout_web/templates/address/_balance_dropdown.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Fetching tokens..." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:195 +#: lib/block_scout_web/templates/address/overview.html.eex:203 +#, elixir-autogen, elixir-format +msgid "Fetching transactions..." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:222 +#: lib/block_scout_web/templates/address/overview.html.eex:230 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:123 +#, elixir-autogen, elixir-format +msgid "Fetching transfers..." +msgstr "" + +#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:16 +#, elixir-autogen, elixir-format +msgid "For any existing contracts in the database, insert all ABI entries into the contract_methods table. Use this in case you have verified smart contracts before early March 2019 and you want other contracts with the same functions to show those ABI's as candidate matches." +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:44 +#, elixir-autogen, elixir-format +msgid "Forked Blocks (Reorgs)" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:43 +#, elixir-autogen, elixir-format +msgid "Forum" +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:38 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:40 +#: lib/block_scout_web/templates/address_transaction/index.html.eex:34 +#: lib/block_scout_web/templates/transaction/overview.html.eex:200 +#: lib/block_scout_web/views/address_internal_transaction_view.ex:10 +#: lib/block_scout_web/views/address_token_transfer_view.ex:10 +#: lib/block_scout_web/views/address_transaction_view.ex:10 +#, elixir-autogen, elixir-format +msgid "From" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:18 +#, elixir-autogen, elixir-format +msgid "GET" +msgstr "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:67 +#: lib/block_scout_web/templates/block/overview.html.eex:187 +#: lib/block_scout_web/templates/transaction/overview.html.eex:373 +#, elixir-autogen, elixir-format +msgid "Gas Limit" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:353 +#, elixir-autogen, elixir-format +msgid "Gas Price" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:241 +#: lib/block_scout_web/templates/block/_tile.html.eex:73 +#: lib/block_scout_web/templates/block/overview.html.eex:178 +#, elixir-autogen, elixir-format +msgid "Gas Used" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:426 +#, elixir-autogen, elixir-format +msgid "Gas Used by Transaction" +msgstr "" + +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:3 +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Gas tracker" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:240 +#, elixir-autogen, elixir-format +msgid "Gas used by the address." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:60 +#, elixir-autogen, elixir-format +msgid "Genesis Block" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Github" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:8 +#, elixir-autogen, elixir-format +msgid "Go to" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:104 +#, elixir-autogen, elixir-format +msgid "GraphQL" +msgstr "" + +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:11 +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:20 +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:21 +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:22 +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:38 +#: lib/block_scout_web/views/block_view.ex:22 +#: lib/block_scout_web/views/wei_helpers.ex:77 +#, elixir-autogen, elixir-format +msgid "Gwei" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:123 +#, elixir-autogen, elixir-format +msgid "Hash" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:454 +#: lib/block_scout_web/templates/transaction/overview.html.eex:458 +#, elixir-autogen, elixir-format +msgid "Hex (Default)" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:108 +#, elixir-autogen, elixir-format +msgid "Holders" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:11 +#, elixir-autogen, elixir-format +msgid "However, in general, the" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19 +#, elixir-autogen, elixir-format +msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts." +msgstr "" + +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:42 +#: lib/block_scout_web/templates/transaction/_tile.html.eex:92 +#, elixir-autogen, elixir-format +msgid "IN" +msgstr "" + +#: lib/block_scout_web/templates/transaction/not_found.html.eex:26 +#, elixir-autogen, elixir-format +msgid "If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:52 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:48 +#, elixir-autogen, elixir-format +msgid "If you enabled optimization during compilation, select yes." +msgstr "" + +#: lib/block_scout_web/templates/transaction/not_found.html.eex:12 +#, elixir-autogen, elixir-format +msgid "If you have just submitted this transaction please wait for at least 30 seconds before refreshing this page." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:133 +#, elixir-autogen, elixir-format +msgid "Implementation" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:132 +#, elixir-autogen, elixir-format +msgid "Implementation address of the proxy contract." +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:30 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:43 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:56 +#, elixir-autogen, elixir-format +msgid "Incoming" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:438 +#, elixir-autogen, elixir-format +msgid "Index position of Transaction in the block." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:249 +#, elixir-autogen, elixir-format +msgid "Index position(s) of referenced stale blocks." +msgstr "" + +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:6 +#, elixir-autogen, elixir-format +msgid "Indexed?" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Input" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:219 +#, elixir-autogen, elixir-format +msgid "Interacted With (To)" +msgstr "" + +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Internal Transaction" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:28 +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:17 +#: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 +#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 +#: lib/block_scout_web/views/address_view.ex:364 +#: lib/block_scout_web/views/transaction_view.ex:513 +#, elixir-autogen, elixir-format +msgid "Internal Transactions" +msgstr "" + +#: lib/block_scout_web/templates/transaction/invalid.html.eex:6 +#, elixir-autogen, elixir-format +msgid "Invalid Transaction Hash" +msgstr "" + +#: lib/block_scout_web/templates/tokens/inventory/index.html.eex:16 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:19 +#: lib/block_scout_web/views/tokens/overview_view.ex:42 +#, elixir-autogen, elixir-format +msgid "Inventory" +msgstr "" + +#: lib/block_scout_web/templates/transaction/not_found.html.eex:16 +#, elixir-autogen, elixir-format +msgid "It could still be in the TX Pool of a different node, waiting to be broadcasted." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:259 +#, elixir-autogen, elixir-format +msgid "Last Balance Update" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/index.html.eex:12 +#: lib/block_scout_web/templates/account/api_key/index.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Learn more" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:45 +#, elixir-autogen, elixir-format +msgid "Less than" +msgstr "" + +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:24 +#, elixir-autogen, elixir-format +msgid "License Expires" +msgstr "" + +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:10 +#, elixir-autogen, elixir-format +msgid "License ID" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:305 +#, elixir-autogen, elixir-format +msgid "List of ERC-1155 tokens created in the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:289 +#, elixir-autogen, elixir-format +msgid "List of token burnt in the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:272 +#, elixir-autogen, elixir-format +msgid "List of token minted in the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:256 +#, elixir-autogen, elixir-format +msgid "List of token transferred in the transaction." +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Loading chart..." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:77 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:105 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:35 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:99 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:49 +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:45 +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:41 +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:49 +#: lib/block_scout_web/templates/address_read_proxy/index.html.eex:12 +#: lib/block_scout_web/templates/address_write_contract/index.html.eex:39 +#: lib/block_scout_web/templates/address_write_contract/index.html.eex:47 +#: lib/block_scout_web/templates/address_write_proxy/index.html.eex:12 +#: lib/block_scout_web/templates/tokens/contract/index.html.eex:17 +#, elixir-autogen, elixir-format +msgid "Loading..." +msgstr "" + +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:2 +#, elixir-autogen, elixir-format +msgid "Log Data" +msgstr "" + +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:131 +#, elixir-autogen, elixir-format +msgid "Log Index" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:41 +#: lib/block_scout_web/templates/address_logs/index.html.eex:10 +#: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 +#: lib/block_scout_web/templates/transaction_log/index.html.eex:8 +#: lib/block_scout_web/views/address_view.ex:375 +#: lib/block_scout_web/views/transaction_view.ex:514 +#, elixir-autogen, elixir-format +msgid "Logs" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:52 +#, elixir-autogen, elixir-format +msgid "Main Networks" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:52 +#: lib/block_scout_web/templates/layout/app.html.eex:46 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:84 +#: lib/block_scout_web/views/address_view.ex:145 +#, elixir-autogen, elixir-format +msgid "Market Cap" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:382 +#, elixir-autogen, elixir-format +msgid "Max Fee per Gas" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:392 +#, elixir-autogen, elixir-format +msgid "Max Priority Fee per Gas" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:320 +#, elixir-autogen, elixir-format +msgid "Max of" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:372 +#, elixir-autogen, elixir-format +msgid "Maximum gas amount approved for the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:381 +#, elixir-autogen, elixir-format +msgid "Maximum total amount per unit of gas a user is willing to pay for a transaction, including base fee and priority fee." +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18 +#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:198 +#, elixir-autogen, elixir-format +msgid "Metadata" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Method Id" +msgstr "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:41 +#: lib/block_scout_web/templates/block/overview.html.eex:98 +#: lib/block_scout_web/templates/chain/_block.html.eex:16 +#: lib/block_scout_web/templates/transaction_state/_state_change.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Miner" +msgstr "" + +#: lib/block_scout_web/views/block_view.ex:63 +#: lib/block_scout_web/views/block_view.ex:68 +#, elixir-autogen, elixir-format +msgid "Miner Reward" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_minimal_proxy_pattern.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Minimal Proxy Contract for" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:206 +#, elixir-autogen, elixir-format +msgid "Minimum fee required per unit of gas. Fee adjusts based on network congestion." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:223 +#, elixir-autogen, elixir-format +msgid "Model" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:58 +#, elixir-autogen, elixir-format +msgid "Module" +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:12 +#, elixir-autogen, elixir-format +msgid "More internal transactions have come in" +msgstr "" + +#: lib/block_scout_web/templates/address_transaction/index.html.eex:46 +#: lib/block_scout_web/templates/chain/show.html.eex:216 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:13 +#: lib/block_scout_web/templates/transaction/index.html.eex:19 +#, elixir-autogen, elixir-format +msgid "More transactions have come in" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:63 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:74 +#, elixir-autogen, elixir-format +msgid "Must be set to:" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Must match the name specified in the code. For example, in contract MyContract {..} MyContract is the contract name." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:116 +#, elixir-autogen, elixir-format +msgid "N/A bytes" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:19 +#: lib/block_scout_web/templates/account/api_key/index.html.eex:28 +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:13 +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:28 +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:18 +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:22 +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:18 +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:22 +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:22 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:19 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_name.html.eex:4 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:52 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:59 +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:4 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21 +#, elixir-autogen, elixir-format +msgid "Name" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Name this API key" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Name this Custom ABI" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:19 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Name this address" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:19 +#, elixir-autogen, elixir-format +msgid "Name this transaction" +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:44 +#, elixir-autogen, elixir-format +msgid "Net Worth" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:5 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:5 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:9 +#, elixir-autogen, elixir-format +msgid "New Smart Contract Verification" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:9 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:7 +#, elixir-autogen, elixir-format +msgid "New Solidity Smart Contract Verification" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:7 +#, elixir-autogen, elixir-format +msgid "New Vyper Smart Contract Verification" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:80 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:87 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:95 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:103 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:111 +#, elixir-autogen, elixir-format +msgid "Next" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:9 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:9 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:42 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:38 +#, elixir-autogen, elixir-format +msgid "No" +msgstr "" + +#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:23 +#, elixir-autogen, elixir-format +msgid "No trace entries found." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:196 +#: lib/block_scout_web/templates/transaction/overview.html.eex:436 +#, elixir-autogen, elixir-format +msgid "Nonce" +msgstr "" + +#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Not unique Token" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:107 +#, elixir-autogen, elixir-format +msgid "Number of accounts holding the token" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:275 +#, elixir-autogen, elixir-format +msgid "Number of blocks validated by this validator." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:130 +#, elixir-autogen, elixir-format +msgid "Number of digits that come after the decimal place when displaying token value" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:186 +#, elixir-autogen, elixir-format +msgid "Number of transactions related to this address." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:118 +#, elixir-autogen, elixir-format +msgid "Number of transfers for the token" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:213 +#, elixir-autogen, elixir-format +msgid "Number of transfers to/from this address." +msgstr "" + +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:40 +#: lib/block_scout_web/templates/transaction/_tile.html.eex:88 +#, elixir-autogen, elixir-format +msgid "OUT" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Only the first" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:61 +#, elixir-autogen, elixir-format +msgid "Optimization enabled" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:70 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:58 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:54 +#, elixir-autogen, elixir-format +msgid "Optimization runs" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:76 +#, elixir-autogen, elixir-format +msgid "Other Explorers" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:35 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:48 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:61 +#, elixir-autogen, elixir-format +msgid "Outgoing" +msgstr "" + +#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Owner Address" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:73 +#, elixir-autogen, elixir-format +msgid "POA solidity flattener or the" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:19 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:26 +#, elixir-autogen, elixir-format +msgid "POST" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:41 +#, elixir-autogen, elixir-format +msgid "Page" +msgstr "" + +#: lib/block_scout_web/templates/page_not_found/index.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Page not found" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:33 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:40 +#, elixir-autogen, elixir-format +msgid "Parameters" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:139 +#, elixir-autogen, elixir-format +msgid "Parent Hash" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:62 +#: lib/block_scout_web/views/transaction_view.ex:349 +#: lib/block_scout_web/views/transaction_view.ex:387 +#, elixir-autogen, elixir-format +msgid "Pending" +msgstr "" + +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Pending Transactions" +msgstr "" + +#: lib/block_scout_web/templates/address/_custom_view_df_title.html.eex:9 +#: lib/block_scout_web/templates/address/_custom_view_df_title.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Play" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:68 +#, elixir-autogen, elixir-format +msgid "Please select notification methods:" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Please select what types of notifications you will receive:" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:438 +#, elixir-autogen, elixir-format +msgid "Position" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:254 +#, elixir-autogen, elixir-format +msgid "Position %{index}" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:32 +#, elixir-autogen, elixir-format +msgid "Potential matches from our contract method database:" +msgstr "" + +#: lib/block_scout_web/templates/layout/_search.html.eex:27 +#, elixir-autogen, elixir-format +msgid "Press / and focus will be moved to the search field" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:41 +#: lib/block_scout_web/templates/layout/app.html.eex:47 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:95 +#, elixir-autogen, elixir-format +msgid "Price" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:94 +#, elixir-autogen, elixir-format +msgid "Price per token on the exchanges" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:352 +#, elixir-autogen, elixir-format +msgid "Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:225 +#: lib/block_scout_web/templates/transaction/overview.html.eex:402 +#, elixir-autogen, elixir-format +msgid "Priority Fee / Tip" +msgstr "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:62 +#, elixir-autogen, elixir-format +msgid "Priority Fees" +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:4 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Profile" +msgstr "" + +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:19 +#, elixir-autogen, elixir-format +msgid "Public Tags" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Public tag" +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:22 +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Public tags" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:50 +#, elixir-autogen, elixir-format +msgid "Public tags* (2 tags maximum, please use \";\" as a divider)" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_btn_qr_code.html.eex:10 +#: lib/block_scout_web/templates/common_components/_modal_qr_code.html.eex:5 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:83 +#, elixir-autogen, elixir-format +msgid "QR Code" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:100 +#, elixir-autogen, elixir-format +msgid "Query" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:109 +#, elixir-autogen, elixir-format +msgid "RPC" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:447 +#, elixir-autogen, elixir-format +msgid "Raw Input" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_tabs.html.eex:24 +#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 +#: lib/block_scout_web/views/transaction_view.ex:515 +#, elixir-autogen, elixir-format +msgid "Raw Trace" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:81 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:27 +#: lib/block_scout_web/views/address_view.ex:369 +#: lib/block_scout_web/views/tokens/overview_view.ex:41 +#, elixir-autogen, elixir-format +msgid "Read Contract" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:88 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:41 +#: lib/block_scout_web/views/address_view.ex:370 +#, elixir-autogen, elixir-format +msgid "Read Proxy" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Records" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/row.html.eex:13 +#: lib/block_scout_web/templates/account/custom_abi/row.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Remove" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:77 +#, elixir-autogen, elixir-format +msgid "Remove from Watch list" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:155 +#, elixir-autogen, elixir-format +msgid "Request URL" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Request a public tag/label" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:37 +#, elixir-autogen, elixir-format +msgid "Request to add public tag" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Request to edit a public tag/label" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:108 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:38 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:104 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:52 +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:48 +#, elixir-autogen, elixir-format +msgid "Reset" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:173 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:134 +#, elixir-autogen, elixir-format +msgid "Response Body" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:185 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:147 +#, elixir-autogen, elixir-format +msgid "Responses" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:98 +#, elixir-autogen, elixir-format +msgid "Result" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:135 +#, elixir-autogen, elixir-format +msgid "Revert reason" +msgstr "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:52 +#: lib/block_scout_web/templates/chain/_block.html.eex:27 +#: lib/block_scout_web/views/internal_transaction_view.ex:28 +#, elixir-autogen, elixir-format +msgid "Reward" +msgstr "" + +#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:21 +#, elixir-autogen, elixir-format +msgid "Run" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:26 +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:31 +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:25 +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:25 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:83 +#, elixir-autogen, elixir-format +msgid "Save" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/index.html.eex:16 +#: lib/block_scout_web/templates/layout/_search.html.eex:34 +#, elixir-autogen, elixir-format +msgid "Search" +msgstr "" + +#: lib/block_scout_web/templates/search/results.html.eex:17 +#, elixir-autogen, elixir-format +msgid "Search Results" +msgstr "" + +#: lib/block_scout_web/templates/layout/_search.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Search by address, token symbol name, transaction hash, or block number" +msgstr "" + +#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:47 +#, elixir-autogen, elixir-format +msgid "Search tokens" +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:27 +#, elixir-autogen, elixir-format +msgid "Self-Destruct" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:63 +#, elixir-autogen, elixir-format +msgid "Send request" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:163 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:124 +#, elixir-autogen, elixir-format +msgid "Server Response" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Show" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_btn_qr_code.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Show QR Code" +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:52 +#, elixir-autogen, elixir-format +msgid "Shows the current" +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:59 +#, elixir-autogen, elixir-format +msgid "Shows the tokens held in the address (includes ERC-20, ERC-721 and ERC-1155)." +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:66 +#, elixir-autogen, elixir-format +msgid "Shows the total CRC balance in the address." +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:45 +#, elixir-autogen, elixir-format +msgid "Shows total assets held in the address" +msgstr "" + +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Sign out" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:114 +#, elixir-autogen, elixir-format +msgid "Size" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:113 +#, elixir-autogen, elixir-format +msgid "Size of the block in bytes." +msgstr "" + +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Slow" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:21 +#, elixir-autogen, elixir-format +msgid "Smart contract / Address" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/address_field.html.eex:4 +#: lib/block_scout_web/templates/account/public_tags_request/address_field.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Smart contract / Address (0x...)" +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:30 +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:50 +#: lib/block_scout_web/templates/address_logs/index.html.eex:23 +#: lib/block_scout_web/templates/address_token/index.html.eex:60 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:58 +#: lib/block_scout_web/templates/address_transaction/index.html.eex:50 +#: lib/block_scout_web/templates/address_validation/index.html.eex:20 +#: lib/block_scout_web/templates/block_transaction/index.html.eex:22 +#: lib/block_scout_web/templates/chain/show.html.eex:157 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:18 +#: lib/block_scout_web/templates/tokens/holder/index.html.eex:24 +#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:23 +#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:23 +#: lib/block_scout_web/templates/tokens/inventory/index.html.eex:23 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:22 +#: lib/block_scout_web/templates/transaction/index.html.eex:25 +#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:13 +#: lib/block_scout_web/templates/transaction_log/index.html.eex:15 +#: lib/block_scout_web/templates/transaction_state/index.html.eex:8 +#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:14 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:45 +#, elixir-autogen, elixir-format +msgid "Something went wrong, click to reload." +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:222 +#, elixir-autogen, elixir-format +msgid "Something went wrong, click to retry." +msgstr "" + +#: lib/block_scout_web/templates/transaction/not_found.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Sorry, We are unable to locate this transaction Hash" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:63 +#, elixir-autogen, elixir-format +msgid "Sources *.sol files" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Sources and Metadata JSON" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:130 +#, elixir-autogen, elixir-format +msgid "Stakes" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Standard Input JSON" +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:24 +#, elixir-autogen, elixir-format +msgid "Static Call" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:110 +#, elixir-autogen, elixir-format +msgid "Status" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Submission date" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:39 +#, elixir-autogen, elixir-format +msgid "Submit an Issue" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 +#: lib/block_scout_web/views/transaction_view.ex:351 +#, elixir-autogen, elixir-format +msgid "Success" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:21 +#: lib/block_scout_web/templates/transaction/_tile.html.eex:52 +#, elixir-autogen, elixir-format +msgid "TX Fee" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:30 +#, elixir-autogen, elixir-format +msgid "Telegram" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:65 +#, elixir-autogen, elixir-format +msgid "Test Networks" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_first.html.eex:11 +#, elixir-autogen, elixir-format +msgid "The 0x library address. This can be found in the generated json file or Truffle output (if using truffle)." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:30 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:26 +#, elixir-autogen, elixir-format +msgid "The EVM version the contract is written for. If the bytecode does not match the version, we try to verify using the latest EVM version." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:122 +#, elixir-autogen, elixir-format +msgid "The SHA256 hash of the block." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:51 +#, elixir-autogen, elixir-format +msgid "The block height of a particular block is defined as the number of blocks preceding it in the blockchain." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:22 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:18 +#, elixir-autogen, elixir-format +msgid "The compiler version is specified in pragma solidity X.X.X. Use the compiler version rather than the nightly build. If using the Solidity compiler, run solc —version to check." +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:38 +#, elixir-autogen, elixir-format +msgid "The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:138 +#, elixir-autogen, elixir-format +msgid "The hash of the block from which this block was generated." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:74 +#, elixir-autogen, elixir-format +msgid "The name found in the source code of the Contract." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:85 +#, elixir-autogen, elixir-format +msgid "The name of the validator." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:79 +#, elixir-autogen, elixir-format +msgid "The number of transactions in the block." +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:40 +#, elixir-autogen, elixir-format +msgid "The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception." +msgstr "" + +#: lib/block_scout_web/templates/page_not_found/index.html.eex:8 +#, elixir-autogen, elixir-format +msgid "The requested path was not found on BlockScout." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:134 +#, elixir-autogen, elixir-format +msgid "The revert reason of the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:109 +#, elixir-autogen, elixir-format +msgid "The status of the transaction: Confirmed or Unconfirmed." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:68 +#, elixir-autogen, elixir-format +msgid "The total amount of tokens issued" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:177 +#, elixir-autogen, elixir-format +msgid "The total gas amount used in the block and its percentage of gas filled in the block." +msgstr "" + +#: lib/block_scout_web/templates/address_validation/index.html.eex:16 +#, elixir-autogen, elixir-format +msgid "There are no blocks validated by this address." +msgstr "" + +#: lib/block_scout_web/templates/block/index.html.eex:17 +#, elixir-autogen, elixir-format +msgid "There are no blocks." +msgstr "" + +#: lib/block_scout_web/templates/tokens/holder/index.html.eex:29 +#, elixir-autogen, elixir-format +msgid "There are no holders for this Token." +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:54 +#, elixir-autogen, elixir-format +msgid "There are no internal transactions for this address." +msgstr "" + +#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:17 +#, elixir-autogen, elixir-format +msgid "There are no internal transactions for this transaction." +msgstr "" + +#: lib/block_scout_web/templates/address_logs/index.html.eex:28 +#, elixir-autogen, elixir-format +msgid "There are no logs for this address." +msgstr "" + +#: lib/block_scout_web/templates/transaction_log/index.html.eex:20 +#, elixir-autogen, elixir-format +msgid "There are no logs for this transaction." +msgstr "" + +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:22 +#, elixir-autogen, elixir-format +msgid "There are no pending transactions." +msgstr "" + +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:53 +#, elixir-autogen, elixir-format +msgid "There are no token transfers for this address." +msgstr "" + +#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:19 +#, elixir-autogen, elixir-format +msgid "There are no token transfers for this transaction" +msgstr "" + +#: lib/block_scout_web/templates/address_token/index.html.eex:65 +#, elixir-autogen, elixir-format +msgid "There are no tokens for this address." +msgstr "" + +#: lib/block_scout_web/templates/tokens/inventory/index.html.eex:28 +#, elixir-autogen, elixir-format +msgid "There are no tokens." +msgstr "" + +#: lib/block_scout_web/templates/address_transaction/index.html.eex:55 +#, elixir-autogen, elixir-format +msgid "There are no transactions for this address." +msgstr "" + +#: lib/block_scout_web/templates/block_transaction/index.html.eex:27 +#, elixir-autogen, elixir-format +msgid "There are no transactions for this block." +msgstr "" + +#: lib/block_scout_web/templates/transaction/index.html.eex:31 +#, elixir-autogen, elixir-format +msgid "There are no transactions." +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:28 +#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:28 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:27 +#, elixir-autogen, elixir-format +msgid "There are no transfers for this Token." +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:35 +#, elixir-autogen, elixir-format +msgid "There is no coin history for this address." +msgstr "" + +#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:29 +#, elixir-autogen, elixir-format +msgid "There is no decompilded contracts for this address." +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:21 +#: lib/block_scout_web/templates/chain/show.html.eex:9 +#, elixir-autogen, elixir-format +msgid "There was a problem loading the chart." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/index.html.eex:6 +#, elixir-autogen, elixir-format +msgid "This API is provided for developers transitioning their applications from Etherscan to BlockScout. It supports GET and POST requests." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:7 +#, elixir-autogen, elixir-format +msgid "This API is provided to support some rpc methods in the exact format specified for ethereum nodes, which can be found " +msgstr "" + +#: lib/block_scout_web/views/block_transaction_view.ex:11 +#, elixir-autogen, elixir-format +msgid "This block has not been processed yet." +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:41 +#, elixir-autogen, elixir-format +msgid "This contract has been partially verified via Sourcify." +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:45 +#, elixir-autogen, elixir-format +msgid "This contract has been verified via Sourcify." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:10 +#, elixir-autogen, elixir-format +msgid "This is useful to allow sending requests to blockscout without having to change anything about the request." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:64 +#, elixir-autogen, elixir-format +msgid "This transaction is pending confirmation." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:71 +#: lib/block_scout_web/templates/transaction/overview.html.eex:177 +#, elixir-autogen, elixir-format +msgid "Timestamp" +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:32 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:34 +#: lib/block_scout_web/templates/address_transaction/index.html.eex:28 +#: lib/block_scout_web/templates/transaction/overview.html.eex:221 +#: lib/block_scout_web/views/address_internal_transaction_view.ex:9 +#: lib/block_scout_web/views/address_token_transfer_view.ex:9 +#: lib/block_scout_web/views/address_transaction_view.ex:9 +#, elixir-autogen, elixir-format +msgid "To" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:20 +#, elixir-autogen, elixir-format +msgid "To have guaranteed accuracy, use the link above to verify the contract's source code." +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:6 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:8 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:6 +#, elixir-autogen, elixir-format +msgid "To see accurate decoded input data, the contract must be verified." +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Toggle navigation" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:55 +#, elixir-autogen, elixir-format +msgid "Token" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:3 +#: lib/block_scout_web/views/transaction_view.ex:449 +#, elixir-autogen, elixir-format +msgid "Token Burning" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:7 +#: lib/block_scout_web/views/transaction_view.ex:450 +#, elixir-autogen, elixir-format +msgid "Token Creation" +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:10 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:34 +#, elixir-autogen, elixir-format +msgid "Token Details" +msgstr "" + +#: lib/block_scout_web/templates/tokens/holder/index.html.eex:17 +#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:16 +#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:17 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:11 +#: lib/block_scout_web/views/tokens/overview_view.ex:40 +#, elixir-autogen, elixir-format +msgid "Token Holders" +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:38 +#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:18 +#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:37 +#, elixir-autogen, elixir-format +msgid "Token ID" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:5 +#: lib/block_scout_web/views/transaction_view.ex:448 +#, elixir-autogen, elixir-format +msgid "Token Minting" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:9 +#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:11 +#: lib/block_scout_web/views/transaction_view.ex:451 +#, elixir-autogen, elixir-format +msgid "Token Transfer" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:13 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 +#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:3 +#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:16 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:5 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:15 +#: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 +#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 +#: lib/block_scout_web/views/address_view.ex:366 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:197 +#: lib/block_scout_web/views/tokens/overview_view.ex:39 +#: lib/block_scout_web/views/transaction_view.ex:512 +#, elixir-autogen, elixir-format +msgid "Token Transfers" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:54 +#, elixir-autogen, elixir-format +msgid "Token name and symbol." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:142 +#, elixir-autogen, elixir-format +msgid "Token type" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:21 +#: lib/block_scout_web/templates/address/overview.html.eex:176 +#: lib/block_scout_web/templates/address_token/overview.html.eex:58 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:13 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:78 +#: lib/block_scout_web/templates/tokens/index.html.eex:10 +#: lib/block_scout_web/views/address_view.ex:363 +#, elixir-autogen, elixir-format +msgid "Tokens" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:290 +#, elixir-autogen, elixir-format +msgid "Tokens Burnt" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:306 +#, elixir-autogen, elixir-format +msgid "Tokens Created" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:273 +#, elixir-autogen, elixir-format +msgid "Tokens Minted" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:257 +#, elixir-autogen, elixir-format +msgid "Tokens Transferred" +msgstr "" + +#: lib/block_scout_web/templates/address/_metatags.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Top Accounts - %{subnetwork} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/index.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Topic" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:71 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:91 +#, elixir-autogen, elixir-format +msgid "Topics" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:169 +#, elixir-autogen, elixir-format +msgid "Total Difficulty" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:83 +#, elixir-autogen, elixir-format +msgid "Total Supply * Price" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:130 +#, elixir-autogen, elixir-format +msgid "Total blocks" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:168 +#, elixir-autogen, elixir-format +msgid "Total difficulty of the chain until this block." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:186 +#, elixir-autogen, elixir-format +msgid "Total gas limit provided by all transactions in the block." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:69 +#, elixir-autogen, elixir-format +msgid "Total supply" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:337 +#, elixir-autogen, elixir-format +msgid "Total transaction fee." +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:109 +#, elixir-autogen, elixir-format +msgid "Total transactions" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:11 +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:23 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:19 +#: lib/block_scout_web/views/transaction_view.ex:461 +#, elixir-autogen, elixir-format +msgid "Transaction" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_metatags.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Transaction %{transaction} - %{subnetwork} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_metatags.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Transaction %{transaction}, %{subnetwork} %{transaction}" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:412 +#, elixir-autogen, elixir-format +msgid "Transaction Burnt Fee" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:50 +#, elixir-autogen, elixir-format +msgid "Transaction Details" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:338 +#, elixir-autogen, elixir-format +msgid "Transaction Fee" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:80 +#, elixir-autogen, elixir-format +msgid "Transaction Hash" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:2 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19 +#, elixir-autogen, elixir-format +msgid "Transaction Inputs" +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:13 +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:7 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:16 +#, elixir-autogen, elixir-format +msgid "Transaction Tags" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:362 +#, elixir-autogen, elixir-format +msgid "Transaction Type" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:435 +#, elixir-autogen, elixir-format +msgid "Transaction number from the sending address. Each transaction sent from an address increments the nonce by 1." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:361 +#, elixir-autogen, elixir-format +msgid "Transaction type, introduced in EIP-2718." +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:7 +#: lib/block_scout_web/templates/address/overview.html.eex:187 +#: lib/block_scout_web/templates/address/overview.html.eex:193 +#: lib/block_scout_web/templates/address/overview.html.eex:201 +#: lib/block_scout_web/templates/address_transaction/index.html.eex:13 +#: lib/block_scout_web/templates/block/overview.html.eex:80 +#: lib/block_scout_web/templates/block_transaction/index.html.eex:10 +#: lib/block_scout_web/templates/chain/show.html.eex:213 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:49 +#: lib/block_scout_web/views/address_view.ex:365 +#, elixir-autogen, elixir-format +msgid "Transactions" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:101 +#, elixir-autogen, elixir-format +msgid "Transactions and address of creation." +msgstr "" + +#: lib/block_scout_web/templates/address/_tile.html.eex:31 +#, elixir-autogen, elixir-format +msgid "Transactions sent" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:214 +#: lib/block_scout_web/templates/address/overview.html.eex:220 +#: lib/block_scout_web/templates/address/overview.html.eex:228 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:50 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:119 +#, elixir-autogen, elixir-format +msgid "Transfers" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:40 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:47 +#, elixir-autogen, elixir-format +msgid "Try it out" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:27 +#, elixir-autogen, elixir-format +msgid "Twitter" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:49 +#, elixir-autogen, elixir-format +msgid "Tx/day" +msgstr "" + +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:5 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Type" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:141 +#, elixir-autogen, elixir-format +msgid "Type of the token standard" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:461 +#, elixir-autogen, elixir-format +msgid "UTF-8" +msgstr "" + +#: lib/block_scout_web/views/block_view.ex:77 +#, elixir-autogen, elixir-format +msgid "Uncle Reward" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:250 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:41 +#, elixir-autogen, elixir-format +msgid "Uncles" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:342 +#, elixir-autogen, elixir-format +msgid "Unconfirmed" +msgstr "" + +#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:9 +#, elixir-autogen, elixir-format +msgid "Unique Token" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:79 +#, elixir-autogen, elixir-format +msgid "Unique character string (TxID) assigned to every verified transaction." +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:7 +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:8 +#, elixir-autogen, elixir-format +msgid "Update" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:391 +#, elixir-autogen, elixir-format +msgid "User defined maximum fee (tip) per unit of gas paid to validator for transaction prioritization." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:401 +#, elixir-autogen, elixir-format +msgid "User-defined tip sent to validator for transaction priority/inclusion." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:224 +#, elixir-autogen, elixir-format +msgid "User-defined tips sent to validator for transaction priority/inclusion." +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:55 +#, elixir-autogen, elixir-format +msgid "Validated" +msgstr "" + +#: lib/block_scout_web/templates/transaction/index.html.eex:12 +#, elixir-autogen, elixir-format +msgid "Validated Transactions" +msgstr "" + +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:30 +#, elixir-autogen, elixir-format +msgid "Validator Creation Date" +msgstr "" + +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Validator Data" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:86 +#, elixir-autogen, elixir-format +msgid "Validator Name" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:323 +#, elixir-autogen, elixir-format +msgid "Value" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:322 +#, elixir-autogen, elixir-format +msgid "Value sent in the native token (and USD) if applicable." +msgstr "" + +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:17 +#: lib/block_scout_web/templates/address_write_contract/index.html.eex:15 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:75 +#, elixir-autogen, elixir-format +msgid "Verified" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:82 +#, elixir-autogen, elixir-format +msgid "Verified at" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:27 +#: lib/block_scout_web/templates/address_contract/index.html.eex:29 +#: lib/block_scout_web/templates/address_contract/index.html.eex:161 +#: lib/block_scout_web/templates/address_contract/index.html.eex:192 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Verify & Publish" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:107 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:37 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:103 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:51 +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:47 +#, elixir-autogen, elixir-format +msgid "Verify & publish" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Verify the contract " +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:91 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:66 +#, elixir-autogen, elixir-format +msgid "Version" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:33 +#, elixir-autogen, elixir-format +msgid "Via Sourcify: Sources and metadata JSON file" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:27 +#, elixir-autogen, elixir-format +msgid "Via Standard Input JSON" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Via flattened source code" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40 +#, elixir-autogen, elixir-format +msgid "Via multi-part files" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:152 +#, elixir-autogen, elixir-format +msgid "View All Blocks" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:212 +#, elixir-autogen, elixir-format +msgid "View All Transactions" +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:16 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20 +#, elixir-autogen, elixir-format +msgid "View Contract" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_tile.html.eex:73 +#, elixir-autogen, elixir-format +msgid "View Less Transfers" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_tile.html.eex:72 +#, elixir-autogen, elixir-format +msgid "View More Transfers" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:39 +#, elixir-autogen, elixir-format +msgid "View next block" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:23 +#, elixir-autogen, elixir-format +msgid "View previous block" +msgstr "" + +#: lib/block_scout_web/templates/address/_metatags.html.eex:9 +#, elixir-autogen, elixir-format +msgid "View the account balance, transactions, and other data for %{address} on the %{network}" +msgstr "" + +#: lib/block_scout_web/templates/block/_metatags.html.eex:10 +#, elixir-autogen, elixir-format +msgid "View the transactions, token transfers, and uncles for block number %{block_number}" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_metatags.html.eex:10 +#, elixir-autogen, elixir-format +msgid "View transaction %{transaction} on %{subnetwork}" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:46 +#, elixir-autogen, elixir-format +msgid "Vyper contract" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:136 +#, elixir-autogen, elixir-format +msgid "WEI" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex:9 +#, elixir-autogen, elixir-format +msgid "Waiting for transaction's confirmation..." +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:138 +#, elixir-autogen, elixir-format +msgid "Wallet addresses" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky." +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:7 +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:7 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Watch list" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:73 +#, elixir-autogen, elixir-format +msgid "We recommend using flattened code. This is necessary if your code utilizes a library or inherits dependencies. Use the" +msgstr "" + +#: lib/block_scout_web/views/wei_helpers.ex:76 +#, elixir-autogen, elixir-format +msgid "Wei" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:100 +#, elixir-autogen, elixir-format +msgid "Write" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:95 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:34 +#: lib/block_scout_web/views/address_view.ex:371 +#, elixir-autogen, elixir-format +msgid "Write Contract" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:102 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:48 +#: lib/block_scout_web/views/address_view.ex:372 +#, elixir-autogen, elixir-format +msgid "Write Proxy" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:14 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:14 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:47 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:43 +#, elixir-autogen, elixir-format +msgid "Yes" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/index.html.eex:18 +#, elixir-autogen, elixir-format +msgid "You can create 3 API keys per account." +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:18 +#, elixir-autogen, elixir-format +msgid "You can create up to 15 Custom ABIs per account." +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:11 +#, elixir-autogen, elixir-format +msgid "You can request a public category tag which is displayed to all Blockscout users. Public tags may be added to contract or external addresses, and any associated transactions will inherit that tag. Clicking a tag opens a page with related information and helps provide context and data organization. Requests are sent to a moderator for review and approval. This process can take several days." +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:14 +#, elixir-autogen, elixir-format +msgid "You don't have address tags yet" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:14 +#, elixir-autogen, elixir-format +msgid "You don't have addresses on you watchlist yet" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:14 +#, elixir-autogen, elixir-format +msgid "You don't have transaction tags yet" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Your name" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Your name*" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:111 +#, elixir-autogen, elixir-format +msgid "at" +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:52 +#, elixir-autogen, elixir-format +msgid "balance of the address" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:411 +#, elixir-autogen, elixir-format +msgid "burned for this transaction. Equals Block Base Fee per Gas * Gas Used." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:215 +#, elixir-autogen, elixir-format +msgid "burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)." +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:27 +#, elixir-autogen, elixir-format +msgid "button" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:230 +#, elixir-autogen, elixir-format +msgid "created" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:12 +#, elixir-autogen, elixir-format +msgid "custom RPC" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:150 +#, elixir-autogen, elixir-format +msgid "doesn't include ERC20, ERC721, ERC1155 tokens)." +msgstr "" + +#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:13 +#, elixir-autogen, elixir-format +msgid "elements are displayed" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:38 +#, elixir-autogen, elixir-format +msgid "fallback" +msgstr "" + +#: lib/block_scout_web/views/address_contract_view.ex:24 +#, elixir-autogen, elixir-format +msgid "false" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:10 +#, elixir-autogen, elixir-format +msgid "here" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:9 +#, elixir-autogen, elixir-format +msgid "here." +msgstr "" + +#: lib/block_scout_web/templates/transaction/invalid.html.eex:8 +#, elixir-autogen, elixir-format +msgid "is not a valid transaction hash" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_function_response.html.eex:3 +#, elixir-autogen, elixir-format +msgid "method Response" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:41 +#, elixir-autogen, elixir-format +msgid "of" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16 +#, elixir-autogen, elixir-format +msgid "page" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:40 +#, elixir-autogen, elixir-format +msgid "receive" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:58 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:69 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:81 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:70 +#, elixir-autogen, elixir-format +msgid "required" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:59 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:70 +#, elixir-autogen, elixir-format +msgid "string" +msgstr "" + +#: lib/block_scout_web/views/address_contract_view.ex:23 +#, elixir-autogen, elixir-format +msgid "true" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:73 +#, elixir-autogen, elixir-format +msgid "truffle flattener" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_first.html.eex:5 +#, elixir-autogen, elixir-format +msgid ") may be added for each contract. Click the Add Library button to add an additional one." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_first.html.eex:5 +#, elixir-autogen, elixir-format +msgid "A library name called in the .sol file. Multiple libraries (up to " +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_address.html.eex:4 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_name.html.eex:4 +#, elixir-autogen, elixir-format +msgid "Library" +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/_state_change.html.eex:9 +#, elixir-autogen, elixir-format +msgid "Address used in token mintings and burnings." +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/index.html.eex:35 +#, elixir-autogen, elixir-format +msgid "Balance after" +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/index.html.eex:32 +#, elixir-autogen, elixir-format +msgid "Balance before" +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/_state_change.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Burn address" +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/index.html.eex:38 +#, elixir-autogen, elixir-format +msgid "Change" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_tabs.html.eex:29 +#: lib/block_scout_web/templates/transaction_state/index.html.eex:6 +#: lib/block_scout_web/views/transaction_view.ex:516 +#, elixir-autogen, elixir-format +msgid "State changes" +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/index.html.eex:13 +#, elixir-autogen, elixir-format +msgid "The changes from this transaction have not yet happened since the transaction is still pending." +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/index.html.eex:17 +#, elixir-autogen, elixir-format +msgid "This transaction hasn't changed state." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:120 +#, elixir-autogen, elixir-format +msgid "Contract was precompiled and created at genesis or contract creation transaction is missing" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:29 +#, elixir-autogen, elixir-format +msgid "Blockchain" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:72 +#, elixir-autogen, elixir-format +msgid "Constructor args" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:41 +#, elixir-autogen, elixir-format +msgid "Contract name or address" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Contracts" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Filter by compiler:" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:13 +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:26 +#, elixir-autogen, elixir-format +msgid "Last 24h" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:78 +#, elixir-autogen, elixir-format +msgid "Market cap" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_contract.html.eex:21 +#: lib/block_scout_web/templates/verified_contracts/_contract.html.eex:58 +#, elixir-autogen, elixir-format +msgid "N/A" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:69 +#, elixir-autogen, elixir-format +msgid "Optimization" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_contract.html.eex:28 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:26 +#: lib/block_scout_web/views/verified_contracts_view.ex:9 +#, elixir-autogen, elixir-format +msgid "Solidity" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:93 +#, elixir-autogen, elixir-format +msgid "There are no verified contracts." +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:9 +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Total" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:60 +#, elixir-autogen, elixir-format +msgid "Txns" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:18 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:6 +#, elixir-autogen, elixir-format +msgid "Verified Contracts" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:68 +#, elixir-autogen, elixir-format +msgid "Verified contracts" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_metatags.html.eex:2 +#, elixir-autogen, elixir-format +msgid "Verified contracts - %{subnetwork} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_metatags.html.eex:8 +#, elixir-autogen, elixir-format +msgid "View the verified contracts on %{subnetwork}" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_contract.html.eex:28 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:32 +#: lib/block_scout_web/views/verified_contracts_view.ex:10 +#, elixir-autogen, elixir-format +msgid "Vyper" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_metatags.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Verifed contracts, %{subnetwork}, %{coin}" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:44 +#, elixir-autogen, elixir-format +msgid "Blocks With Internal Transactions Indexed" +msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po new file mode 100644 index 0000000..ca77f22 --- /dev/null +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -0,0 +1,3426 @@ +#: lib/block_scout_web/views/address_token_balance_view.ex:10 +#, elixir-autogen, elixir-format +msgid "%{count} token" +msgid_plural "%{count} tokens" +msgstr[0] "" +msgstr[1] "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:29 +#, elixir-autogen, elixir-format +msgid "%{count} transaction" +msgid_plural "%{count} transactions" +msgstr[0] "" +msgstr[1] "" + +#: lib/block_scout_web/templates/common_components/_minimal_proxy_pattern.html.eex:9 +#, elixir-autogen, elixir-format +msgid " - minimal bytecode implementation that delegates all calls to a known address" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:14 +#, elixir-autogen, elixir-format +msgid " is recommended." +msgstr "" + +#: lib/block_scout_web/templates/address/_metatags.html.eex:3 +#, elixir-autogen, elixir-format +msgid "%{address} - %{subnetwork} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:12 +#, elixir-autogen, elixir-format +msgid "%{block_type} Details" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:55 +#, elixir-autogen, elixir-format +msgid "%{block_type} Height" +msgstr "" + +#: lib/block_scout_web/templates/block/index.html.eex:7 +#, elixir-autogen, elixir-format +msgid "%{block_type}s" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:85 +#, elixir-autogen, elixir-format +msgid "%{count} Transaction" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:87 +#: lib/block_scout_web/templates/chain/_block.html.eex:11 +#, elixir-autogen, elixir-format +msgid "%{count} Transactions" +msgstr "" + +#: lib/block_scout_web/templates/chain/_metatags.html.eex:2 +#, elixir-autogen, elixir-format +msgid "%{subnetwork} %{network} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/layout/_default_title.html.eex:2 +#, elixir-autogen, elixir-format +msgid "%{subnetwork} Explorer - BlockScout" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:350 +#, elixir-autogen, elixir-format +msgid "(Awaiting internal transactions for status)" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:59 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:70 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:82 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:104 +#, elixir-autogen, elixir-format +msgid "(query)" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:230 +#, elixir-autogen, elixir-format +msgid "- We're indexing this chain right now. Some of the counts may be inaccurate." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:195 +#, elixir-autogen, elixir-format +msgid "64-bit hash of value verifying proof-of-work (note: null for POA chains)." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:97 +#: lib/block_scout_web/templates/transaction_state/_state_change.html.eex:23 +#, elixir-autogen, elixir-format +msgid "A block producer who successfully included the block onto the blockchain." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:73 +#, elixir-autogen, elixir-format +msgid "A string with the name of the action to be invoked." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:62 +#, elixir-autogen, elixir-format +msgid "A string with the name of the module to be invoked." +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:24 +#, elixir-autogen, elixir-format +msgid "ABI" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_constructor_args.html.eex:3 +#, elixir-autogen, elixir-format +msgid "ABI-encoded Constructor Arguments (if required by the contract)" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/index.html.eex:4 +#, elixir-autogen, elixir-format +msgid "API Documentation" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_metatags.html.eex:4 +#, elixir-autogen, elixir-format +msgid "API endpoints for the %{subnetwork}" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_metatags.html.eex:2 +#, elixir-autogen, elixir-format +msgid "API for the %{subnetwork} - BlockScout" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:7 +#: lib/block_scout_web/templates/account/api_key/form.html.eex:13 +#: lib/block_scout_web/templates/account/api_key/form.html.eex:14 +#: lib/block_scout_web/templates/account/api_key/index.html.eex:29 +#, elixir-autogen, elixir-format +msgid "API key" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/index.html.eex:7 +#: lib/block_scout_web/templates/account/common/_nav.html.eex:16 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:17 +#, elixir-autogen, elixir-format +msgid "API keys" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:100 +#, elixir-autogen, elixir-format +msgid "APIs" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:24 +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:24 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:69 +#, elixir-autogen, elixir-format +msgid "Action" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:25 +#, elixir-autogen, elixir-format +msgid "Actions" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:425 +#, elixir-autogen, elixir-format +msgid "Actual gas amount used by the transaction." +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:7 +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:8 +#: lib/block_scout_web/templates/layout/_add_chain_to_mm.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Add" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/index.html.eex:44 +#, elixir-autogen, elixir-format +msgid "Add API key" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:82 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:76 +#, elixir-autogen, elixir-format +msgid "Add Contract Libraries" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:44 +#, elixir-autogen, elixir-format +msgid "Add Custom ABI" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:93 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:87 +#, elixir-autogen, elixir-format +msgid "Add Library" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:38 +#, elixir-autogen, elixir-format +msgid "Add address" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:7 +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:37 +#, elixir-autogen, elixir-format +msgid "Add address tag" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Add address to the Watch list" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:7 +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:37 +#, elixir-autogen, elixir-format +msgid "Add transaction tag" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:11 +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:23 +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:23 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:12 +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_address.html.eex:4 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20 +#: lib/block_scout_web/templates/transaction_state/index.html.eex:29 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:54 +#: lib/block_scout_web/views/address_view.ex:107 +#, elixir-autogen, elixir-format +msgid "Address" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:217 +#, elixir-autogen, elixir-format +msgid "Address (external or contract) receiving the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:199 +#, elixir-autogen, elixir-format +msgid "Address (external or contract) sending the transaction." +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:10 +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:7 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Address Tags" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:150 +#, elixir-autogen, elixir-format +msgid "Address balance in" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:51 +#, elixir-autogen, elixir-format +msgid "Address of the token contract" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/address_field.html.eex:2 +#, elixir-autogen, elixir-format +msgid "Address*" +msgstr "" + +#: lib/block_scout_web/templates/address/index.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Addresses" +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:26 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:28 +#: lib/block_scout_web/templates/address_transaction/index.html.eex:22 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:82 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:20 +#: lib/block_scout_web/views/address_internal_transaction_view.ex:11 +#: lib/block_scout_web/views/address_token_transfer_view.ex:11 +#: lib/block_scout_web/views/address_transaction_view.ex:11 +#: lib/block_scout_web/views/verified_contracts_view.ex:11 +#, elixir-autogen, elixir-format +msgid "All" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13 +#, elixir-autogen, elixir-format +msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:27 +#, elixir-autogen, elixir-format +msgid "All metadata displayed below is from that contract. In order to verify current contract, click" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:175 +#, elixir-autogen, elixir-format +msgid "All tokens in the account and total value." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:411 +#, elixir-autogen, elixir-format +msgid "Amount of" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:236 +#, elixir-autogen, elixir-format +msgid "Amount of distributed reward. Miners receive a static block reward + Tx fees + uncle fees." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Anything not in this list is not supported. Click on the method to be taken to the documentation for that method, and check the notes section for any potential differences." +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:128 +#, elixir-autogen, elixir-format +msgid "Apps" +msgstr "" + +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:21 +#, elixir-autogen, elixir-format +msgid "Average" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:100 +#, elixir-autogen, elixir-format +msgid "Average block time" +msgstr "" + +#: lib/block_scout_web/templates/page_not_found/index.html.eex:9 +#: lib/block_scout_web/templates/transaction/not_found.html.eex:30 +#, elixir-autogen, elixir-format +msgid "Back Home" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:25 +#, elixir-autogen, elixir-format +msgid "Back to API keys (Cancel)" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Back to Address Tags (Cancel)" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:30 +#, elixir-autogen, elixir-format +msgid "Back to Custom ABI (Cancel)" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Back to Transaction Tags (Cancel)" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:81 +#, elixir-autogen, elixir-format +msgid "Back to Watch list (Cancel)" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:24 +#: lib/block_scout_web/templates/address/overview.html.eex:151 +#: lib/block_scout_web/templates/address_token/overview.html.eex:51 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:57 +#, elixir-autogen, elixir-format +msgid "Balance" +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Balances" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:207 +#, elixir-autogen, elixir-format +msgid "Base Fee per Gas" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:5 +#: lib/block_scout_web/templates/api_docs/index.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Base URL:" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:446 +#, elixir-autogen, elixir-format +msgid "Binary data included with the transaction. See input / logs below for additional info." +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/_coin_balances.html.eex:8 +#: lib/block_scout_web/templates/block/overview.html.eex:29 +#: lib/block_scout_web/templates/transaction/overview.html.eex:158 +#, elixir-autogen, elixir-format +msgid "Block" +msgstr "" + +#: lib/block_scout_web/templates/block/_link.html.eex:2 +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:32 +#: lib/block_scout_web/templates/tokens/transfer/_token_transfer.html.eex:43 +#, elixir-autogen, elixir-format +msgid "Block #%{number}" +msgstr "" + +#: lib/block_scout_web/templates/block/_metatags.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Block %{block_number} - %{subnetwork} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/block_transaction/404.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Block Details" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:53 +#, elixir-autogen, elixir-format +msgid "Block Height" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:43 +#, elixir-autogen, elixir-format +msgid "Block Mined, awaiting import..." +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:34 +#, elixir-autogen, elixir-format +msgid "Block Pending" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:158 +#, elixir-autogen, elixir-format +msgid "Block difficulty for miner, used to calibrate block generation time (Note: constant in POA based networks)." +msgstr "" + +#: lib/block_scout_web/views/block_transaction_view.ex:15 +#, elixir-autogen, elixir-format +msgid "Block not found, please try again later." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:157 +#, elixir-autogen, elixir-format +msgid "Block number containing the transaction." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:258 +#, elixir-autogen, elixir-format +msgid "Block number in which the address was updated." +msgstr "" + +#: lib/block_scout_web/templates/chain/_metatags.html.eex:4 +#, elixir-autogen, elixir-format +msgid "BlockScout provides analytics data, API, and Smart Contract tools for the %{subnetwork}" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:153 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:34 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:38 +#, elixir-autogen, elixir-format +msgid "Blocks" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:42 +#, elixir-autogen, elixir-format +msgid "Blocks Indexed" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:48 +#: lib/block_scout_web/templates/address/overview.html.eex:276 +#: lib/block_scout_web/templates/address_validation/index.html.eex:11 +#: lib/block_scout_web/views/address_view.ex:374 +#, elixir-autogen, elixir-format +msgid "Blocks Validated" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks." +msgstr "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:64 +#: lib/block_scout_web/templates/block/overview.html.eex:216 +#, elixir-autogen, elixir-format +msgid "Burnt Fees" +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:65 +#, elixir-autogen, elixir-format +msgid "CRC Worth" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_csv_export_button.html.eex:2 +#, elixir-autogen, elixir-format +msgid "CSV" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:10 +#: lib/block_scout_web/views/internal_transaction_view.ex:21 +#, elixir-autogen, elixir-format +msgid "Call" +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:22 +#, elixir-autogen, elixir-format +msgid "Call Code" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:62 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:120 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:111 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:41 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:107 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:55 +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:51 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54 +#, elixir-autogen, elixir-format +msgid "Cancel" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:41 +#, elixir-autogen, elixir-format +msgid "Chat (#blockscout)" +msgstr "" + +#: lib/block_scout_web/views/block_view.ex:65 +#, elixir-autogen, elixir-format +msgid "Chore Reward" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:137 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:106 +#, elixir-autogen, elixir-format +msgid "Clear" +msgstr "" + +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37 +#: lib/block_scout_web/templates/common_components/_modal_qr_code.html.eex:6 +#: lib/block_scout_web/templates/common_components/_modal_qr_code.html.eex:14 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:84 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:92 +#, elixir-autogen, elixir-format +msgid "Close" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:58 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:165 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:126 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:149 +#: lib/block_scout_web/views/address_view.ex:367 +#, elixir-autogen, elixir-format +msgid "Code" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:34 +#: lib/block_scout_web/views/address_view.ex:373 +#, elixir-autogen, elixir-format +msgid "Coin Balance History" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55 +#, elixir-autogen, elixir-format +msgid "Collapse" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Company name" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:32 +#, elixir-autogen, elixir-format +msgid "Company website" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_compiler_field.html.eex:3 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:63 +#, elixir-autogen, elixir-format +msgid "Compiler" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:65 +#, elixir-autogen, elixir-format +msgid "Compiler version" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:343 +#, elixir-autogen, elixir-format +msgid "Confirmed" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:124 +#, elixir-autogen, elixir-format +msgid "Confirmed by " +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:190 +#, elixir-autogen, elixir-format +msgid "Confirmed within" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:2 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:6 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:2 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:4 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:6 +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:4 +#: lib/block_scout_web/templates/tokens/holder/index.html.eex:16 +#, elixir-autogen, elixir-format +msgid "Connection Lost" +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:12 +#: lib/block_scout_web/templates/block/index.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Connection Lost, click to load newer blocks" +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Connection Lost, click to load newer internal transactions" +msgstr "" + +#: lib/block_scout_web/templates/address_transaction/index.html.eex:11 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:16 +#: lib/block_scout_web/templates/transaction/index.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Connection Lost, click to load newer transactions" +msgstr "" + +#: lib/block_scout_web/templates/address_validation/index.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Connection Lost, click to load newer validations" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:90 +#, elixir-autogen, elixir-format +msgid "Constructor Arguments" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:52 +#: lib/block_scout_web/templates/transaction/overview.html.eex:227 +#, elixir-autogen, elixir-format +msgid "Contract" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:122 +#, elixir-autogen, elixir-format +msgid "Contract ABI" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:18 +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:29 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_address_field.html.eex:3 +#: lib/block_scout_web/views/address_view.ex:105 +#, elixir-autogen, elixir-format +msgid "Contract Address" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:16 +#: lib/block_scout_web/views/address_view.ex:45 +#: lib/block_scout_web/views/address_view.ex:79 +#, elixir-autogen, elixir-format +msgid "Contract Address Pending" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:458 +#, elixir-autogen, elixir-format +msgid "Contract Call" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:455 +#, elixir-autogen, elixir-format +msgid "Contract Creation" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:138 +#: lib/block_scout_web/templates/address_contract/index.html.eex:153 +#, elixir-autogen, elixir-format +msgid "Contract Creation Code" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:86 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:80 +#, elixir-autogen, elixir-format +msgid "Contract Libraries" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:75 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_contract_name_field.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Contract Name" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:25 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:57 +#, elixir-autogen, elixir-format +msgid "Contract name:" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:100 +#, elixir-autogen, elixir-format +msgid "Contract source code" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:144 +#, elixir-autogen, elixir-format +msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified." +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:40 +#, elixir-autogen, elixir-format +msgid "Contribute" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:124 +#, elixir-autogen, elixir-format +msgid "Copy ABI" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/row.html.eex:6 +#: lib/block_scout_web/templates/account/api_key/row.html.eex:6 +#, elixir-autogen, elixir-format +msgid "Copy API key" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/row.html.eex:8 +#: lib/block_scout_web/templates/account/tag_address/row.html.eex:8 +#: lib/block_scout_web/templates/account/tag_transaction/row.html.eex:11 +#: lib/block_scout_web/templates/account/tag_transaction/row.html.eex:11 +#: lib/block_scout_web/templates/account/watchlist_address/row.html.eex:7 +#: lib/block_scout_web/templates/address/overview.html.eex:38 +#: lib/block_scout_web/templates/address/overview.html.eex:39 +#: lib/block_scout_web/templates/block/overview.html.eex:104 +#: lib/block_scout_web/templates/block/overview.html.eex:105 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:43 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:44 +#, elixir-autogen, elixir-format +msgid "Copy Address" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/row.html.eex:6 +#: lib/block_scout_web/templates/account/custom_abi/row.html.eex:6 +#, elixir-autogen, elixir-format +msgid "Copy Contract Address" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:140 +#: lib/block_scout_web/templates/address_contract/index.html.eex:156 +#, elixir-autogen, elixir-format +msgid "Copy Contract Creation Code" +msgstr "" + +#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:19 +#, elixir-autogen, elixir-format +msgid "Copy Decompiled Contract Code" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:177 +#: lib/block_scout_web/templates/address_contract/index.html.eex:187 +#, elixir-autogen, elixir-format +msgid "Copy Deployed ByteCode" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/row.html.eex:7 +#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:17 +#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:18 +#: lib/block_scout_web/templates/transaction/overview.html.eex:207 +#: lib/block_scout_web/templates/transaction/overview.html.eex:208 +#, elixir-autogen, elixir-format +msgid "Copy From Address" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:129 +#: lib/block_scout_web/templates/block/overview.html.eex:130 +#, elixir-autogen, elixir-format +msgid "Copy Hash" +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Copy Metadata" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:149 +#: lib/block_scout_web/templates/block/overview.html.eex:150 +#, elixir-autogen, elixir-format +msgid "Copy Parent Hash" +msgstr "" + +#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Copy Raw Trace" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:102 +#: lib/block_scout_web/templates/address_contract/index.html.eex:113 +#, elixir-autogen, elixir-format +msgid "Copy Source Code" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:34 +#: lib/block_scout_web/templates/transaction/_total_transfers_from_to.html.eex:35 +#: lib/block_scout_web/templates/transaction/overview.html.eex:234 +#: lib/block_scout_web/templates/transaction/overview.html.eex:235 +#: lib/block_scout_web/templates/transaction/overview.html.eex:242 +#: lib/block_scout_web/templates/transaction/overview.html.eex:243 +#, elixir-autogen, elixir-format +msgid "Copy To Address" +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:32 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:33 +#, elixir-autogen, elixir-format +msgid "Copy Token ID" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:87 +#, elixir-autogen, elixir-format +msgid "Copy Transaction Hash" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:88 +#, elixir-autogen, elixir-format +msgid "Copy Txn Hash" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:472 +#, elixir-autogen, elixir-format +msgid "Copy Txn Hex Input" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:478 +#, elixir-autogen, elixir-format +msgid "Copy Txn UTF-8 Input" +msgstr "" + +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:20 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:41 +#: lib/block_scout_web/templates/transaction/overview.html.eex:471 +#: lib/block_scout_web/templates/transaction/overview.html.eex:477 +#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Copy Value" +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:25 +#, elixir-autogen, elixir-format +msgid "Create" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:12 +#, elixir-autogen, elixir-format +msgid "Create a Custom ABI to interact with contracts." +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/index.html.eex:12 +#, elixir-autogen, elixir-format +msgid "Create an API key to use with your RPC и EthRPC API requests." +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:26 +#, elixir-autogen, elixir-format +msgid "Create2" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:102 +#, elixir-autogen, elixir-format +msgid "Creator" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:146 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:116 +#, elixir-autogen, elixir-format +msgid "Curl" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:97 +#, elixir-autogen, elixir-format +msgid "Current transaction state: Success, Failed (Error), or Pending (In Process)" +msgstr "" + +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:20 +#: lib/block_scout_web/templates/address_write_contract/index.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Custom" +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:19 +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:8 +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:7 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Custom ABI" +msgstr "" + +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:25 +#: lib/block_scout_web/templates/address_write_contract/index.html.eex:23 +#, elixir-autogen, elixir-format +msgid "Custom ABI from account" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:69 +#, elixir-autogen, elixir-format +msgid "Daily Transactions" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:101 +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:23 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:121 +#, elixir-autogen, elixir-format +msgid "Data" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:70 +#, elixir-autogen, elixir-format +msgid "Date & time at which block was produced." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:176 +#, elixir-autogen, elixir-format +msgid "Date & time of transaction inclusion, including length of time for confirmation." +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:52 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:131 +#, elixir-autogen, elixir-format +msgid "Decimals" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:32 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:53 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:34 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:57 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:73 +#, elixir-autogen, elixir-format +msgid "Decoded" +msgstr "" + +#: lib/block_scout_web/views/address_view.ex:368 +#, elixir-autogen, elixir-format +msgid "Decompiled Code" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:75 +#, elixir-autogen, elixir-format +msgid "Decompiled code" +msgstr "" + +#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:17 +#, elixir-autogen, elixir-format +msgid "Decompiled contract code" +msgstr "" + +#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Decompiler version" +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:23 +#, elixir-autogen, elixir-format +msgid "Delegate Call" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:175 +#: lib/block_scout_web/templates/address_contract/index.html.eex:183 +#, elixir-autogen, elixir-format +msgid "Deployed ByteCode" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:53 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:188 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:60 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:150 +#, elixir-autogen, elixir-format +msgid "Description" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:56 +#, elixir-autogen, elixir-format +msgid "Description*" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:30 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:166 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:127 +#, elixir-autogen, elixir-format +msgid "Details" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:159 +#, elixir-autogen, elixir-format +msgid "Difficulty" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:145 +#, elixir-autogen, elixir-format +msgid "Displaying the init data provided of the creating transaction." +msgstr "" + +#: lib/block_scout_web/templates/csv_export/index.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Download" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:72 +#, elixir-autogen, elixir-format +msgid "Drop all Solidity contract source files into the drop zone." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Drop sources and metadata JSON file or click here" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:67 +#, elixir-autogen, elixir-format +msgid "Drop sources or click here" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:28 +#, elixir-autogen, elixir-format +msgid "Drop the standard input JSON file or click here" +msgstr "" + +#: lib/block_scout_web/templates/transaction/not_found.html.eex:22 +#, elixir-autogen, elixir-format +msgid "During times when the network is busy (i.e during ICOs) it can take a while for your transaction to propagate through the network and for us to index it." +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:27 +#, elixir-autogen, elixir-format +msgid "E-mail*" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_minimal_proxy_pattern.html.eex:6 +#, elixir-autogen, elixir-format +msgid "EIP-1167" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:215 +#, elixir-autogen, elixir-format +msgid "ERC-1155 " +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:213 +#, elixir-autogen, elixir-format +msgid "ERC-20 " +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:40 +#, elixir-autogen, elixir-format +msgid "ERC-20 tokens (beta)" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:214 +#, elixir-autogen, elixir-format +msgid "ERC-721 " +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:53 +#, elixir-autogen, elixir-format +msgid "ERC-721, ERC-1155 tokens (NFT) (beta)" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:4 +#, elixir-autogen, elixir-format +msgid "ETH RPC API Documentation" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:76 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:26 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:22 +#, elixir-autogen, elixir-format +msgid "EVM Version" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:30 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:26 +#, elixir-autogen, elixir-format +msgid "EVM version details" +msgstr "" + +#: lib/block_scout_web/views/block_transaction_view.ex:7 +#, elixir-autogen, elixir-format +msgid "Easy Cowboy! This block does not exist yet!" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/row.html.eex:16 +#: lib/block_scout_web/templates/account/custom_abi/row.html.eex:16 +#: lib/block_scout_web/templates/account/watchlist_address/row.html.eex:27 +#, elixir-autogen, elixir-format +msgid "Edit" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Edit Watch list address" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:71 +#, elixir-autogen, elixir-format +msgid "Email notifications" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Emission Contract" +msgstr "" + +#: lib/block_scout_web/views/block_view.ex:73 +#, elixir-autogen, elixir-format +msgid "Emission Reward" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:68 +#, elixir-autogen, elixir-format +msgid "Enter the Solidity Contract Code" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Enter the Vyper Contract Code" +msgstr "" + +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:11 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Error" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_tile.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Error in internal transactions" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:33 +#, elixir-autogen, elixir-format +msgid "Error rendering value" +msgstr "" + +#: lib/block_scout_web/templates/address/_balance_dropdown.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Error trying to fetch balances." +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:354 +#, elixir-autogen, elixir-format +msgid "Error: %{reason}" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:352 +#, elixir-autogen, elixir-format +msgid "Error: (Awaiting internal transactions for reason)" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:120 +#, elixir-autogen, elixir-format +msgid "Error: Could not determine contract creator." +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:114 +#, elixir-autogen, elixir-format +msgid "Eth RPC" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:211 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:164 +#, elixir-autogen, elixir-format +msgid "Example Value" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:128 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:99 +#, elixir-autogen, elixir-format +msgid "Execute" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:55 +#, elixir-autogen, elixir-format +msgid "Expand" +msgstr "" + +#: lib/block_scout_web/templates/csv_export/index.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Export Data" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:212 +#, elixir-autogen, elixir-format +msgid "External libraries" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:40 +#, elixir-autogen, elixir-format +msgid "Failed to decode input data." +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:35 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:37 +#, elixir-autogen, elixir-format +msgid "Failed to decode log data." +msgstr "" + +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Fast" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:248 +#, elixir-autogen, elixir-format +msgid "Fetching gas used..." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:112 +#, elixir-autogen, elixir-format +msgid "Fetching holders..." +msgstr "" + +#: lib/block_scout_web/templates/address/_balance_dropdown.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Fetching tokens..." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:195 +#: lib/block_scout_web/templates/address/overview.html.eex:203 +#, elixir-autogen, elixir-format +msgid "Fetching transactions..." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:222 +#: lib/block_scout_web/templates/address/overview.html.eex:230 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:123 +#, elixir-autogen, elixir-format +msgid "Fetching transfers..." +msgstr "" + +#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:16 +#, elixir-autogen, elixir-format +msgid "For any existing contracts in the database, insert all ABI entries into the contract_methods table. Use this in case you have verified smart contracts before early March 2019 and you want other contracts with the same functions to show those ABI's as candidate matches." +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:44 +#, elixir-autogen, elixir-format +msgid "Forked Blocks (Reorgs)" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:43 +#, elixir-autogen, elixir-format +msgid "Forum" +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:38 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:40 +#: lib/block_scout_web/templates/address_transaction/index.html.eex:34 +#: lib/block_scout_web/templates/transaction/overview.html.eex:200 +#: lib/block_scout_web/views/address_internal_transaction_view.ex:10 +#: lib/block_scout_web/views/address_token_transfer_view.ex:10 +#: lib/block_scout_web/views/address_transaction_view.ex:10 +#, elixir-autogen, elixir-format +msgid "From" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:18 +#, elixir-autogen, elixir-format +msgid "GET" +msgstr "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:67 +#: lib/block_scout_web/templates/block/overview.html.eex:187 +#: lib/block_scout_web/templates/transaction/overview.html.eex:373 +#, elixir-autogen, elixir-format +msgid "Gas Limit" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:353 +#, elixir-autogen, elixir-format +msgid "Gas Price" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:241 +#: lib/block_scout_web/templates/block/_tile.html.eex:73 +#: lib/block_scout_web/templates/block/overview.html.eex:178 +#, elixir-autogen, elixir-format +msgid "Gas Used" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:426 +#, elixir-autogen, elixir-format +msgid "Gas Used by Transaction" +msgstr "" + +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:3 +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Gas tracker" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:240 +#, elixir-autogen, elixir-format +msgid "Gas used by the address." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:60 +#, elixir-autogen, elixir-format +msgid "Genesis Block" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Github" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:8 +#, elixir-autogen, elixir-format +msgid "Go to" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:104 +#, elixir-autogen, elixir-format +msgid "GraphQL" +msgstr "" + +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:11 +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:20 +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:21 +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:22 +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:38 +#: lib/block_scout_web/views/block_view.ex:22 +#: lib/block_scout_web/views/wei_helpers.ex:77 +#, elixir-autogen, elixir-format +msgid "Gwei" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:123 +#, elixir-autogen, elixir-format +msgid "Hash" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:454 +#: lib/block_scout_web/templates/transaction/overview.html.eex:458 +#, elixir-autogen, elixir-format +msgid "Hex (Default)" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:108 +#, elixir-autogen, elixir-format +msgid "Holders" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:11 +#, elixir-autogen, elixir-format +msgid "However, in general, the" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19 +#, elixir-autogen, elixir-format +msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts." +msgstr "" + +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:42 +#: lib/block_scout_web/templates/transaction/_tile.html.eex:92 +#, elixir-autogen, elixir-format +msgid "IN" +msgstr "" + +#: lib/block_scout_web/templates/transaction/not_found.html.eex:26 +#, elixir-autogen, elixir-format +msgid "If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:52 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:48 +#, elixir-autogen, elixir-format +msgid "If you enabled optimization during compilation, select yes." +msgstr "" + +#: lib/block_scout_web/templates/transaction/not_found.html.eex:12 +#, elixir-autogen, elixir-format +msgid "If you have just submitted this transaction please wait for at least 30 seconds before refreshing this page." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:133 +#, elixir-autogen, elixir-format +msgid "Implementation" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:132 +#, elixir-autogen, elixir-format +msgid "Implementation address of the proxy contract." +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:30 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:43 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:56 +#, elixir-autogen, elixir-format +msgid "Incoming" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:438 +#, elixir-autogen, elixir-format +msgid "Index position of Transaction in the block." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:249 +#, elixir-autogen, elixir-format +msgid "Index position(s) of referenced stale blocks." +msgstr "" + +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:6 +#, elixir-autogen, elixir-format +msgid "Indexed?" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Input" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:219 +#, elixir-autogen, elixir-format +msgid "Interacted With (To)" +msgstr "" + +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Internal Transaction" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:28 +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:17 +#: lib/block_scout_web/templates/transaction/_tabs.html.eex:11 +#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:6 +#: lib/block_scout_web/views/address_view.ex:364 +#: lib/block_scout_web/views/transaction_view.ex:513 +#, elixir-autogen, elixir-format +msgid "Internal Transactions" +msgstr "" + +#: lib/block_scout_web/templates/transaction/invalid.html.eex:6 +#, elixir-autogen, elixir-format +msgid "Invalid Transaction Hash" +msgstr "" + +#: lib/block_scout_web/templates/tokens/inventory/index.html.eex:16 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:19 +#: lib/block_scout_web/views/tokens/overview_view.ex:42 +#, elixir-autogen, elixir-format +msgid "Inventory" +msgstr "" + +#: lib/block_scout_web/templates/transaction/not_found.html.eex:16 +#, elixir-autogen, elixir-format +msgid "It could still be in the TX Pool of a different node, waiting to be broadcasted." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:259 +#, elixir-autogen, elixir-format +msgid "Last Balance Update" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/index.html.eex:12 +#: lib/block_scout_web/templates/account/api_key/index.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Learn more" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:45 +#, elixir-autogen, elixir-format +msgid "Less than" +msgstr "" + +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:24 +#, elixir-autogen, elixir-format +msgid "License Expires" +msgstr "" + +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:10 +#, elixir-autogen, elixir-format +msgid "License ID" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:305 +#, elixir-autogen, elixir-format +msgid "List of ERC-1155 tokens created in the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:289 +#, elixir-autogen, elixir-format +msgid "List of token burnt in the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:272 +#, elixir-autogen, elixir-format +msgid "List of token minted in the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:256 +#, elixir-autogen, elixir-format +msgid "List of token transferred in the transaction." +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Loading chart..." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:77 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:105 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:35 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:99 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:49 +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:45 +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:41 +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:49 +#: lib/block_scout_web/templates/address_read_proxy/index.html.eex:12 +#: lib/block_scout_web/templates/address_write_contract/index.html.eex:39 +#: lib/block_scout_web/templates/address_write_contract/index.html.eex:47 +#: lib/block_scout_web/templates/address_write_proxy/index.html.eex:12 +#: lib/block_scout_web/templates/tokens/contract/index.html.eex:17 +#, elixir-autogen, elixir-format +msgid "Loading..." +msgstr "" + +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:2 +#, elixir-autogen, elixir-format +msgid "Log Data" +msgstr "" + +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:131 +#, elixir-autogen, elixir-format +msgid "Log Index" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:41 +#: lib/block_scout_web/templates/address_logs/index.html.eex:10 +#: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 +#: lib/block_scout_web/templates/transaction_log/index.html.eex:8 +#: lib/block_scout_web/views/address_view.ex:375 +#: lib/block_scout_web/views/transaction_view.ex:514 +#, elixir-autogen, elixir-format +msgid "Logs" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:52 +#, elixir-autogen, elixir-format +msgid "Main Networks" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:52 +#: lib/block_scout_web/templates/layout/app.html.eex:46 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:84 +#: lib/block_scout_web/views/address_view.ex:145 +#, elixir-autogen, elixir-format +msgid "Market Cap" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:382 +#, elixir-autogen, elixir-format +msgid "Max Fee per Gas" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:392 +#, elixir-autogen, elixir-format +msgid "Max Priority Fee per Gas" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:320 +#, elixir-autogen, elixir-format +msgid "Max of" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:372 +#, elixir-autogen, elixir-format +msgid "Maximum gas amount approved for the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:381 +#, elixir-autogen, elixir-format +msgid "Maximum total amount per unit of gas a user is willing to pay for a transaction, including base fee and priority fee." +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/metadata/index.html.eex:18 +#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:10 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:198 +#, elixir-autogen, elixir-format +msgid "Metadata" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Method Id" +msgstr "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:41 +#: lib/block_scout_web/templates/block/overview.html.eex:98 +#: lib/block_scout_web/templates/chain/_block.html.eex:16 +#: lib/block_scout_web/templates/transaction_state/_state_change.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Miner" +msgstr "" + +#: lib/block_scout_web/views/block_view.ex:63 +#: lib/block_scout_web/views/block_view.ex:68 +#, elixir-autogen, elixir-format +msgid "Miner Reward" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_minimal_proxy_pattern.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Minimal Proxy Contract for" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:206 +#, elixir-autogen, elixir-format +msgid "Minimum fee required per unit of gas. Fee adjusts based on network congestion." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:223 +#, elixir-autogen, elixir-format +msgid "Model" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:58 +#, elixir-autogen, elixir-format +msgid "Module" +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:12 +#, elixir-autogen, elixir-format +msgid "More internal transactions have come in" +msgstr "" + +#: lib/block_scout_web/templates/address_transaction/index.html.eex:46 +#: lib/block_scout_web/templates/chain/show.html.eex:216 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:13 +#: lib/block_scout_web/templates/transaction/index.html.eex:19 +#, elixir-autogen, elixir-format +msgid "More transactions have come in" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:63 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:74 +#, elixir-autogen, elixir-format +msgid "Must be set to:" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Must match the name specified in the code. For example, in contract MyContract {..} MyContract is the contract name." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:116 +#, elixir-autogen, elixir-format +msgid "N/A bytes" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:19 +#: lib/block_scout_web/templates/account/api_key/index.html.eex:28 +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:13 +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:28 +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:18 +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:22 +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:18 +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:22 +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:22 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:19 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_name.html.eex:4 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:52 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:59 +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:4 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21 +#, elixir-autogen, elixir-format +msgid "Name" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Name this API key" +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Name this Custom ABI" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:19 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Name this address" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:19 +#, elixir-autogen, elixir-format +msgid "Name this transaction" +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:44 +#, elixir-autogen, elixir-format +msgid "Net Worth" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:5 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:5 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:9 +#, elixir-autogen, elixir-format +msgid "New Smart Contract Verification" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:9 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:7 +#, elixir-autogen, elixir-format +msgid "New Solidity Smart Contract Verification" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:7 +#, elixir-autogen, elixir-format +msgid "New Vyper Smart Contract Verification" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:80 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:87 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:95 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:103 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:111 +#, elixir-autogen, elixir-format +msgid "Next" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:9 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:9 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:42 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:38 +#, elixir-autogen, elixir-format +msgid "No" +msgstr "" + +#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:23 +#, elixir-autogen, elixir-format +msgid "No trace entries found." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:196 +#: lib/block_scout_web/templates/transaction/overview.html.eex:436 +#, elixir-autogen, elixir-format +msgid "Nonce" +msgstr "" + +#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Not unique Token" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:107 +#, elixir-autogen, elixir-format +msgid "Number of accounts holding the token" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:275 +#, elixir-autogen, elixir-format +msgid "Number of blocks validated by this validator." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:130 +#, elixir-autogen, elixir-format +msgid "Number of digits that come after the decimal place when displaying token value" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:186 +#, elixir-autogen, elixir-format +msgid "Number of transactions related to this address." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:118 +#, elixir-autogen, elixir-format +msgid "Number of transfers for the token" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:213 +#, elixir-autogen, elixir-format +msgid "Number of transfers to/from this address." +msgstr "" + +#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:40 +#: lib/block_scout_web/templates/transaction/_tile.html.eex:88 +#, elixir-autogen, elixir-format +msgid "OUT" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Only the first" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:61 +#, elixir-autogen, elixir-format +msgid "Optimization enabled" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:70 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:58 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:54 +#, elixir-autogen, elixir-format +msgid "Optimization runs" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:76 +#, elixir-autogen, elixir-format +msgid "Other Explorers" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:35 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:48 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:61 +#, elixir-autogen, elixir-format +msgid "Outgoing" +msgstr "" + +#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Owner Address" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:73 +#, elixir-autogen, elixir-format +msgid "POA solidity flattener or the" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:19 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:26 +#, elixir-autogen, elixir-format +msgid "POST" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:41 +#, elixir-autogen, elixir-format +msgid "Page" +msgstr "" + +#: lib/block_scout_web/templates/page_not_found/index.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Page not found" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:33 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:40 +#, elixir-autogen, elixir-format +msgid "Parameters" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:139 +#, elixir-autogen, elixir-format +msgid "Parent Hash" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:62 +#: lib/block_scout_web/views/transaction_view.ex:349 +#: lib/block_scout_web/views/transaction_view.ex:387 +#, elixir-autogen, elixir-format +msgid "Pending" +msgstr "" + +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Pending Transactions" +msgstr "" + +#: lib/block_scout_web/templates/address/_custom_view_df_title.html.eex:9 +#: lib/block_scout_web/templates/address/_custom_view_df_title.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Play" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:68 +#, elixir-autogen, elixir-format +msgid "Please select notification methods:" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Please select what types of notifications you will receive:" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:438 +#, elixir-autogen, elixir-format +msgid "Position" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:254 +#, elixir-autogen, elixir-format +msgid "Position %{index}" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:32 +#, elixir-autogen, elixir-format +msgid "Potential matches from our contract method database:" +msgstr "" + +#: lib/block_scout_web/templates/layout/_search.html.eex:27 +#, elixir-autogen, elixir-format +msgid "Press / and focus will be moved to the search field" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:41 +#: lib/block_scout_web/templates/layout/app.html.eex:47 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:95 +#, elixir-autogen, elixir-format +msgid "Price" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:94 +#, elixir-autogen, elixir-format +msgid "Price per token on the exchanges" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:352 +#, elixir-autogen, elixir-format +msgid "Price per unit of gas specified by the sender. Higher gas prices can prioritize transaction inclusion during times of high usage." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:225 +#: lib/block_scout_web/templates/transaction/overview.html.eex:402 +#, elixir-autogen, elixir-format +msgid "Priority Fee / Tip" +msgstr "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:62 +#, elixir-autogen, elixir-format +msgid "Priority Fees" +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:4 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Profile" +msgstr "" + +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:19 +#, elixir-autogen, elixir-format +msgid "Public Tags" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Public tag" +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:22 +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Public tags" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:50 +#, elixir-autogen, elixir-format +msgid "Public tags* (2 tags maximum, please use \";\" as a divider)" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_btn_qr_code.html.eex:10 +#: lib/block_scout_web/templates/common_components/_modal_qr_code.html.eex:5 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:83 +#, elixir-autogen, elixir-format +msgid "QR Code" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:100 +#, elixir-autogen, elixir-format +msgid "Query" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:109 +#, elixir-autogen, elixir-format +msgid "RPC" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:447 +#, elixir-autogen, elixir-format +msgid "Raw Input" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_tabs.html.eex:24 +#: lib/block_scout_web/templates/transaction_raw_trace/index.html.eex:7 +#: lib/block_scout_web/views/transaction_view.ex:515 +#, elixir-autogen, elixir-format +msgid "Raw Trace" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:81 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:27 +#: lib/block_scout_web/views/address_view.ex:369 +#: lib/block_scout_web/views/tokens/overview_view.ex:41 +#, elixir-autogen, elixir-format +msgid "Read Contract" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:88 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:41 +#: lib/block_scout_web/views/address_view.ex:370 +#, elixir-autogen, elixir-format +msgid "Read Proxy" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Records" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/row.html.eex:13 +#: lib/block_scout_web/templates/account/custom_abi/row.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Remove" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:77 +#, elixir-autogen, elixir-format +msgid "Remove from Watch list" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:155 +#, elixir-autogen, elixir-format +msgid "Request URL" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Request a public tag/label" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:37 +#, elixir-autogen, elixir-format +msgid "Request to add public tag" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Request to edit a public tag/label" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:108 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:38 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:104 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:52 +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:48 +#, elixir-autogen, elixir-format +msgid "Reset" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:173 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:134 +#, elixir-autogen, elixir-format +msgid "Response Body" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:185 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:147 +#, elixir-autogen, elixir-format +msgid "Responses" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:98 +#, elixir-autogen, elixir-format +msgid "Result" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:135 +#, elixir-autogen, elixir-format +msgid "Revert reason" +msgstr "" + +#: lib/block_scout_web/templates/block/_tile.html.eex:52 +#: lib/block_scout_web/templates/chain/_block.html.eex:27 +#: lib/block_scout_web/views/internal_transaction_view.ex:28 +#, elixir-autogen, elixir-format +msgid "Reward" +msgstr "" + +#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:21 +#, elixir-autogen, elixir-format +msgid "Run" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:26 +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:31 +#: lib/block_scout_web/templates/account/tag_address/form.html.eex:25 +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:25 +#: lib/block_scout_web/templates/account/watchlist_address/form.html.eex:83 +#, elixir-autogen, elixir-format +msgid "Save" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/index.html.eex:16 +#: lib/block_scout_web/templates/layout/_search.html.eex:34 +#, elixir-autogen, elixir-format +msgid "Search" +msgstr "" + +#: lib/block_scout_web/templates/search/results.html.eex:17 +#, elixir-autogen, elixir-format +msgid "Search Results" +msgstr "" + +#: lib/block_scout_web/templates/layout/_search.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Search by address, token symbol name, transaction hash, or block number" +msgstr "" + +#: lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex:47 +#, elixir-autogen, elixir-format +msgid "Search tokens" +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:27 +#, elixir-autogen, elixir-format +msgid "Self-Destruct" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:63 +#, elixir-autogen, elixir-format +msgid "Send request" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:163 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:124 +#, elixir-autogen, elixir-format +msgid "Server Response" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Show" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_btn_qr_code.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Show QR Code" +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:52 +#, elixir-autogen, elixir-format +msgid "Shows the current" +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:59 +#, elixir-autogen, elixir-format +msgid "Shows the tokens held in the address (includes ERC-20, ERC-721 and ERC-1155)." +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:66 +#, elixir-autogen, elixir-format +msgid "Shows the total CRC balance in the address." +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:45 +#, elixir-autogen, elixir-format +msgid "Shows total assets held in the address" +msgstr "" + +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Sign out" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:114 +#, elixir-autogen, elixir-format +msgid "Size" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:113 +#, elixir-autogen, elixir-format +msgid "Size of the block in bytes." +msgstr "" + +#: lib/block_scout_web/templates/chain/gas_price_oracle_legend_item.html.eex:20 +#, elixir-autogen, elixir-format +msgid "Slow" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:21 +#, elixir-autogen, elixir-format +msgid "Smart contract / Address" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/address_field.html.eex:4 +#: lib/block_scout_web/templates/account/public_tags_request/address_field.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Smart contract / Address (0x...)" +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:30 +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:50 +#: lib/block_scout_web/templates/address_logs/index.html.eex:23 +#: lib/block_scout_web/templates/address_token/index.html.eex:60 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:58 +#: lib/block_scout_web/templates/address_transaction/index.html.eex:50 +#: lib/block_scout_web/templates/address_validation/index.html.eex:20 +#: lib/block_scout_web/templates/block_transaction/index.html.eex:22 +#: lib/block_scout_web/templates/chain/show.html.eex:157 +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:18 +#: lib/block_scout_web/templates/tokens/holder/index.html.eex:24 +#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:23 +#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:23 +#: lib/block_scout_web/templates/tokens/inventory/index.html.eex:23 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:22 +#: lib/block_scout_web/templates/transaction/index.html.eex:25 +#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:13 +#: lib/block_scout_web/templates/transaction_log/index.html.eex:15 +#: lib/block_scout_web/templates/transaction_state/index.html.eex:8 +#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:14 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:45 +#, elixir-autogen, elixir-format +msgid "Something went wrong, click to reload." +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:222 +#, elixir-autogen, elixir-format +msgid "Something went wrong, click to retry." +msgstr "" + +#: lib/block_scout_web/templates/transaction/not_found.html.eex:7 +#, elixir-autogen, elixir-format +msgid "Sorry, We are unable to locate this transaction Hash" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:63 +#, elixir-autogen, elixir-format +msgid "Sources *.sol files" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Sources and Metadata JSON" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:130 +#, elixir-autogen, elixir-format +msgid "Stakes" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:24 +#, elixir-autogen, elixir-format +msgid "Standard Input JSON" +msgstr "" + +#: lib/block_scout_web/views/internal_transaction_view.ex:24 +#, elixir-autogen, elixir-format +msgid "Static Call" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:110 +#, elixir-autogen, elixir-format +msgid "Status" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Submission date" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:39 +#, elixir-autogen, elixir-format +msgid "Submit an Issue" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_emission_reward_tile.html.eex:8 +#: lib/block_scout_web/views/transaction_view.ex:351 +#, elixir-autogen, elixir-format +msgid "Success" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_pending_tile.html.eex:21 +#: lib/block_scout_web/templates/transaction/_tile.html.eex:52 +#, elixir-autogen, elixir-format +msgid "TX Fee" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:30 +#, elixir-autogen, elixir-format +msgid "Telegram" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:65 +#, elixir-autogen, elixir-format +msgid "Test Networks" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_first.html.eex:11 +#, elixir-autogen, elixir-format +msgid "The 0x library address. This can be found in the generated json file or Truffle output (if using truffle)." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:30 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:26 +#, elixir-autogen, elixir-format +msgid "The EVM version the contract is written for. If the bytecode does not match the version, we try to verify using the latest EVM version." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:122 +#, elixir-autogen, elixir-format +msgid "The SHA256 hash of the block." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:51 +#, elixir-autogen, elixir-format +msgid "The block height of a particular block is defined as the number of blocks preceding it in the blockchain." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:22 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:18 +#, elixir-autogen, elixir-format +msgid "The compiler version is specified in pragma solidity X.X.X. Use the compiler version rather than the nightly build. If using the Solidity compiler, run solc —version to check." +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:38 +#, elixir-autogen, elixir-format +msgid "The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:138 +#, elixir-autogen, elixir-format +msgid "The hash of the block from which this block was generated." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:74 +#, elixir-autogen, elixir-format +msgid "The name found in the source code of the Contract." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:85 +#, elixir-autogen, elixir-format +msgid "The name of the validator." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:79 +#, elixir-autogen, elixir-format +msgid "The number of transactions in the block." +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:40 +#, elixir-autogen, elixir-format +msgid "The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception." +msgstr "" + +#: lib/block_scout_web/templates/page_not_found/index.html.eex:8 +#, elixir-autogen, elixir-format +msgid "The requested path was not found on BlockScout." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:134 +#, elixir-autogen, elixir-format +msgid "The revert reason of the transaction." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:109 +#, elixir-autogen, elixir-format +msgid "The status of the transaction: Confirmed or Unconfirmed." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:68 +#, elixir-autogen, elixir-format +msgid "The total amount of tokens issued" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:177 +#, elixir-autogen, elixir-format +msgid "The total gas amount used in the block and its percentage of gas filled in the block." +msgstr "" + +#: lib/block_scout_web/templates/address_validation/index.html.eex:16 +#, elixir-autogen, elixir-format +msgid "There are no blocks validated by this address." +msgstr "" + +#: lib/block_scout_web/templates/block/index.html.eex:17 +#, elixir-autogen, elixir-format +msgid "There are no blocks." +msgstr "" + +#: lib/block_scout_web/templates/tokens/holder/index.html.eex:29 +#, elixir-autogen, elixir-format +msgid "There are no holders for this Token." +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:54 +#, elixir-autogen, elixir-format +msgid "There are no internal transactions for this address." +msgstr "" + +#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:17 +#, elixir-autogen, elixir-format +msgid "There are no internal transactions for this transaction." +msgstr "" + +#: lib/block_scout_web/templates/address_logs/index.html.eex:28 +#, elixir-autogen, elixir-format +msgid "There are no logs for this address." +msgstr "" + +#: lib/block_scout_web/templates/transaction_log/index.html.eex:20 +#, elixir-autogen, elixir-format +msgid "There are no logs for this transaction." +msgstr "" + +#: lib/block_scout_web/templates/pending_transaction/index.html.eex:22 +#, elixir-autogen, elixir-format +msgid "There are no pending transactions." +msgstr "" + +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:53 +#, elixir-autogen, elixir-format +msgid "There are no token transfers for this address." +msgstr "" + +#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:19 +#, elixir-autogen, elixir-format +msgid "There are no token transfers for this transaction" +msgstr "" + +#: lib/block_scout_web/templates/address_token/index.html.eex:65 +#, elixir-autogen, elixir-format +msgid "There are no tokens for this address." +msgstr "" + +#: lib/block_scout_web/templates/tokens/inventory/index.html.eex:28 +#, elixir-autogen, elixir-format +msgid "There are no tokens." +msgstr "" + +#: lib/block_scout_web/templates/address_transaction/index.html.eex:55 +#, elixir-autogen, elixir-format +msgid "There are no transactions for this address." +msgstr "" + +#: lib/block_scout_web/templates/block_transaction/index.html.eex:27 +#, elixir-autogen, elixir-format +msgid "There are no transactions for this block." +msgstr "" + +#: lib/block_scout_web/templates/transaction/index.html.eex:31 +#, elixir-autogen, elixir-format +msgid "There are no transactions." +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:28 +#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:28 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:27 +#, elixir-autogen, elixir-format +msgid "There are no transfers for this Token." +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:35 +#, elixir-autogen, elixir-format +msgid "There is no coin history for this address." +msgstr "" + +#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:29 +#, elixir-autogen, elixir-format +msgid "There is no decompilded contracts for this address." +msgstr "" + +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:21 +#: lib/block_scout_web/templates/chain/show.html.eex:9 +#, elixir-autogen, elixir-format +msgid "There was a problem loading the chart." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/index.html.eex:6 +#, elixir-autogen, elixir-format +msgid "This API is provided for developers transitioning their applications from Etherscan to BlockScout. It supports GET and POST requests." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:7 +#, elixir-autogen, elixir-format +msgid "This API is provided to support some rpc methods in the exact format specified for ethereum nodes, which can be found " +msgstr "" + +#: lib/block_scout_web/views/block_transaction_view.ex:11 +#, elixir-autogen, elixir-format +msgid "This block has not been processed yet." +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:41 +#, elixir-autogen, elixir-format +msgid "This contract has been partially verified via Sourcify." +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:45 +#, elixir-autogen, elixir-format +msgid "This contract has been verified via Sourcify." +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:10 +#, elixir-autogen, elixir-format +msgid "This is useful to allow sending requests to blockscout without having to change anything about the request." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:64 +#, elixir-autogen, elixir-format +msgid "This transaction is pending confirmation." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:71 +#: lib/block_scout_web/templates/transaction/overview.html.eex:177 +#, elixir-autogen, elixir-format +msgid "Timestamp" +msgstr "" + +#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:32 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:34 +#: lib/block_scout_web/templates/address_transaction/index.html.eex:28 +#: lib/block_scout_web/templates/transaction/overview.html.eex:221 +#: lib/block_scout_web/views/address_internal_transaction_view.ex:9 +#: lib/block_scout_web/views/address_token_transfer_view.ex:9 +#: lib/block_scout_web/views/address_transaction_view.ex:9 +#, elixir-autogen, elixir-format +msgid "To" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:20 +#, elixir-autogen, elixir-format +msgid "To have guaranteed accuracy, use the link above to verify the contract's source code." +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:6 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:8 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:6 +#, elixir-autogen, elixir-format +msgid "To see accurate decoded input data, the contract must be verified." +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:18 +#, elixir-autogen, elixir-format +msgid "Toggle navigation" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:55 +#, elixir-autogen, elixir-format +msgid "Token" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:3 +#: lib/block_scout_web/views/transaction_view.ex:449 +#, elixir-autogen, elixir-format +msgid "Token Burning" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:7 +#: lib/block_scout_web/views/transaction_view.ex:450 +#, elixir-autogen, elixir-format +msgid "Token Creation" +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:10 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:34 +#, elixir-autogen, elixir-format +msgid "Token Details" +msgstr "" + +#: lib/block_scout_web/templates/tokens/holder/index.html.eex:17 +#: lib/block_scout_web/templates/tokens/instance/holder/index.html.eex:16 +#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:17 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:11 +#: lib/block_scout_web/views/tokens/overview_view.ex:40 +#, elixir-autogen, elixir-format +msgid "Token Holders" +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:38 +#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:18 +#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:37 +#, elixir-autogen, elixir-format +msgid "Token ID" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:5 +#: lib/block_scout_web/views/transaction_view.ex:448 +#, elixir-autogen, elixir-format +msgid "Token Minting" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:9 +#: lib/block_scout_web/templates/common_components/_token_transfer_type_display_name.html.eex:11 +#: lib/block_scout_web/views/transaction_view.ex:451 +#, elixir-autogen, elixir-format +msgid "Token Transfer" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:13 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 +#: lib/block_scout_web/templates/tokens/instance/overview/_tabs.html.eex:3 +#: lib/block_scout_web/templates/tokens/instance/transfer/index.html.eex:16 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:5 +#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:15 +#: lib/block_scout_web/templates/transaction/_tabs.html.eex:4 +#: lib/block_scout_web/templates/transaction_token_transfer/index.html.eex:7 +#: lib/block_scout_web/views/address_view.ex:366 +#: lib/block_scout_web/views/tokens/instance/overview_view.ex:197 +#: lib/block_scout_web/views/tokens/overview_view.ex:39 +#: lib/block_scout_web/views/transaction_view.ex:512 +#, elixir-autogen, elixir-format +msgid "Token Transfers" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:54 +#, elixir-autogen, elixir-format +msgid "Token name and symbol." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:142 +#, elixir-autogen, elixir-format +msgid "Token type" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:21 +#: lib/block_scout_web/templates/address/overview.html.eex:176 +#: lib/block_scout_web/templates/address_token/overview.html.eex:58 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:13 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:78 +#: lib/block_scout_web/templates/tokens/index.html.eex:10 +#: lib/block_scout_web/views/address_view.ex:363 +#, elixir-autogen, elixir-format +msgid "Tokens" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:290 +#, elixir-autogen, elixir-format +msgid "Tokens Burnt" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:306 +#, elixir-autogen, elixir-format +msgid "Tokens Created" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:273 +#, elixir-autogen, elixir-format +msgid "Tokens Minted" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:257 +#, elixir-autogen, elixir-format +msgid "Tokens Transferred" +msgstr "" + +#: lib/block_scout_web/templates/address/_metatags.html.eex:13 +#, elixir-autogen, elixir-format +msgid "Top Accounts - %{subnetwork} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/index.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Topic" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:71 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:91 +#, elixir-autogen, elixir-format +msgid "Topics" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:169 +#, elixir-autogen, elixir-format +msgid "Total Difficulty" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:83 +#, elixir-autogen, elixir-format +msgid "Total Supply * Price" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:130 +#, elixir-autogen, elixir-format +msgid "Total blocks" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:168 +#, elixir-autogen, elixir-format +msgid "Total difficulty of the chain until this block." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:186 +#, elixir-autogen, elixir-format +msgid "Total gas limit provided by all transactions in the block." +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:69 +#, elixir-autogen, elixir-format +msgid "Total supply" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:337 +#, elixir-autogen, elixir-format +msgid "Total transaction fee." +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:109 +#, elixir-autogen, elixir-format +msgid "Total transactions" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_transaction/form.html.eex:11 +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:23 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:19 +#: lib/block_scout_web/views/transaction_view.ex:461 +#, elixir-autogen, elixir-format +msgid "Transaction" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_metatags.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Transaction %{transaction} - %{subnetwork} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_metatags.html.eex:11 +#, elixir-autogen, elixir-format +msgid "Transaction %{transaction}, %{subnetwork} %{transaction}" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:412 +#, elixir-autogen, elixir-format +msgid "Transaction Burnt Fee" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:50 +#, elixir-autogen, elixir-format +msgid "Transaction Details" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:338 +#, elixir-autogen, elixir-format +msgid "Transaction Fee" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:80 +#, elixir-autogen, elixir-format +msgid "Transaction Hash" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:2 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:19 +#, elixir-autogen, elixir-format +msgid "Transaction Inputs" +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:13 +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:7 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:16 +#, elixir-autogen, elixir-format +msgid "Transaction Tags" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:362 +#, elixir-autogen, elixir-format +msgid "Transaction Type" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:435 +#, elixir-autogen, elixir-format +msgid "Transaction number from the sending address. Each transaction sent from an address increments the nonce by 1." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:361 +#, elixir-autogen, elixir-format +msgid "Transaction type, introduced in EIP-2718." +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:7 +#: lib/block_scout_web/templates/address/overview.html.eex:187 +#: lib/block_scout_web/templates/address/overview.html.eex:193 +#: lib/block_scout_web/templates/address/overview.html.eex:201 +#: lib/block_scout_web/templates/address_transaction/index.html.eex:13 +#: lib/block_scout_web/templates/block/overview.html.eex:80 +#: lib/block_scout_web/templates/block_transaction/index.html.eex:10 +#: lib/block_scout_web/templates/chain/show.html.eex:213 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:49 +#: lib/block_scout_web/views/address_view.ex:365 +#, elixir-autogen, elixir-format +msgid "Transactions" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:101 +#, elixir-autogen, elixir-format +msgid "Transactions and address of creation." +msgstr "" + +#: lib/block_scout_web/templates/address/_tile.html.eex:31 +#, elixir-autogen, elixir-format +msgid "Transactions sent" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:214 +#: lib/block_scout_web/templates/address/overview.html.eex:220 +#: lib/block_scout_web/templates/address/overview.html.eex:228 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:50 +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:119 +#, elixir-autogen, elixir-format +msgid "Transfers" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:40 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:47 +#, elixir-autogen, elixir-format +msgid "Try it out" +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:27 +#, elixir-autogen, elixir-format +msgid "Twitter" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:49 +#, elixir-autogen, elixir-format +msgid "Tx/day" +msgstr "" + +#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:5 +#: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Type" +msgstr "" + +#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:141 +#, elixir-autogen, elixir-format +msgid "Type of the token standard" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:461 +#, elixir-autogen, elixir-format +msgid "UTF-8" +msgstr "" + +#: lib/block_scout_web/views/block_view.ex:77 +#, elixir-autogen, elixir-format +msgid "Uncle Reward" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:250 +#: lib/block_scout_web/templates/layout/_topnav.html.eex:41 +#, elixir-autogen, elixir-format +msgid "Uncles" +msgstr "" + +#: lib/block_scout_web/views/transaction_view.ex:342 +#, elixir-autogen, elixir-format +msgid "Unconfirmed" +msgstr "" + +#: lib/block_scout_web/templates/tokens/inventory/_token.html.eex:9 +#, elixir-autogen, elixir-format +msgid "Unique Token" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:79 +#, elixir-autogen, elixir-format +msgid "Unique character string (TxID) assigned to every verified transaction." +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/form.html.eex:7 +#: lib/block_scout_web/templates/account/custom_abi/form.html.eex:8 +#, elixir-autogen, elixir-format +msgid "Update" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:391 +#, elixir-autogen, elixir-format +msgid "User defined maximum fee (tip) per unit of gas paid to validator for transaction prioritization." +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:401 +#, elixir-autogen, elixir-format +msgid "User-defined tip sent to validator for transaction priority/inclusion." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:224 +#, elixir-autogen, elixir-format +msgid "User-defined tips sent to validator for transaction priority/inclusion." +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:55 +#, elixir-autogen, elixir-format +msgid "Validated" +msgstr "" + +#: lib/block_scout_web/templates/transaction/index.html.eex:12 +#, elixir-autogen, elixir-format +msgid "Validated Transactions" +msgstr "" + +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:30 +#, elixir-autogen, elixir-format +msgid "Validator Creation Date" +msgstr "" + +#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:5 +#, elixir-autogen, elixir-format +msgid "Validator Data" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:86 +#, elixir-autogen, elixir-format +msgid "Validator Name" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:323 +#, elixir-autogen, elixir-format +msgid "Value" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:322 +#, elixir-autogen, elixir-format +msgid "Value sent in the native token (and USD) if applicable." +msgstr "" + +#: lib/block_scout_web/templates/address_read_contract/index.html.eex:17 +#: lib/block_scout_web/templates/address_write_contract/index.html.eex:15 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:75 +#, elixir-autogen, elixir-format +msgid "Verified" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:82 +#, elixir-autogen, elixir-format +msgid "Verified at" +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:27 +#: lib/block_scout_web/templates/address_contract/index.html.eex:29 +#: lib/block_scout_web/templates/address_contract/index.html.eex:161 +#: lib/block_scout_web/templates/address_contract/index.html.eex:192 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Verify & Publish" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:107 +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:37 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:103 +#: lib/block_scout_web/templates/address_contract_verification_via_standard_json_input/new.html.eex:51 +#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:47 +#, elixir-autogen, elixir-format +msgid "Verify & publish" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Verify the contract " +msgstr "" + +#: lib/block_scout_web/templates/layout/_footer.html.eex:91 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:66 +#, elixir-autogen, elixir-format +msgid "Version" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:33 +#, elixir-autogen, elixir-format +msgid "Via Sourcify: Sources and metadata JSON file" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:27 +#, elixir-autogen, elixir-format +msgid "Via Standard Input JSON" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:22 +#, elixir-autogen, elixir-format +msgid "Via flattened source code" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40 +#, elixir-autogen, elixir-format +msgid "Via multi-part files" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:152 +#, elixir-autogen, elixir-format +msgid "View All Blocks" +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:212 +#, elixir-autogen, elixir-format +msgid "View All Transactions" +msgstr "" + +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:16 +#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20 +#, elixir-autogen, elixir-format +msgid "View Contract" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_tile.html.eex:73 +#, elixir-autogen, elixir-format +msgid "View Less Transfers" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_tile.html.eex:72 +#, elixir-autogen, elixir-format +msgid "View More Transfers" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:39 +#, elixir-autogen, elixir-format +msgid "View next block" +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:23 +#, elixir-autogen, elixir-format +msgid "View previous block" +msgstr "" + +#: lib/block_scout_web/templates/address/_metatags.html.eex:9 +#, elixir-autogen, elixir-format +msgid "View the account balance, transactions, and other data for %{address} on the %{network}" +msgstr "" + +#: lib/block_scout_web/templates/block/_metatags.html.eex:10 +#, elixir-autogen, elixir-format +msgid "View the transactions, token transfers, and uncles for block number %{block_number}" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_metatags.html.eex:10 +#, elixir-autogen, elixir-format +msgid "View transaction %{transaction} on %{subnetwork}" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:46 +#, elixir-autogen, elixir-format +msgid "Vyper contract" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:136 +#, elixir-autogen, elixir-format +msgid "WEI" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_pending_contract_write.html.eex:9 +#, elixir-autogen, elixir-format +msgid "Waiting for transaction's confirmation..." +msgstr "" + +#: lib/block_scout_web/templates/chain/show.html.eex:138 +#, elixir-autogen, elixir-format +msgid "Wallet addresses" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_changed_bytecode_warning.html.eex:3 +#, elixir-autogen, elixir-format +msgid "Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky." +msgstr "" + +#: lib/block_scout_web/templates/account/common/_nav.html.eex:7 +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:7 +#: lib/block_scout_web/templates/layout/_account_menu_item.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Watch list" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:73 +#, elixir-autogen, elixir-format +msgid "We recommend using flattened code. This is necessary if your code utilizes a library or inherits dependencies. Use the" +msgstr "" + +#: lib/block_scout_web/views/wei_helpers.ex:76 +#, elixir-autogen, elixir-format +msgid "Wei" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:100 +#, elixir-autogen, elixir-format +msgid "Write" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:95 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:34 +#: lib/block_scout_web/views/address_view.ex:371 +#, elixir-autogen, elixir-format +msgid "Write Contract" +msgstr "" + +#: lib/block_scout_web/templates/address/_tabs.html.eex:102 +#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:48 +#: lib/block_scout_web/views/address_view.ex:372 +#, elixir-autogen, elixir-format +msgid "Write Proxy" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_fetch_constructor_args.html.eex:14 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_include_nightly_builds_field.html.eex:14 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:47 +#: lib/block_scout_web/templates/address_contract_verification_via_multi_part_files/new.html.eex:43 +#, elixir-autogen, elixir-format +msgid "Yes" +msgstr "" + +#: lib/block_scout_web/templates/account/api_key/index.html.eex:18 +#, elixir-autogen, elixir-format +msgid "You can create 3 API keys per account." +msgstr "" + +#: lib/block_scout_web/templates/account/custom_abi/index.html.eex:18 +#, elixir-autogen, elixir-format +msgid "You can create up to 15 Custom ABIs per account." +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/index.html.eex:11 +#, elixir-autogen, elixir-format +msgid "You can request a public category tag which is displayed to all Blockscout users. Public tags may be added to contract or external addresses, and any associated transactions will inherit that tag. Clicking a tag opens a page with related information and helps provide context and data organization. Requests are sent to a moderator for review and approval. This process can take several days." +msgstr "" + +#: lib/block_scout_web/templates/account/tag_address/index.html.eex:14 +#, elixir-autogen, elixir-format +msgid "You don't have address tags yet" +msgstr "" + +#: lib/block_scout_web/templates/account/watchlist/show.html.eex:14 +#, elixir-autogen, elixir-format +msgid "You don't have addresses on you watchlist yet" +msgstr "" + +#: lib/block_scout_web/templates/account/tag_transaction/index.html.eex:14 +#, elixir-autogen, elixir-format +msgid "You don't have transaction tags yet" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Your name" +msgstr "" + +#: lib/block_scout_web/templates/account/public_tags_request/form.html.eex:14 +#, elixir-autogen, elixir-format +msgid "Your name*" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:111 +#, elixir-autogen, elixir-format +msgid "at" +msgstr "" + +#: lib/block_scout_web/templates/address_token/overview.html.eex:52 +#, elixir-autogen, elixir-format +msgid "balance of the address" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:411 +#, elixir-autogen, elixir-format +msgid "burned for this transaction. Equals Block Base Fee per Gas * Gas Used." +msgstr "" + +#: lib/block_scout_web/templates/block/overview.html.eex:215 +#, elixir-autogen, elixir-format +msgid "burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)." +msgstr "" + +#: lib/block_scout_web/templates/address_contract/index.html.eex:27 +#, elixir-autogen, elixir-format +msgid "button" +msgstr "" + +#: lib/block_scout_web/templates/transaction/overview.html.eex:230 +#, elixir-autogen, elixir-format +msgid "created" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:12 +#, elixir-autogen, elixir-format +msgid "custom RPC" +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:150 +#, elixir-autogen, elixir-format +msgid "doesn't include ERC20, ERC721, ERC1155 tokens)." +msgstr "" + +#: lib/block_scout_web/templates/common_components/_rap_pagination_container.html.eex:13 +#, elixir-autogen, elixir-format +msgid "elements are displayed" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:38 +#, elixir-autogen, elixir-format +msgid "fallback" +msgstr "" + +#: lib/block_scout_web/views/address_contract_view.ex:24 +#, elixir-autogen, elixir-format +msgid "false" +msgstr "" + +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:10 +#, elixir-autogen, elixir-format +msgid "here" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/eth_rpc.html.eex:9 +#, elixir-autogen, elixir-format +msgid "here." +msgstr "" + +#: lib/block_scout_web/templates/transaction/invalid.html.eex:8 +#, elixir-autogen, elixir-format +msgid "is not a valid transaction hash" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_function_response.html.eex:3 +#, elixir-autogen, elixir-format +msgid "method Response" +msgstr "" + +#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:41 +#, elixir-autogen, elixir-format +msgid "of" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16 +#, elixir-autogen, elixir-format +msgid "page" +msgstr "" + +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:40 +#, elixir-autogen, elixir-format +msgid "receive" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:58 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:69 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:81 +#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:70 +#, elixir-autogen, elixir-format +msgid "required" +msgstr "" + +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:59 +#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:70 +#, elixir-autogen, elixir-format +msgid "string" +msgstr "" + +#: lib/block_scout_web/views/address_contract_view.ex:23 +#, elixir-autogen, elixir-format +msgid "true" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:73 +#, elixir-autogen, elixir-format +msgid "truffle flattener" +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_first.html.eex:5 +#, elixir-autogen, elixir-format +msgid ") may be added for each contract. Click the Add Library button to add an additional one." +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_first.html.eex:5 +#, elixir-autogen, elixir-format, fuzzy +msgid "A library name called in the .sol file. Multiple libraries (up to " +msgstr "" + +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_address.html.eex:4 +#: lib/block_scout_web/templates/address_contract_verification_common_fields/_library_name.html.eex:4 +#, elixir-autogen, elixir-format, fuzzy +msgid "Library" +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/_state_change.html.eex:9 +#, elixir-autogen, elixir-format +msgid "Address used in token mintings and burnings." +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/index.html.eex:35 +#, elixir-autogen, elixir-format, fuzzy +msgid "Balance after" +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/index.html.eex:32 +#, elixir-autogen, elixir-format, fuzzy +msgid "Balance before" +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/_state_change.html.eex:10 +#, elixir-autogen, elixir-format +msgid "Burn address" +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/index.html.eex:38 +#, elixir-autogen, elixir-format +msgid "Change" +msgstr "" + +#: lib/block_scout_web/templates/transaction/_tabs.html.eex:29 +#: lib/block_scout_web/templates/transaction_state/index.html.eex:6 +#: lib/block_scout_web/views/transaction_view.ex:516 +#, elixir-autogen, elixir-format +msgid "State changes" +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/index.html.eex:13 +#, elixir-autogen, elixir-format +msgid "The changes from this transaction have not yet happened since the transaction is still pending." +msgstr "" + +#: lib/block_scout_web/templates/transaction_state/index.html.eex:17 +#, elixir-autogen, elixir-format +msgid "This transaction hasn't changed state." +msgstr "" + +#: lib/block_scout_web/templates/address/overview.html.eex:120 +#, elixir-autogen, elixir-format +msgid "Contract was precompiled and created at genesis or contract creation transaction is missing" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:29 +#, elixir-autogen, elixir-format, fuzzy +msgid "Blockchain" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:72 +#, elixir-autogen, elixir-format, fuzzy +msgid "Constructor args" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:41 +#, elixir-autogen, elixir-format, fuzzy +msgid "Contract name or address" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:5 +#, elixir-autogen, elixir-format, fuzzy +msgid "Contracts" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:15 +#, elixir-autogen, elixir-format +msgid "Filter by compiler:" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:13 +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:26 +#, elixir-autogen, elixir-format +msgid "Last 24h" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:78 +#, elixir-autogen, elixir-format, fuzzy +msgid "Market cap" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_contract.html.eex:21 +#: lib/block_scout_web/templates/verified_contracts/_contract.html.eex:58 +#, elixir-autogen, elixir-format +msgid "N/A" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:69 +#, elixir-autogen, elixir-format, fuzzy +msgid "Optimization" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_contract.html.eex:28 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:26 +#: lib/block_scout_web/views/verified_contracts_view.ex:9 +#, elixir-autogen, elixir-format +msgid "Solidity" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:93 +#, elixir-autogen, elixir-format, fuzzy +msgid "There are no verified contracts." +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:9 +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:22 +#, elixir-autogen, elixir-format, fuzzy +msgid "Total" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:60 +#, elixir-autogen, elixir-format +msgid "Txns" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_stats.html.eex:18 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:6 +#, elixir-autogen, elixir-format, fuzzy +msgid "Verified Contracts" +msgstr "" + +#: lib/block_scout_web/templates/layout/_topnav.html.eex:68 +#, elixir-autogen, elixir-format, fuzzy +msgid "Verified contracts" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_metatags.html.eex:2 +#, elixir-autogen, elixir-format, fuzzy +msgid "Verified contracts - %{subnetwork} Explorer" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_metatags.html.eex:8 +#, elixir-autogen, elixir-format +msgid "View the verified contracts on %{subnetwork}" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_contract.html.eex:28 +#: lib/block_scout_web/templates/verified_contracts/index.html.eex:32 +#: lib/block_scout_web/views/verified_contracts_view.ex:10 +#, elixir-autogen, elixir-format +msgid "Vyper" +msgstr "" + +#: lib/block_scout_web/templates/verified_contracts/_metatags.html.eex:7 +#, elixir-autogen, elixir-format, fuzzy +msgid "Verifed contracts, %{subnetwork}, %{coin}" +msgstr "" + +#: lib/block_scout_web/templates/layout/app.html.eex:44 +#, elixir-autogen, elixir-format, fuzzy +msgid "Blocks With Internal Transactions Indexed" +msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/errors.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/errors.po new file mode 100644 index 0000000..cdaaac6 --- /dev/null +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/errors.po @@ -0,0 +1,94 @@ +## This file is a PO Template file. +## +## `msgid`s here are often extracted from source code. +## Add new translations manually only if they're dynamic +## translations that can't be statically extracted. +## +## Run `mix gettext.extract` to bring this file up to +## date. Leave `msgstr`s empty as changing them here as no +## effect: edit them in PO (`.po`) files instead. +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "" + +## From Ecto.Changeset.validate_acceptance/3 +msgid "must be accepted" +msgstr "" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated with this entry" +msgstr "" + +msgid "are still associated with this entry" +msgstr "" + +## From Ecto.Changeset.validate_length/3 +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" diff --git a/apps/block_scout_web/priv/gettext/errors.pot b/apps/block_scout_web/priv/gettext/errors.pot new file mode 100644 index 0000000..cdaaac6 --- /dev/null +++ b/apps/block_scout_web/priv/gettext/errors.pot @@ -0,0 +1,94 @@ +## This file is a PO Template file. +## +## `msgid`s here are often extracted from source code. +## Add new translations manually only if they're dynamic +## translations that can't be statically extracted. +## +## Run `mix gettext.extract` to bring this file up to +## date. Leave `msgstr`s empty as changing them here as no +## effect: edit them in PO (`.po`) files instead. +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "" + +## From Ecto.Changeset.validate_acceptance/3 +msgid "must be accepted" +msgstr "" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated with this entry" +msgstr "" + +msgid "are still associated with this entry" +msgstr "" + +## From Ecto.Changeset.validate_length/3 +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" diff --git a/apps/block_scout_web/test/block_scout_web/chain_test.exs b/apps/block_scout_web/test/block_scout_web/chain_test.exs new file mode 100644 index 0000000..7656ff2 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/chain_test.exs @@ -0,0 +1,82 @@ +defmodule BlockScoutWeb.ChainTest do + use Explorer.DataCase + + alias Explorer.Chain.{Address, Block, Transaction} + alias BlockScoutWeb.Chain + + describe "current_filter/1" do + test "sets direction based on to filter" do + assert [direction: :to] = Chain.current_filter(%{"filter" => "to"}) + end + + test "sets direction based on from filter" do + assert [direction: :from] = Chain.current_filter(%{"filter" => "from"}) + end + + test "no direction set" do + assert [] = Chain.current_filter(%{}) + end + + test "no direction set with paging_options" do + assert [paging_options: "test"] = Chain.current_filter(%{paging_options: "test"}) + end + end + + describe "from_param/1" do + test "finds a block by block number with a valid block number" do + %Block{number: number} = insert(:block, number: 37) + + assert {:ok, %Block{number: ^number}} = + number + |> to_string() + |> Chain.from_param() + end + + test "finds a transaction by hash string" do + transaction = %Transaction{hash: hash} = insert(:transaction) + + assert {:ok, %Transaction{hash: ^hash}} = transaction |> Phoenix.Param.to_param() |> Chain.from_param() + end + + test "finds an address by hash string" do + address = %Address{hash: hash} = insert(:address) + + assert {:ok, %Address{hash: ^hash}} = address |> Phoenix.Param.to_param() |> Chain.from_param() + end + + test "finds a token by its name" do + name = "AYR" + insert(:token, symbol: name) + + assert {:ok, %Address{}} = name |> Chain.from_param() + end + + test "finds a token by its name even if lowercase name was passed" do + name = "ayr" + insert(:token, symbol: String.upcase(name)) + + assert {:ok, %Address{}} = name |> Chain.from_param() + end + + test "returns {:error, :not_found} when garbage is passed in" do + assert {:error, :not_found} = Chain.from_param("any ol' thing") + end + + test "returns {:error, :not_found} when it does not find a match" do + transaction_hash = String.pad_trailing("0xnonsense", 43, "0") + address_hash = String.pad_trailing("0xbaddress", 42, "0") + + assert {:error, :not_found} = Chain.from_param("38999") + assert {:error, :not_found} = Chain.from_param(transaction_hash) + assert {:error, :not_found} = Chain.from_param(address_hash) + end + end + + describe "Posion.encode!" do + test "correctly encodes decimal values" do + val = Decimal.from_float(5.55) + + assert "\"5.55\"" == Poison.encode!(val) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/channels/address_channel_test.exs b/apps/block_scout_web/test/block_scout_web/channels/address_channel_test.exs new file mode 100644 index 0000000..07b890f --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/channels/address_channel_test.exs @@ -0,0 +1,231 @@ +defmodule BlockScoutWeb.AddressChannelTest do + use BlockScoutWeb.ChannelCase, + # ETS tables are shared in `Explorer.Counters.AddressesCounter` + async: false + + alias BlockScoutWeb.UserSocket + alias BlockScoutWeb.Notifier + alias Explorer.Counters.AddressesCounter + + test "subscribed user is notified of new_address count event" do + topic = "addresses:new_address" + @endpoint.subscribe(topic) + + address = insert(:address) + + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + Notifier.handle_event({:chain_event, :addresses, :realtime, [address]}) + + assert_receive %Phoenix.Socket.Broadcast{topic: ^topic, event: "count", payload: %{count: _}}, :timer.seconds(5) + end + + describe "user pushing to channel" do + setup do + address = insert(:address, fetched_coin_balance: 100_000, fetched_coin_balance_block_number: 1) + topic = "addresses:#{address.hash}" + + {:ok, _, socket} = + UserSocket + |> socket("no_id", %{locale: "en"}) + |> subscribe_and_join(topic) + + {:ok, %{address: address, topic: topic, socket: socket}} + end + + test "can retrieve current balance card of the address", %{socket: socket, address: address} do + ref = push(socket, "get_balance", %{}) + + assert_reply(ref, :ok, %{balance: sent_balance, balance_card: _balance_card}) + + assert sent_balance == address.fetched_coin_balance.value + # assert balance_card =~ "/address/#{address.hash}/token-balances" + end + end + + describe "user subscribed to address" do + setup do + address = insert(:address) + topic = "addresses:#{address.hash}" + @endpoint.subscribe(topic) + {:ok, %{address: address, topic: topic}} + end + + test "notified of balance_update for matching address", %{address: address, topic: topic} do + address_with_balance = %{address | fetched_coin_balance: 1} + + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + Notifier.handle_event({:chain_event, :addresses, :realtime, [address_with_balance]}) + + assert_receive %Phoenix.Socket.Broadcast{topic: ^topic, event: "balance_update", payload: payload}, + :timer.seconds(5) + + assert payload.address.hash == address_with_balance.hash + end + + test "not notified of balance_update if fetched_coin_balance is nil", %{address: address} do + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + Notifier.handle_event({:chain_event, :addresses, :realtime, [address]}) + + refute_receive _, 100, "Message was broadcast for nil fetched_coin_balance." + end + + test "notified of new_pending_transaction for matching from_address", %{address: address, topic: topic} do + pending = insert(:transaction, from_address: address) + + Notifier.handle_event({:chain_event, :transactions, :realtime, [pending]}) + + assert_receive %Phoenix.Socket.Broadcast{topic: ^topic, event: "pending_transaction", payload: payload}, + :timer.seconds(5) + + assert payload.address.hash == address.hash + assert payload.transaction.hash == pending.hash + end + + test "notified of new_transaction for matching from_address", %{address: address, topic: topic} do + transaction = + :transaction + |> insert(from_address: address) + |> with_block() + + Notifier.handle_event({:chain_event, :transactions, :realtime, [transaction]}) + + assert_receive %Phoenix.Socket.Broadcast{topic: ^topic, event: "transaction", payload: payload}, :timer.seconds(5) + assert payload.address.hash == address.hash + assert payload.transaction.hash == transaction.hash + end + + test "notified of new_transaction for matching to_address", %{address: address, topic: topic} do + transaction = + :transaction + |> insert(to_address: address) + |> with_block() + + Notifier.handle_event({:chain_event, :transactions, :realtime, [transaction]}) + + assert_receive %Phoenix.Socket.Broadcast{topic: ^topic, event: "transaction", payload: payload}, :timer.seconds(5) + assert payload.address.hash == address.hash + assert payload.transaction.hash == transaction.hash + end + + test "not notified twice of new_transaction if to and from address are equal", %{address: address, topic: topic} do + transaction = + :transaction + |> insert(from_address: address, to_address: address) + |> with_block() + + Notifier.handle_event({:chain_event, :transactions, :realtime, [transaction]}) + + assert_receive %Phoenix.Socket.Broadcast{topic: ^topic, event: "transaction", payload: payload}, :timer.seconds(5) + assert payload.address.hash == address.hash + assert payload.transaction.hash == transaction.hash + + refute_receive _, 100, "Received duplicate broadcast." + end + + test "notified of new_internal_transaction for matching from_address", %{address: address, topic: topic} do + transaction = + :transaction + |> insert(from_address: address) + |> with_block() + + internal_transaction = + insert( + :internal_transaction, + transaction: transaction, + from_address: address, + index: 0, + block_hash: transaction.block_hash, + block_index: 0 + ) + + Notifier.handle_event({:chain_event, :internal_transactions, :realtime, [internal_transaction]}) + + assert_receive %Phoenix.Socket.Broadcast{ + topic: ^topic, + event: "internal_transaction", + payload: %{ + address: %{hash: address_hash}, + internal_transaction: %{transaction_hash: transaction_hash, index: index} + } + }, + :timer.seconds(5) + + assert address_hash == address.hash + assert {transaction_hash, index} == {internal_transaction.transaction_hash, internal_transaction.index} + end + + test "notified of new_internal_transaction for matching to_address", %{address: address, topic: topic} do + transaction = + :transaction + |> insert(to_address: address) + |> with_block() + + internal_transaction = + insert(:internal_transaction, + transaction: transaction, + to_address: address, + index: 0, + block_hash: transaction.block_hash, + block_index: 0 + ) + + Notifier.handle_event({:chain_event, :internal_transactions, :realtime, [internal_transaction]}) + + assert_receive %Phoenix.Socket.Broadcast{ + topic: ^topic, + event: "internal_transaction", + payload: %{ + address: %{hash: address_hash}, + internal_transaction: %{transaction_hash: transaction_hash, index: index} + } + }, + :timer.seconds(5) + + assert address_hash == address.hash + assert {transaction_hash, index} == {internal_transaction.transaction_hash, internal_transaction.index} + end + + test "not notified twice of new_internal_transaction if to and from address are equal", %{ + address: address, + topic: topic + } do + transaction = + :transaction + |> insert(from_address: address, to_address: address) + |> with_block() + + internal_transaction = + insert(:internal_transaction, + transaction: transaction, + from_address: address, + to_address: address, + index: 0, + block_hash: transaction.block_hash, + block_index: 0 + ) + + Notifier.handle_event({:chain_event, :internal_transactions, :realtime, [internal_transaction]}) + + assert_receive %Phoenix.Socket.Broadcast{ + topic: ^topic, + event: "internal_transaction", + payload: %{ + address: %{hash: address_hash}, + internal_transaction: %{transaction_hash: transaction_hash, index: index} + } + }, + :timer.seconds(5) + + assert address_hash == address.hash + assert {transaction_hash, index} == {internal_transaction.transaction_hash, internal_transaction.index} + + refute_receive _, 100, "Received duplicate broadcast." + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/channels/block_channel_test.exs b/apps/block_scout_web/test/block_scout_web/channels/block_channel_test.exs new file mode 100644 index 0000000..f9ffc01 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/channels/block_channel_test.exs @@ -0,0 +1,22 @@ +defmodule BlockScoutWeb.BlockChannelTest do + use BlockScoutWeb.ChannelCase + + alias BlockScoutWeb.Notifier + + test "subscribed user is notified of new_block event" do + topic = "blocks:new_block" + @endpoint.subscribe(topic) + + block = insert(:block, number: 1) + + Notifier.handle_event({:chain_event, :blocks, :realtime, [block]}) + + receive do + %Phoenix.Socket.Broadcast{topic: ^topic, event: "new_block", payload: %{block: _}} -> + assert true + after + :timer.seconds(5) -> + assert false, "Expected message received nothing." + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/channels/exchange_rate_channel_test.exs b/apps/block_scout_web/test/block_scout_web/channels/exchange_rate_channel_test.exs new file mode 100644 index 0000000..bac3c72 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/channels/exchange_rate_channel_test.exs @@ -0,0 +1,100 @@ +defmodule BlockScoutWeb.ExchangeRateChannelTest do + use BlockScoutWeb.ChannelCase + + import Mox + + alias BlockScoutWeb.Notifier + alias Explorer.ExchangeRates + alias Explorer.ExchangeRates.Token + alias Explorer.ExchangeRates.Source.TestSource + alias Explorer.Market + + setup :verify_on_exit! + + setup do + # Use TestSource mock and ets table for this test set + configuration = Application.get_env(:explorer, Explorer.ExchangeRates) + Application.put_env(:explorer, Explorer.ExchangeRates, source: TestSource) + Application.put_env(:explorer, Explorer.ExchangeRates, table_name: :rates) + Application.put_env(:explorer, Explorer.ExchangeRates, enabled: true) + + ExchangeRates.init([]) + + token = %Token{ + available_supply: Decimal.new("1000000.0"), + total_supply: Decimal.new("1000000.0"), + btc_value: Decimal.new("1.000"), + id: "test", + last_updated: DateTime.utc_now(), + market_cap_usd: Decimal.new("1000000.0"), + name: "test", + symbol: Explorer.coin(), + usd_value: Decimal.new("2.5"), + volume_24h_usd: Decimal.new("1000.0") + } + + on_exit(fn -> + Application.put_env(:explorer, Explorer.ExchangeRates, configuration) + end) + + {:ok, %{token: token}} + end + + describe "new_rate" do + test "subscribed user is notified", %{token: token} do + ExchangeRates.handle_info({nil, {:ok, [token]}}, %{}) + Supervisor.terminate_child(Explorer.Supervisor, {ConCache, Explorer.Market.MarketHistoryCache.cache_name()}) + Supervisor.restart_child(Explorer.Supervisor, {ConCache, Explorer.Market.MarketHistoryCache.cache_name()}) + + topic = "exchange_rate:new_rate" + @endpoint.subscribe(topic) + + Notifier.handle_event({:chain_event, :exchange_rate}) + + receive do + %Phoenix.Socket.Broadcast{topic: ^topic, event: "new_rate", payload: payload} -> + assert payload.exchange_rate == Map.from_struct(token) + assert payload.market_history_data == [] + after + :timer.seconds(5) -> + assert false, "Expected message received nothing." + end + end + + test "subscribed user is notified with market history", %{token: token} do + ExchangeRates.handle_info({nil, {:ok, [token]}}, %{}) + Supervisor.terminate_child(Explorer.Supervisor, {ConCache, Explorer.Market.MarketHistoryCache.cache_name()}) + Supervisor.restart_child(Explorer.Supervisor, {ConCache, Explorer.Market.MarketHistoryCache.cache_name()}) + + today = Date.utc_today() + + old_records = + for i <- 1..29 do + %{ + date: Timex.shift(today, days: i * -1), + closing_price: Decimal.new(1) + } + end + + records = [%{date: today, closing_price: token.usd_value} | old_records] + + Market.bulk_insert_history(records) + + Market.fetch_recent_history() + + topic = "exchange_rate:new_rate" + @endpoint.subscribe(topic) + + Notifier.handle_event({:chain_event, :exchange_rate}) + + receive do + %Phoenix.Socket.Broadcast{topic: ^topic, event: "new_rate", payload: payload} -> + assert payload.exchange_rate == Map.from_struct(token) + assert payload.market_history_data == records + after + :timer.seconds(5) -> + assert false, "Expected message received nothing." + end + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/channels/reward_channel_test.exs b/apps/block_scout_web/test/block_scout_web/channels/reward_channel_test.exs new file mode 100644 index 0000000..6dd8dae --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/channels/reward_channel_test.exs @@ -0,0 +1,57 @@ +defmodule BlockScoutWeb.RewardChannelTest do + use BlockScoutWeb.ChannelCase, async: false + + alias BlockScoutWeb.Notifier + + describe "user subscribed to rewards" do + test "does nothing if the configuration is turned off" do + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, has_emission_funds: false) + + address = insert(:address) + block = insert(:block) + reward = insert(:reward, address_hash: address.hash, block_hash: block.hash) + + topic = "rewards:#{address.hash}" + @endpoint.subscribe(topic) + + Notifier.handle_event({:chain_event, :block_rewards, :realtime, [reward]}) + + refute_receive _, :timer.seconds(2) + + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, has_emission_funds: false) + end + + test "notified of new reward for matching address" do + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, has_emission_funds: true) + address = insert(:address) + block = insert(:block) + reward = insert(:reward, address_hash: address.hash, block_hash: block.hash) + + topic = "rewards:#{address.hash}" + @endpoint.subscribe(topic) + + Notifier.handle_event({:chain_event, :block_rewards, :realtime, [reward]}) + + assert_receive %Phoenix.Socket.Broadcast{topic: ^topic, event: "new_reward", payload: _}, :timer.seconds(5) + + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, has_emission_funds: false) + end + + test "not notified of new reward for other address" do + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, has_emission_funds: true) + + address = insert(:address) + block = insert(:block) + reward = insert(:reward, address_hash: address.hash, block_hash: block.hash) + + topic = "rewards:0x0" + @endpoint.subscribe(topic) + + Notifier.handle_event({:chain_event, :block_rewards, :realtime, [reward]}) + + refute_receive _, :timer.seconds(2) + + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, has_emission_funds: false) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/channels/transaction_channel_test.exs b/apps/block_scout_web/test/block_scout_web/channels/transaction_channel_test.exs new file mode 100644 index 0000000..b0d5491 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/channels/transaction_channel_test.exs @@ -0,0 +1,63 @@ +defmodule BlockScoutWeb.TransactionChannelTest do + use BlockScoutWeb.ChannelCase + + alias Explorer.Chain.Hash + alias BlockScoutWeb.Notifier + + test "subscribed user is notified of new_transaction topic" do + topic = "transactions:new_transaction" + @endpoint.subscribe(topic) + + transaction = + :transaction + |> insert() + |> with_block() + + Notifier.handle_event({:chain_event, :transactions, :realtime, [transaction]}) + + receive do + %Phoenix.Socket.Broadcast{topic: ^topic, event: "transaction", payload: payload} -> + assert payload.transaction.hash == transaction.hash + after + :timer.seconds(5) -> + assert false, "Expected message received nothing." + end + end + + test "subscribed user is notified of new_pending_transaction topic" do + topic = "transactions:new_pending_transaction" + @endpoint.subscribe(topic) + + pending = insert(:transaction) + + Notifier.handle_event({:chain_event, :transactions, :realtime, [pending]}) + + receive do + %Phoenix.Socket.Broadcast{topic: ^topic, event: "pending_transaction", payload: payload} -> + assert payload.transaction.hash == pending.hash + after + :timer.seconds(5) -> + assert false, "Expected message received nothing." + end + end + + test "subscribed user is notified of transaction_hash collated event" do + transaction = + :transaction + |> insert() + |> with_block() + + topic = "transactions:#{Hash.to_string(transaction.hash)}" + @endpoint.subscribe(topic) + + Notifier.handle_event({:chain_event, :transactions, :realtime, [transaction]}) + + receive do + %Phoenix.Socket.Broadcast{topic: ^topic, event: "collated", payload: %{}} -> + assert true + after + :timer.seconds(5) -> + assert false, "Expected message received nothing." + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/account/api/v1/user_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v1/user_controller_test.exs new file mode 100644 index 0000000..0f56777 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/account/api/v1/user_controller_test.exs @@ -0,0 +1,919 @@ +defmodule BlockScoutWeb.Account.Api.V1.UserControllerTest do + use BlockScoutWeb.ConnCase + + alias BlockScoutWeb.Models.UserFromAuth + + setup %{conn: conn} do + auth = build(:auth) + + {:ok, user} = UserFromAuth.find_or_create(auth) + + {:ok, user: user, conn: Plug.Test.init_test_session(conn, current_user: user)} + end + + describe "Test account/api/v1/user" do + test "get user info", %{conn: conn, user: user} do + result_conn = + conn + |> get("/api/account/v1/user/info") + |> doc(description: "Get info about user") + + assert json_response(result_conn, 200) == %{ + "nickname" => user.nickname, + "name" => user.name, + "email" => user.email, + "avatar" => user.avatar + } + end + + test "post private address tag", %{conn: conn} do + tag_address_response = + conn + |> post("/api/account/v1/user/tags/address", %{ + "address_hash" => "0x3e9ac8f16c92bc4f093357933b5befbf1e16987b", + "name" => "MyName" + }) + |> doc(description: "Add private address tag") + |> json_response(200) + + conn + |> get("/api/account/v1/tags/address/0x3e9ac8f16c92bc4f093357933b5befbf1e16987b") + |> doc(description: "Get tags for address") + |> json_response(200) + + assert tag_address_response["address_hash"] == "0x3e9ac8f16c92bc4f093357933b5befbf1e16987b" + assert tag_address_response["name"] == "MyName" + assert tag_address_response["id"] + end + + test "edit private address tag", %{conn: conn} do + address_tag = build(:tag_address) + + tag_address_response = + conn + |> post("/api/account/v1/user/tags/address", address_tag) + |> json_response(200) + + _response = + conn + |> get("/api/account/v1/user/tags/address") + |> json_response(200) == [tag_address_response] + + assert tag_address_response["address_hash"] == address_tag["address_hash"] + assert tag_address_response["name"] == address_tag["name"] + assert tag_address_response["id"] + + new_address_tag = build(:tag_address) + + new_tag_address_response = + conn + |> put("/api/account/v1/user/tags/address/#{tag_address_response["id"]}", new_address_tag) + |> doc(description: "Edit private address tag") + |> json_response(200) + + assert new_tag_address_response["address_hash"] == new_address_tag["address_hash"] + assert new_tag_address_response["name"] == new_address_tag["name"] + assert new_tag_address_response["id"] == tag_address_response["id"] + end + + test "get address tags after inserting one private tags", %{conn: conn} do + addresses = Enum.map(0..2, fn _x -> to_string(build(:address).hash) end) + names = Enum.map(0..2, fn x -> "name#{x}" end) + zipped = Enum.zip(addresses, names) + + created = + Enum.map(zipped, fn {addr, name} -> + id = + (conn + |> post("/api/account/v1/user/tags/address", %{ + "address_hash" => addr, + "name" => name + }) + |> json_response(200))["id"] + + {addr, %{"display_name" => name, "label" => name, "address_hash" => addr}, + %{"address_hash" => addr, "id" => id, "name" => name}} + end) + + assert Enum.all?(created, fn {addr, map_tag, _} -> + response = + conn + |> get("/api/account/v1/tags/address/#{addr}") + |> json_response(200) + + response["personal_tags"] == [map_tag] + end) + + response = + conn + |> get("/api/account/v1/user/tags/address") + |> doc(description: "Get private addresses tags") + |> json_response(200) + + assert Enum.all?(created, fn {_, _, map} -> map in response end) + end + + test "delete address tag", %{conn: conn} do + addresses = Enum.map(0..2, fn _x -> to_string(build(:address).hash) end) + names = Enum.map(0..2, fn x -> "name#{x}" end) + zipped = Enum.zip(addresses, names) + + created = + Enum.map(zipped, fn {addr, name} -> + id = + (conn + |> post("/api/account/v1/user/tags/address", %{ + "address_hash" => addr, + "name" => name + }) + |> json_response(200))["id"] + + {addr, %{"display_name" => name, "label" => name, "address_hash" => addr}, + %{"address_hash" => addr, "id" => id, "name" => name}} + end) + + assert Enum.all?(created, fn {addr, map_tag, _} -> + response = + conn + |> get("/api/account/v1/tags/address/#{addr}") + |> json_response(200) + + response["personal_tags"] == [map_tag] + end) + + response = + conn + |> get("/api/account/v1/user/tags/address") + |> json_response(200) + + assert Enum.all?(created, fn {_, _, map} -> map in response end) + + {_, _, %{"id" => id}} = Enum.at(created, 0) + + assert conn + |> delete("/api/account/v1/user/tags/address/#{id}") + |> doc("Delete private address tag") + |> json_response(200) == %{"message" => "OK"} + + assert Enum.all?(Enum.drop(created, 1), fn {_, _, %{"id" => id}} -> + conn + |> delete("/api/account/v1/user/tags/address/#{id}") + |> json_response(200) == %{"message" => "OK"} + end) + + assert conn + |> get("/api/account/v1/user/tags/address") + |> json_response(200) == [] + + assert Enum.all?(created, fn {addr, _, _} -> + response = + conn + |> get("/api/account/v1/tags/address/#{addr}") + |> json_response(200) + + response["personal_tags"] == [] + end) + end + + test "post private transaction tag", %{conn: conn} do + tx_hash_non_existing = to_string(build(:transaction).hash) + tx_hash = to_string(insert(:transaction).hash) + + assert conn + |> post("/api/account/v1/user/tags/transaction", %{ + "transaction_hash" => tx_hash_non_existing, + "name" => "MyName" + }) + |> doc(description: "Error on try to create private transaction tag for tx does not exist") + |> json_response(422) == %{"errors" => %{"tx_hash" => ["Transaction does not exist"]}} + + tag_transaction_response = + conn + |> post("/api/account/v1/user/tags/transaction", %{ + "transaction_hash" => tx_hash, + "name" => "MyName" + }) + |> doc(description: "Create private transaction tag") + |> json_response(200) + + conn + |> get("/api/account/v1/tags/transaction/#{tx_hash}") + |> doc(description: "Get tags for transaction") + |> json_response(200) + + assert tag_transaction_response["transaction_hash"] == tx_hash + assert tag_transaction_response["name"] == "MyName" + assert tag_transaction_response["id"] + end + + test "edit private transaction tag", %{conn: conn} do + tx_tag = build(:tag_transaction) + + tag_response = + conn + |> post("/api/account/v1/user/tags/transaction", tx_tag) + |> json_response(200) + + _response = + conn + |> get("/api/account/v1/user/tags/transaction") + |> json_response(200) == [tag_response] + + assert tag_response["address_hash"] == tx_tag["address_hash"] + assert tag_response["name"] == tx_tag["name"] + assert tag_response["id"] + + new_tx_tag = build(:tag_transaction) + + new_tag_response = + conn + |> put("/api/account/v1/user/tags/transaction/#{tag_response["id"]}", new_tx_tag) + |> doc(description: "Edit private transaction tag") + |> json_response(200) + + assert new_tag_response["address_hash"] == new_tx_tag["address_hash"] + assert new_tag_response["name"] == new_tx_tag["name"] + assert new_tag_response["id"] == tag_response["id"] + end + + test "get transaction tags after inserting one private tags", %{conn: conn} do + transactions = Enum.map(0..2, fn _x -> to_string(insert(:transaction).hash) end) + names = Enum.map(0..2, fn x -> "name#{x}" end) + zipped = Enum.zip(transactions, names) + + created = + Enum.map(zipped, fn {tx_hash, name} -> + id = + (conn + |> post("/api/account/v1/user/tags/transaction", %{ + "transaction_hash" => tx_hash, + "name" => name + }) + |> json_response(200))["id"] + + {tx_hash, %{"label" => name}, %{"transaction_hash" => tx_hash, "id" => id, "name" => name}} + end) + + assert Enum.all?(created, fn {tx_hash, map_tag, _} -> + response = + conn + |> get("/api/account/v1/tags/transaction/#{tx_hash}") + |> json_response(200) + + response["personal_tx_tag"] == map_tag + end) + + response = + conn + |> get("/api/account/v1/user/tags/transaction") + |> doc(description: "Get private transactions tags") + |> json_response(200) + + assert Enum.all?(created, fn {_, _, map} -> map in response end) + end + + test "delete transaction tag", %{conn: conn} do + transactions = Enum.map(0..2, fn _x -> to_string(insert(:transaction).hash) end) + names = Enum.map(0..2, fn x -> "name#{x}" end) + zipped = Enum.zip(transactions, names) + + created = + Enum.map(zipped, fn {tx_hash, name} -> + id = + (conn + |> post("/api/account/v1/user/tags/transaction", %{ + "transaction_hash" => tx_hash, + "name" => name + }) + |> json_response(200))["id"] + + {tx_hash, %{"label" => name}, %{"transaction_hash" => tx_hash, "id" => id, "name" => name}} + end) + + assert Enum.all?(created, fn {tx_hash, map_tag, _} -> + response = + conn + |> get("/api/account/v1/tags/transaction/#{tx_hash}") + |> json_response(200) + + response["personal_tx_tag"] == map_tag + end) + + response = + conn + |> get("/api/account/v1/user/tags/transaction") + |> json_response(200) + + assert Enum.all?(created, fn {_, _, map} -> map in response end) + + {_, _, %{"id" => id}} = Enum.at(created, 0) + + assert conn + |> delete("/api/account/v1/user/tags/transaction/#{id}") + |> doc("Delete private transaction tag") + |> json_response(200) == %{"message" => "OK"} + + assert Enum.all?(Enum.drop(created, 1), fn {_, _, %{"id" => id}} -> + conn + |> delete("/api/account/v1/user/tags/transaction/#{id}") + |> json_response(200) == %{"message" => "OK"} + end) + + assert conn + |> get("/api/account/v1/user/tags/transaction") + |> json_response(200) == [] + + assert Enum.all?(created, fn {addr, _, _} -> + response = + conn + |> get("/api/account/v1/tags/transaction/#{addr}") + |> json_response(200) + + response["personal_tx_tag"] == nil + end) + end + + test "post && get watchlist address", %{conn: conn} do + watchlist_address_map = build(:watchlist_address) + + post_watchlist_address_response = + conn + |> post( + "/api/account/v1/user/watchlist", + watchlist_address_map + ) + |> doc(description: "Add address to watch list") + |> json_response(200) + + assert post_watchlist_address_response["notification_settings"] == watchlist_address_map["notification_settings"] + assert post_watchlist_address_response["name"] == watchlist_address_map["name"] + assert post_watchlist_address_response["notification_methods"] == watchlist_address_map["notification_methods"] + assert post_watchlist_address_response["address_hash"] == watchlist_address_map["address_hash"] + + get_watchlist_address_response = conn |> get("/api/account/v1/user/watchlist") |> json_response(200) |> Enum.at(0) + + assert get_watchlist_address_response["notification_settings"] == watchlist_address_map["notification_settings"] + assert get_watchlist_address_response["name"] == watchlist_address_map["name"] + assert get_watchlist_address_response["notification_methods"] == watchlist_address_map["notification_methods"] + assert get_watchlist_address_response["address_hash"] == watchlist_address_map["address_hash"] + assert get_watchlist_address_response["id"] == post_watchlist_address_response["id"] + + watchlist_address_map_1 = build(:watchlist_address) + + post_watchlist_address_response_1 = + conn + |> post( + "/api/account/v1/user/watchlist", + watchlist_address_map_1 + ) + |> json_response(200) + + get_watchlist_address_response_1_0 = + conn + |> get("/api/account/v1/user/watchlist") + |> doc(description: "Get addresses from watchlists") + |> json_response(200) + |> Enum.at(1) + + get_watchlist_address_response_1_1 = + conn |> get("/api/account/v1/user/watchlist") |> json_response(200) |> Enum.at(0) + + assert get_watchlist_address_response_1_0 == get_watchlist_address_response + + assert get_watchlist_address_response_1_1["notification_settings"] == + watchlist_address_map_1["notification_settings"] + + assert get_watchlist_address_response_1_1["name"] == watchlist_address_map_1["name"] + + assert get_watchlist_address_response_1_1["notification_methods"] == + watchlist_address_map_1["notification_methods"] + + assert get_watchlist_address_response_1_1["address_hash"] == watchlist_address_map_1["address_hash"] + assert get_watchlist_address_response_1_1["id"] == post_watchlist_address_response_1["id"] + end + + test "delete watchlist address", %{conn: conn} do + watchlist_address_map = build(:watchlist_address) + + post_watchlist_address_response = + conn + |> post( + "/api/account/v1/user/watchlist", + watchlist_address_map + ) + |> json_response(200) + + assert post_watchlist_address_response["notification_settings"] == watchlist_address_map["notification_settings"] + assert post_watchlist_address_response["name"] == watchlist_address_map["name"] + assert post_watchlist_address_response["notification_methods"] == watchlist_address_map["notification_methods"] + assert post_watchlist_address_response["address_hash"] == watchlist_address_map["address_hash"] + + get_watchlist_address_response = conn |> get("/api/account/v1/user/watchlist") |> json_response(200) |> Enum.at(0) + + assert get_watchlist_address_response["notification_settings"] == watchlist_address_map["notification_settings"] + assert get_watchlist_address_response["name"] == watchlist_address_map["name"] + assert get_watchlist_address_response["notification_methods"] == watchlist_address_map["notification_methods"] + assert get_watchlist_address_response["address_hash"] == watchlist_address_map["address_hash"] + assert get_watchlist_address_response["id"] == post_watchlist_address_response["id"] + + watchlist_address_map_1 = build(:watchlist_address) + + post_watchlist_address_response_1 = + conn + |> post( + "/api/account/v1/user/watchlist", + watchlist_address_map_1 + ) + |> json_response(200) + + get_watchlist_address_response_1_0 = + conn |> get("/api/account/v1/user/watchlist") |> json_response(200) |> Enum.at(1) + + get_watchlist_address_response_1_1 = + conn |> get("/api/account/v1/user/watchlist") |> json_response(200) |> Enum.at(0) + + assert get_watchlist_address_response_1_0 == get_watchlist_address_response + + assert get_watchlist_address_response_1_1["notification_settings"] == + watchlist_address_map_1["notification_settings"] + + assert get_watchlist_address_response_1_1["name"] == watchlist_address_map_1["name"] + + assert get_watchlist_address_response_1_1["notification_methods"] == + watchlist_address_map_1["notification_methods"] + + assert get_watchlist_address_response_1_1["address_hash"] == watchlist_address_map_1["address_hash"] + assert get_watchlist_address_response_1_1["id"] == post_watchlist_address_response_1["id"] + + assert conn + |> delete("/api/account/v1/user/watchlist/#{get_watchlist_address_response_1_1["id"]}") + |> doc(description: "Delete address from watchlist by id") + |> json_response(200) == %{"message" => "OK"} + + assert conn + |> delete("/api/account/v1/user/watchlist/#{get_watchlist_address_response_1_0["id"]}") + |> json_response(200) == %{"message" => "OK"} + + assert conn |> get("/api/account/v1/user/watchlist") |> json_response(200) == [] + end + + test "put watchlist address", %{conn: conn} do + watchlist_address_map = build(:watchlist_address) + + post_watchlist_address_response = + conn + |> post( + "/api/account/v1/user/watchlist", + watchlist_address_map + ) + |> json_response(200) + + assert post_watchlist_address_response["notification_settings"] == watchlist_address_map["notification_settings"] + assert post_watchlist_address_response["name"] == watchlist_address_map["name"] + assert post_watchlist_address_response["notification_methods"] == watchlist_address_map["notification_methods"] + assert post_watchlist_address_response["address_hash"] == watchlist_address_map["address_hash"] + + get_watchlist_address_response = conn |> get("/api/account/v1/user/watchlist") |> json_response(200) |> Enum.at(0) + + assert get_watchlist_address_response["notification_settings"] == watchlist_address_map["notification_settings"] + assert get_watchlist_address_response["name"] == watchlist_address_map["name"] + assert get_watchlist_address_response["notification_methods"] == watchlist_address_map["notification_methods"] + assert get_watchlist_address_response["address_hash"] == watchlist_address_map["address_hash"] + assert get_watchlist_address_response["id"] == post_watchlist_address_response["id"] + + new_watchlist_address_map = build(:watchlist_address) + + put_watchlist_address_response = + conn + |> put( + "/api/account/v1/user/watchlist/#{post_watchlist_address_response["id"]}", + new_watchlist_address_map + ) + |> doc(description: "Edit watchlist address") + |> json_response(200) + + assert put_watchlist_address_response["notification_settings"] == + new_watchlist_address_map["notification_settings"] + + assert put_watchlist_address_response["name"] == new_watchlist_address_map["name"] + assert put_watchlist_address_response["notification_methods"] == new_watchlist_address_map["notification_methods"] + assert put_watchlist_address_response["address_hash"] == new_watchlist_address_map["address_hash"] + assert get_watchlist_address_response["id"] == put_watchlist_address_response["id"] + end + + test "cannot create duplicate of watchlist address", %{conn: conn} do + watchlist_address_map = build(:watchlist_address) + + post_watchlist_address_response = + conn + |> post( + "/api/account/v1/user/watchlist", + watchlist_address_map + ) + |> json_response(200) + + assert post_watchlist_address_response["notification_settings"] == watchlist_address_map["notification_settings"] + assert post_watchlist_address_response["name"] == watchlist_address_map["name"] + assert post_watchlist_address_response["notification_methods"] == watchlist_address_map["notification_methods"] + assert post_watchlist_address_response["address_hash"] == watchlist_address_map["address_hash"] + + assert conn + |> post( + "/api/account/v1/user/watchlist", + watchlist_address_map + ) + |> doc(description: "Example of error on creating watchlist address") + |> json_response(422) == %{"errors" => %{"watchlist_id" => ["Address already added to the watch list"]}} + + new_watchlist_address_map = build(:watchlist_address) + + post_watchlist_address_response_1 = + conn + |> post( + "/api/account/v1/user/watchlist", + new_watchlist_address_map + ) + |> json_response(200) + + assert conn + |> put( + "/api/account/v1/user/watchlist/#{post_watchlist_address_response_1["id"]}", + watchlist_address_map + ) + |> doc(description: "Example of error on editing watchlist address") + |> json_response(422) == %{"errors" => %{"watchlist_id" => ["Address already added to the watch list"]}} + end + + test "post api key", %{conn: conn} do + post_api_key_response = + conn + |> post( + "/api/account/v1/user/api_keys", + %{"name" => "test"} + ) + |> doc(description: "Add api key") + |> json_response(200) + + assert post_api_key_response["name"] == "test" + assert post_api_key_response["api_key"] + end + + test "can create not more than 3 api keys + get api keys", %{conn: conn} do + Enum.each(0..2, fn _x -> + conn + |> post( + "/api/account/v1/user/api_keys", + %{"name" => "test"} + ) + |> json_response(200) + end) + + assert conn + |> post( + "/api/account/v1/user/api_keys", + %{"name" => "test"} + ) + |> doc(description: "Example of error on creating api key") + |> json_response(422) == %{"errors" => %{"name" => ["Max 3 keys per account"]}} + + assert conn + |> get("/api/account/v1/user/api_keys") + |> doc(description: "Get api keys list") + |> json_response(200) + |> Enum.count() == 3 + end + + test "edit api key", %{conn: conn} do + post_api_key_response = + conn + |> post( + "/api/account/v1/user/api_keys", + %{"name" => "test"} + ) + |> json_response(200) + + assert post_api_key_response["name"] == "test" + assert post_api_key_response["api_key"] + + put_api_key_response = + conn + |> put( + "/api/account/v1/user/api_keys/#{post_api_key_response["api_key"]}", + %{"name" => "test_1"} + ) + |> doc(description: "Edit api key") + |> json_response(200) + + assert put_api_key_response["api_key"] == post_api_key_response["api_key"] + assert put_api_key_response["name"] == "test_1" + + assert conn + |> get("/api/account/v1/user/api_keys") + |> json_response(200) == [put_api_key_response] + end + + test "delete api key", %{conn: conn} do + post_api_key_response = + conn + |> post( + "/api/account/v1/user/api_keys", + %{"name" => "test"} + ) + |> json_response(200) + + assert post_api_key_response["name"] == "test" + assert post_api_key_response["api_key"] + + assert conn + |> get("/api/account/v1/user/api_keys") + |> json_response(200) + |> Enum.count() == 1 + + assert conn + |> delete("/api/account/v1/user/api_keys/#{post_api_key_response["api_key"]}") + |> doc(description: "Delete api key") + |> json_response(200) == %{"message" => "OK"} + + assert conn + |> get("/api/account/v1/user/api_keys") + |> json_response(200) == [] + end + + test "post custom abi", %{conn: conn} do + custom_abi = build(:custom_abi) + + post_custom_abi_response = + conn + |> post( + "/api/account/v1/user/custom_abis", + custom_abi + ) + |> doc(description: "Add custom abi") + |> json_response(200) + + assert post_custom_abi_response["name"] == custom_abi["name"] + assert post_custom_abi_response["abi"] == custom_abi["abi"] + assert post_custom_abi_response["contract_address_hash"] == custom_abi["contract_address_hash"] + assert post_custom_abi_response["id"] + end + + test "can create not more than 15 custom abis + get custom abi", %{conn: conn} do + Enum.each(0..14, fn _x -> + conn + |> post( + "/api/account/v1/user/custom_abis", + build(:custom_abi) + ) + |> json_response(200) + end) + + assert conn + |> post( + "/api/account/v1/user/custom_abis", + build(:custom_abi) + ) + |> doc(description: "Example of error on creating custom abi") + |> json_response(422) == %{"errors" => %{"name" => ["Max 15 ABIs per account"]}} + + assert conn + |> get("/api/account/v1/user/custom_abis") + |> doc(description: "Get custom abis list") + |> json_response(200) + |> Enum.count() == 15 + end + + test "edit custom abi", %{conn: conn} do + custom_abi = build(:custom_abi) + + post_custom_abi_response = + conn + |> post( + "/api/account/v1/user/custom_abis", + custom_abi + ) + |> json_response(200) + + assert post_custom_abi_response["name"] == custom_abi["name"] + assert post_custom_abi_response["abi"] == custom_abi["abi"] + assert post_custom_abi_response["contract_address_hash"] == custom_abi["contract_address_hash"] + assert post_custom_abi_response["id"] + + custom_abi_1 = build(:custom_abi) + + put_custom_abi_response = + conn + |> put( + "/api/account/v1/user/custom_abis/#{post_custom_abi_response["id"]}", + custom_abi_1 + ) + |> doc(description: "Edit custom abi") + |> json_response(200) + + assert put_custom_abi_response["name"] == custom_abi_1["name"] + assert put_custom_abi_response["id"] == post_custom_abi_response["id"] + assert put_custom_abi_response["contract_address_hash"] == custom_abi_1["contract_address_hash"] + assert put_custom_abi_response["abi"] == custom_abi_1["abi"] + + assert conn + |> get("/api/account/v1/user/custom_abis") + |> json_response(200) == [put_custom_abi_response] + end + + test "delete custom abi", %{conn: conn} do + custom_abi = build(:custom_abi) + + post_custom_abi_response = + conn + |> post( + "/api/account/v1/user/custom_abis", + custom_abi + ) + |> json_response(200) + + assert post_custom_abi_response["name"] == custom_abi["name"] + assert post_custom_abi_response["id"] + + assert conn + |> get("/api/account/v1/user/custom_abis") + |> json_response(200) + |> Enum.count() == 1 + + assert conn + |> delete("/api/account/v1/user/custom_abis/#{post_custom_abi_response["id"]}") + |> doc(description: "Delete custom abi") + |> json_response(200) == %{"message" => "OK"} + + assert conn + |> get("/api/account/v1/user/custom_abis") + |> json_response(200) == [] + end + end + + describe "public tags" do + test "create public tags reuqest", %{conn: conn} do + public_tags_request = build(:public_tags_request) + + post_public_tasg_request_response = + conn + |> post( + "/api/account/v1/user/public_tags", + public_tags_request + ) + |> doc(description: "Submit request to add a public tag") + |> json_response(200) + + assert post_public_tasg_request_response["full_name"] == public_tags_request["full_name"] + assert post_public_tasg_request_response["email"] == public_tags_request["email"] + assert post_public_tasg_request_response["tags"] == public_tags_request["tags"] + assert post_public_tasg_request_response["website"] == public_tags_request["website"] + assert post_public_tasg_request_response["additional_comment"] == public_tags_request["additional_comment"] + assert post_public_tasg_request_response["addresses"] == public_tags_request["addresses"] + assert post_public_tasg_request_response["company"] == public_tags_request["company"] + assert post_public_tasg_request_response["is_owner"] == public_tags_request["is_owner"] + assert post_public_tasg_request_response["id"] + end + + test "get one public tags requests", %{conn: conn} do + public_tags_request = build(:public_tags_request) + + post_public_tasg_request_response = + conn + |> post( + "/api/account/v1/user/public_tags", + public_tags_request + ) + |> json_response(200) + + assert post_public_tasg_request_response["full_name"] == public_tags_request["full_name"] + assert post_public_tasg_request_response["email"] == public_tags_request["email"] + assert post_public_tasg_request_response["tags"] == public_tags_request["tags"] + assert post_public_tasg_request_response["website"] == public_tags_request["website"] + assert post_public_tasg_request_response["additional_comment"] == public_tags_request["additional_comment"] + assert post_public_tasg_request_response["addresses"] == public_tags_request["addresses"] + assert post_public_tasg_request_response["company"] == public_tags_request["company"] + assert post_public_tasg_request_response["is_owner"] == public_tags_request["is_owner"] + assert post_public_tasg_request_response["id"] + + assert conn + |> get("/api/account/v1/user/public_tags") + |> json_response(200) + |> Enum.map(&convert_date/1) == + [post_public_tasg_request_response] + |> Enum.map(&convert_date/1) + end + + test "get and delete several public tags requests", %{conn: conn} do + public_tags_list = build_list(10, :public_tags_request) + + final_list = + public_tags_list + |> Enum.map(fn request -> + response = + conn + |> post( + "/api/account/v1/user/public_tags", + request + ) + |> json_response(200) + + assert response["full_name"] == request["full_name"] + assert response["email"] == request["email"] + assert response["tags"] == request["tags"] + assert response["website"] == request["website"] + assert response["additional_comment"] == request["additional_comment"] + assert response["addresses"] == request["addresses"] + assert response["company"] == request["company"] + assert response["is_owner"] == request["is_owner"] + assert response["id"] + + convert_date(response) + end) + |> Enum.reverse() + + assert conn + |> get("/api/account/v1/user/public_tags") + |> doc(description: "Get list of requests to add a public tag") + |> json_response(200) + |> Enum.map(&convert_date/1) == final_list + + %{"id" => id} = Enum.at(final_list, 0) + + assert conn + |> delete("/api/account/v1/user/public_tags/#{id}", %{"remove_reason" => "reason"}) + |> doc(description: "Delete public tags request") + |> json_response(200) == %{"message" => "OK"} + + Enum.each(Enum.drop(final_list, 1), fn request -> + assert conn + |> delete("/api/account/v1/user/public_tags/#{request["id"]}", %{"remove_reason" => "reason"}) + |> json_response(200) == %{"message" => "OK"} + end) + + assert conn + |> get("/api/account/v1/user/public_tags") + |> json_response(200) == [] + end + + test "edit public tags request", %{conn: conn} do + public_tags_request = build(:public_tags_request) + + post_public_tasg_request_response = + conn + |> post( + "/api/account/v1/user/public_tags", + public_tags_request + ) + |> json_response(200) + + assert post_public_tasg_request_response["full_name"] == public_tags_request["full_name"] + assert post_public_tasg_request_response["email"] == public_tags_request["email"] + assert post_public_tasg_request_response["tags"] == public_tags_request["tags"] + assert post_public_tasg_request_response["website"] == public_tags_request["website"] + assert post_public_tasg_request_response["additional_comment"] == public_tags_request["additional_comment"] + assert post_public_tasg_request_response["addresses"] == public_tags_request["addresses"] + assert post_public_tasg_request_response["company"] == public_tags_request["company"] + assert post_public_tasg_request_response["is_owner"] == public_tags_request["is_owner"] + assert post_public_tasg_request_response["id"] + + assert conn + |> get("/api/account/v1/user/public_tags") + |> json_response(200) + |> Enum.map(&convert_date/1) == + [post_public_tasg_request_response] + |> Enum.map(&convert_date/1) + + new_public_tags_request = build(:public_tags_request) + + put_public_tasg_request_response = + conn + |> put( + "/api/account/v1/user/public_tags/#{post_public_tasg_request_response["id"]}", + new_public_tags_request + ) + |> doc(description: "Edit request to add a public tag") + |> json_response(200) + + assert put_public_tasg_request_response["full_name"] == new_public_tags_request["full_name"] + assert put_public_tasg_request_response["email"] == new_public_tags_request["email"] + assert put_public_tasg_request_response["tags"] == new_public_tags_request["tags"] + assert put_public_tasg_request_response["website"] == new_public_tags_request["website"] + assert put_public_tasg_request_response["additional_comment"] == new_public_tags_request["additional_comment"] + assert put_public_tasg_request_response["addresses"] == new_public_tags_request["addresses"] + assert put_public_tasg_request_response["company"] == new_public_tags_request["company"] + assert put_public_tasg_request_response["is_owner"] == new_public_tags_request["is_owner"] + assert put_public_tasg_request_response["id"] == post_public_tasg_request_response["id"] + + assert conn + |> get("/api/account/v1/user/public_tags") + |> json_response(200) + |> Enum.map(&convert_date/1) == + [put_public_tasg_request_response] + |> Enum.map(&convert_date/1) + end + end + + def convert_date(request) do + {:ok, time, _} = DateTime.from_iso8601(request["submission_date"]) + %{request | "submission_date" => Calendar.strftime(time, "%b %d, %Y")} + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/account/custom_abi_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/account/custom_abi_controller_test.exs new file mode 100644 index 0000000..89ff276 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/account/custom_abi_controller_test.exs @@ -0,0 +1,186 @@ +defmodule BlockScoutWeb.Account.CustomABIControllerTest do + use BlockScoutWeb.ConnCase + + alias BlockScoutWeb.Models.UserFromAuth + + @custom_abi "[{\"type\":\"function\",\"outputs\":[{\"type\":\"string\",\"name\":\"\"}],\"name\":\"name\",\"inputs\":[],\"constant\":true}]" + + setup %{conn: conn} do + auth = build(:auth) + + {:ok, user} = UserFromAuth.find_or_create(auth) + + {:ok, conn: Plug.Test.init_test_session(conn, current_user: user)} + end + + describe "test custom ABI functionality" do + test "custom ABI page opens correctly", %{conn: conn} do + result_conn = + conn + |> get(custom_abi_path(conn, :index)) + + assert html_response(result_conn, 200) =~ "Create a Custom ABI to interact with contracts." + end + + test "do not add custom ABI with wrong ABI", %{conn: conn} do + contract_address = insert(:address, contract_code: "0x0102") + + custom_abi = %{ + "name" => "1", + "address_hash" => to_string(contract_address), + "abi" => "" + } + + result_conn = + conn + |> post(custom_abi_path(conn, :create, %{"custom_abi" => custom_abi})) + + assert html_response(result_conn, 200) =~ "Add Custom ABI" + assert html_response(result_conn, 200) =~ to_string(contract_address.hash) + assert html_response(result_conn, 200) =~ "Required" + + result_conn_1 = + conn + |> post(custom_abi_path(conn, :create, %{"custom_abi" => Map.put(custom_abi, "abi", "123")})) + + assert html_response(result_conn_1, 200) =~ "Add Custom ABI" + assert html_response(result_conn_1, 200) =~ to_string(contract_address.hash) + assert html_response(result_conn_1, 200) =~ "Invalid format" + + result_conn_2 = + conn + |> get(custom_abi_path(conn, :index)) + + assert html_response(result_conn_2, 200) =~ "Create a Custom ABI to interact with contracts." + refute html_response(result_conn_2, 200) =~ to_string(contract_address.hash) + end + + test "add one custom abi and do not allow to create duplicates", %{conn: conn} do + contract_address = insert(:contract_address, contract_code: "0x0102") + + custom_abi = %{ + "name" => "1", + "address_hash" => to_string(contract_address), + "abi" => @custom_abi + } + + result_conn = + conn + |> post(custom_abi_path(conn, :create, %{"custom_abi" => custom_abi})) + + assert redirected_to(result_conn) == custom_abi_path(conn, :index) + + result_conn_2 = get(result_conn, custom_abi_path(conn, :index)) + assert html_response(result_conn_2, 200) =~ to_string(contract_address.hash) + assert html_response(result_conn_2, 200) =~ "Create a Custom ABI to interact with contracts." + + result_conn_1 = + conn + |> post(custom_abi_path(conn, :create, %{"custom_abi" => custom_abi})) + + assert html_response(result_conn_1, 200) =~ "Add Custom ABI" + assert html_response(result_conn_1, 200) =~ to_string(contract_address.hash) + assert html_response(result_conn_1, 200) =~ "Custom ABI for this address has already been added before" + end + + test "show error on address which is not smart contract", %{conn: conn} do + contract_address = insert(:address) + + custom_abi = %{ + "name" => "1", + "address_hash" => to_string(contract_address), + "abi" => @custom_abi + } + + result_conn = + conn + |> post(custom_abi_path(conn, :create, %{"custom_abi" => custom_abi})) + + assert html_response(result_conn, 200) =~ "Add Custom ABI" + assert html_response(result_conn, 200) =~ to_string(contract_address.hash) + assert html_response(result_conn, 200) =~ "Address is not a smart contract" + end + + test "user can add up to 15 custom ABIs", %{conn: conn} do + addresses = + Enum.map(1..15, fn _x -> + address = insert(:contract_address, contract_code: "0x0102") + + custom_abi = %{ + "name" => "1", + "address_hash" => to_string(address), + "abi" => @custom_abi + } + + assert conn + |> post(custom_abi_path(conn, :create, %{"custom_abi" => custom_abi})) + |> redirected_to() == custom_abi_path(conn, :index) + + to_string(address.hash) + end) + + assert abi_list = + conn + |> get(custom_abi_path(conn, :index)) + |> html_response(200) + + Enum.each(addresses, fn address -> assert abi_list =~ address end) + + address = insert(:contract_address, contract_code: "0x0102") + + custom_abi = %{ + "name" => "1", + "address_hash" => to_string(address), + "abi" => @custom_abi + } + + assert error_form = + conn + |> post(custom_abi_path(conn, :create, %{"custom_abi" => custom_abi})) + |> html_response(200) + + assert error_form =~ "Add Custom ABI" + assert error_form =~ "Max 15 ABIs per account" + assert error_form =~ to_string(address.hash) + + assert abi_list_new = + conn + |> get(custom_abi_path(conn, :index)) + |> html_response(200) + + Enum.each(addresses, fn address -> assert abi_list_new =~ address end) + + refute abi_list_new =~ to_string(address.hash) + assert abi_list_new =~ "You can create up to 15 Custom ABIs per account." + end + + test "after adding custom ABI on address page appear Read/Write Contract tab", %{conn: conn} do + contract_address = insert(:contract_address, contract_code: "0x0102") + + custom_abi = %{ + "name" => "1", + "address_hash" => to_string(contract_address), + "abi" => + "[{\"type\":\"function\",\"outputs\":[{\"type\":\"string\",\"name\":\"\"}],\"name\":\"name\",\"inputs\":[],\"constant\":true},{\"type\":\"function\",\"outputs\":[{\"type\":\"bool\",\"name\":\"success\"}],\"name\":\"approve\",\"inputs\":[{\"type\":\"address\",\"name\":\"_spender\"},{\"type\":\"uint256\",\"name\":\"_value\"}],\"constant\":false}]" + } + + result_conn = + conn + |> post(custom_abi_path(conn, :create, %{"custom_abi" => custom_abi})) + + assert redirected_to(result_conn) == custom_abi_path(conn, :index) + + result_conn_2 = get(result_conn, custom_abi_path(conn, :index)) + assert html_response(result_conn_2, 200) =~ to_string(contract_address.hash) + assert html_response(result_conn_2, 200) =~ "Create a Custom ABI to interact with contracts." + + assert contract_page = + result_conn + |> get(address_contract_path(result_conn, :index, to_string(contract_address))) + |> html_response(200) + + assert contract_page =~ "Write Contract" + assert contract_page =~ "Read Contract" + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_coin_balance_by_day_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_coin_balance_by_day_controller_test.exs new file mode 100644 index 0000000..8d94957 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_coin_balance_by_day_controller_test.exs @@ -0,0 +1,27 @@ +defmodule BlockScoutWeb.AddressCoinBalanceByDayControllerTest do + use BlockScoutWeb.ConnCase + + alias Explorer.Chain.Address + + describe "GET index/2" do + test "returns the coin balance history grouped by date", %{conn: conn} do + address = insert(:address) + noon = Timex.now() |> Timex.beginning_of_day() |> Timex.set(hour: 12) + block = insert(:block, timestamp: noon, number: 2) + block_one_day_ago = insert(:block, timestamp: Timex.shift(noon, days: -1), number: 1) + insert(:fetched_balance, address_hash: address.hash, value: 1000, block_number: block.number) + insert(:fetched_balance, address_hash: address.hash, value: 2000, block_number: block_one_day_ago.number) + insert(:fetched_balance_daily, address_hash: address.hash, value: 1000, day: noon) + insert(:fetched_balance_daily, address_hash: address.hash, value: 2000, day: Timex.shift(noon, days: -1)) + + conn = get(conn, address_coin_balance_by_day_path(conn, :index, Address.checksum(address)), %{"type" => "JSON"}) + + response = json_response(conn, 200) + + assert [ + %{"date" => _, "value" => 2.0e-15}, + %{"date" => _, "value" => 1.0e-15} + ] = response + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_contract_controller_test.exs new file mode 100644 index 0000000..57e608a --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_contract_controller_test.exs @@ -0,0 +1,57 @@ +defmodule BlockScoutWeb.AddressContractControllerTest do + use BlockScoutWeb.ConnCase, async: true + + import BlockScoutWeb.WebRouter.Helpers, only: [address_contract_path: 3] + + alias Explorer.Chain.{Address, Hash} + alias Explorer.ExchangeRates.Token + alias Explorer.Factory + + describe "GET index/3" do + test "returns not found for nonexistent address", %{conn: conn} do + nonexistent_address_hash = Hash.to_string(Factory.address_hash()) + + conn = + get(conn, address_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(nonexistent_address_hash))) + + assert html_response(conn, 404) + end + + test "returns not found given an invalid address hash ", %{conn: conn} do + invalid_hash = "invalid_hash" + + conn = get(conn, address_contract_path(BlockScoutWeb.Endpoint, :index, invalid_hash)) + + assert html_response(conn, 404) + end + + test "returns not found when the address isn't a contract", %{conn: conn} do + address = insert(:address) + + conn = get(conn, address_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address))) + + assert html_response(conn, 404) + end + + test "successfully renders the page when the address is a contract", %{conn: conn} do + address = insert(:address, contract_code: Factory.data("contract_code"), smart_contract: nil) + + transaction = insert(:transaction, from_address: address) |> with_block() + + insert( + :internal_transaction_create, + index: 0, + transaction: transaction, + created_contract_address: address, + block_hash: transaction.block_hash, + block_index: 0 + ) + + conn = get(conn, address_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address))) + + assert html_response(conn, 200) + assert address.hash == conn.assigns.address.hash + assert %Token{} = conn.assigns.exchange_rate + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_controller_test.exs new file mode 100644 index 0000000..10c133d --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_controller_test.exs @@ -0,0 +1,97 @@ +defmodule BlockScoutWeb.AddressControllerTest do + use BlockScoutWeb.ConnCase, + # ETS tables are shared in `Explorer.Counters.*` + async: false + + import Mox + + alias Explorer.Chain.Address + alias Explorer.Counters.{AddressesCounter} + + describe "GET index/2" do + setup :set_mox_global + + setup do + # Use TestSource mock for this test set + configuration = Application.get_env(:block_scout_web, :show_percentage) + Application.put_env(:block_scout_web, :show_percentage, false) + + :ok + + on_exit(fn -> + Application.put_env(:block_scout_web, :show_percentage, configuration) + end) + end + + test "returns top addresses", %{conn: conn} do + address_hashes = + 4..1 + |> Enum.map(&insert(:address, fetched_coin_balance: &1)) + |> Enum.map(& &1.hash) + + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + conn = get(conn, address_path(conn, :index, %{type: "JSON"})) + {:ok, %{"items" => items}} = Poison.decode(conn.resp_body) + + assert Enum.count(items) == Enum.count(address_hashes) + end + + test "returns an address's primary name when present", %{conn: conn} do + address = insert(:address, fetched_coin_balance: 1) + insert(:address_name, address: address, primary: true, name: "POA Wallet") + + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + conn = get(conn, address_path(conn, :index, %{type: "JSON"})) + + {:ok, %{"items" => [item]}} = Poison.decode(conn.resp_body) + + assert String.contains?(item, "POA Wallet") + end + end + + describe "GET show/3" do + setup :set_mox_global + + setup do + configuration = Application.get_env(:explorer, :checksum_function) + Application.put_env(:explorer, :checksum_function, :eth) + + :ok + + on_exit(fn -> + Application.put_env(:explorer, :checksum_function, configuration) + end) + end + + test "redirects to address/:address_id/transactions", %{conn: conn} do + insert(:address, hash: "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed") + + conn = get(conn, "/address/0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed") + + assert html_response(conn, 200) + end + end + + describe "GET address-counters/2" do + test "returns address counters", %{conn: conn} do + address = insert(:address) + + conn = get(conn, "/address-counters", %{"id" => Address.checksum(address.hash)}) + + assert conn.status == 200 + {:ok, response} = Jason.decode(conn.resp_body) + + assert %{ + "transaction_count" => 0, + "token_transfer_count" => 0, + "validation_count" => 0, + "gas_usage_count" => 0 + } == + response + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs new file mode 100644 index 0000000..a78aff7 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_internal_transaction_controller_test.exs @@ -0,0 +1,405 @@ +defmodule BlockScoutWeb.AddressInternalTransactionControllerTest do + use BlockScoutWeb.ConnCase, async: true + + import BlockScoutWeb.WebRouter.Helpers, + only: [address_internal_transaction_path: 3, address_internal_transaction_path: 4] + + alias Explorer.Chain.{Address, Block, InternalTransaction, Transaction} + alias Explorer.ExchangeRates.Token + + describe "GET index/3" do + test "with invalid address hash", %{conn: conn} do + conn = + conn + |> get(address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, "invalid_address")) + + assert html_response(conn, 404) + end + + test "with valid address hash without address", %{conn: conn} do + conn = + get( + conn, + address_internal_transaction_path( + conn, + :index, + Address.checksum("0x8bf38d4764929064f2d4d3a56520a76ab3df415b") + ) + ) + + assert html_response(conn, 200) + end + + test "includes USD exchange rate value for address in assigns", %{conn: conn} do + address = insert(:address) + + conn = + get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash))) + + assert %Token{} = conn.assigns.exchange_rate + end + + test "returns internal transactions for the address", %{conn: conn} do + address = insert(:address) + + transaction = + :transaction + |> insert() + |> with_block(insert(:block, number: 1)) + + from_internal_transaction = + insert(:internal_transaction, + transaction: transaction, + from_address: address, + index: 1, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 1 + ) + + to_internal_transaction = + insert(:internal_transaction, + transaction: transaction, + to_address: address, + index: 2, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 2 + ) + + path = address_internal_transaction_path(conn, :index, Address.checksum(address), %{"type" => "JSON"}) + conn = get(conn, path) + + internal_transaction_tiles = json_response(conn, 200)["items"] + + assert Enum.all?([from_internal_transaction, to_internal_transaction], fn internal_transaction -> + Enum.any?(internal_transaction_tiles, fn tile -> + String.contains?(tile, to_string(internal_transaction.transaction_hash)) && + String.contains?(tile, "data-internal-transaction-index=\"#{internal_transaction.index}\"") + end) + end) + end + + test "returns internal transactions coming from the address", %{conn: conn} do + address = insert(:address) + + transaction = + :transaction + |> insert() + |> with_block(insert(:block, number: 1)) + + from_internal_transaction = + insert(:internal_transaction, + transaction: transaction, + from_address: address, + index: 1, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 1 + ) + + to_internal_transaction = + insert(:internal_transaction, + transaction: transaction, + to_address: address, + index: 2, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 2 + ) + + path = + address_internal_transaction_path(conn, :index, Address.checksum(address), %{ + "filter" => "from", + "type" => "JSON" + }) + + conn = get(conn, path) + + internal_transaction_tiles = json_response(conn, 200)["items"] + + assert Enum.any?(internal_transaction_tiles, fn tile -> + String.contains?(tile, to_string(from_internal_transaction.transaction_hash)) && + String.contains?(tile, "data-internal-transaction-index=\"#{from_internal_transaction.index}\"") + end) + + refute Enum.any?(internal_transaction_tiles, fn tile -> + String.contains?(tile, to_string(to_internal_transaction.transaction_hash)) && + String.contains?(tile, "data-internal-transaction-index=\"#{to_internal_transaction.index}\"") + end) + end + + test "returns internal transactions going to the address", %{conn: conn} do + address = insert(:address) + + transaction = + :transaction + |> insert() + |> with_block(insert(:block, number: 1)) + + from_internal_transaction = + insert(:internal_transaction, + transaction: transaction, + from_address: address, + index: 1, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 1 + ) + + to_internal_transaction = + insert(:internal_transaction, + transaction: transaction, + to_address: address, + index: 2, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 2 + ) + + path = + address_internal_transaction_path(conn, :index, Address.checksum(address), %{"filter" => "to", "type" => "JSON"}) + + conn = get(conn, path) + + internal_transaction_tiles = json_response(conn, 200)["items"] + + assert Enum.any?(internal_transaction_tiles, fn tile -> + String.contains?(tile, to_string(to_internal_transaction.transaction_hash)) && + String.contains?(tile, "data-internal-transaction-index=\"#{to_internal_transaction.index}\"") + end) + + refute Enum.any?(internal_transaction_tiles, fn tile -> + String.contains?(tile, to_string(from_internal_transaction.transaction_hash)) && + String.contains?(tile, "data-internal-transaction-index=\"#{from_internal_transaction.index}\"") + end) + end + + test "returns internal an transaction that created the address", %{conn: conn} do + address = insert(:address) + + transaction = + :transaction + |> insert() + |> with_block(insert(:block, number: 1)) + + from_internal_transaction = + insert(:internal_transaction, + transaction: transaction, + from_address: address, + index: 1, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 1 + ) + + to_internal_transaction = + insert(:internal_transaction, + transaction: transaction, + to_address: nil, + created_contract_address: address, + index: 2, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 2 + ) + + path = + address_internal_transaction_path(conn, :index, Address.checksum(address), %{"filter" => "to", "type" => "JSON"}) + + conn = get(conn, path) + + internal_transaction_tiles = json_response(conn, 200)["items"] + + assert Enum.any?(internal_transaction_tiles, fn tile -> + String.contains?(tile, to_string(to_internal_transaction.transaction_hash)) && + String.contains?(tile, "data-internal-transaction-index=\"#{to_internal_transaction.index}\"") + end) + + refute Enum.any?(internal_transaction_tiles, fn tile -> + String.contains?(tile, to_string(from_internal_transaction.transaction_hash)) && + String.contains?(tile, "data-internal-transaction-index=\"#{from_internal_transaction.index}\"") + end) + end + + test "returns next page of results based on last seen internal transaction", %{conn: conn} do + address = insert(:address) + + a_block = insert(:block, number: 1000) + b_block = insert(:block, number: 2000) + + transaction_1 = + :transaction + |> insert() + |> with_block(a_block) + + transaction_2 = + :transaction + |> insert() + |> with_block(a_block) + + transaction_3 = + :transaction + |> insert() + |> with_block(b_block) + + transaction_1_hashes = + 1..20 + |> Enum.map(fn index -> + insert( + :internal_transaction, + transaction: transaction_1, + from_address: address, + index: index, + block_number: transaction_1.block_number, + transaction_index: transaction_1.index, + block_hash: a_block.hash, + block_index: index + ) + end) + + transaction_2_hashes = + 1..20 + |> Enum.map(fn index -> + insert( + :internal_transaction, + transaction: transaction_2, + from_address: address, + index: index, + block_number: transaction_2.block_number, + transaction_index: transaction_2.index, + block_hash: a_block.hash, + block_index: 20 + index + ) + end) + + transaction_3_hashes = + 1..10 + |> Enum.map(fn index -> + insert( + :internal_transaction, + transaction: transaction_3, + from_address: address, + index: index, + block_number: transaction_3.block_number, + transaction_index: transaction_3.index, + block_hash: b_block.hash, + block_index: index + ) + end) + + second_page = transaction_1_hashes ++ transaction_2_hashes ++ transaction_3_hashes + + %InternalTransaction{index: index} = + insert( + :internal_transaction, + transaction: transaction_3, + from_address: address, + index: 11, + block_number: transaction_3.block_number, + transaction_index: transaction_3.index, + block_hash: b_block.hash, + block_index: 11 + ) + + conn = + get(conn, address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash)), %{ + "block_number" => Integer.to_string(b_block.number), + "transaction_index" => Integer.to_string(transaction_3.index), + "index" => Integer.to_string(index), + "type" => "JSON" + }) + + internal_transaction_tiles = json_response(conn, 200)["items"] + + assert Enum.all?(second_page, fn internal_transaction -> + Enum.any?(internal_transaction_tiles, fn tile -> + String.contains?(tile, to_string(internal_transaction.transaction_hash)) && + String.contains?(tile, "data-internal-transaction-index=\"#{internal_transaction.index}\"") + end) + end) + end + + test "next_page_params exist if not on last page", %{conn: conn} do + address = insert(:address) + block = %Block{number: number} = insert(:block, number: 7000) + + transaction = + %Transaction{index: transaction_index} = + :transaction + |> insert() + |> with_block(block) + + 1..60 + |> Enum.map(fn index -> + insert( + :internal_transaction, + transaction: transaction, + from_address: address, + index: index, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: index + ) + end) + + conn = + get( + conn, + address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash), %{ + "type" => "JSON" + }) + ) + + expected_response = + address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, address.hash, %{ + "block_number" => number, + "index" => 11, + "transaction_index" => transaction_index, + "items_count" => "50" + }) + + assert expected_response == json_response(conn, 200)["next_page_path"] + end + + test "next_page_params are empty if on last page", %{conn: conn} do + address = insert(:address) + + transaction = + :transaction + |> insert() + |> with_block() + + 1..2 + |> Enum.map(fn index -> + insert( + :internal_transaction, + transaction: transaction, + from_address: address, + index: index, + block_hash: transaction.block_hash, + block_index: index + ) + end) + + conn = + get( + conn, + address_internal_transaction_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash), %{ + "type" => "JSON" + }) + ) + + assert %{"next_page_path" => nil} = json_response(conn, 200) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs new file mode 100644 index 0000000..6a01c12 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs @@ -0,0 +1,130 @@ +defmodule BlockScoutWeb.AddressReadContractControllerTest do + use BlockScoutWeb.ConnCase, async: true + use ExUnit.Case, async: false + + alias Explorer.ExchangeRates.Token + alias Explorer.Chain.Address + + import Mox + + describe "GET index/3" do + setup :set_mox_global + + setup do + configuration = Application.get_env(:explorer, :checksum_function) + Application.put_env(:explorer, :checksum_function, :eth) + + :ok + + on_exit(fn -> + Application.put_env(:explorer, :checksum_function, configuration) + end) + end + + test "with invalid address hash", %{conn: conn} do + conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, "invalid_address")) + + assert html_response(conn, 404) + end + + test "with valid address that is not a contract", %{conn: conn} do + address = insert(:address) + + conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash))) + + assert html_response(conn, 404) + end + + test "successfully renders the page when the address is a contract", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = insert(:transaction, from_address: contract_address) |> with_block() + + insert( + :internal_transaction_create, + index: 0, + transaction: transaction, + created_contract_address: contract_address, + block_hash: transaction.block_hash, + block_index: 0 + ) + + insert(:smart_contract, address_hash: contract_address.hash, contract_code_md5: "123") + + get_eip1967_implementation() + + conn = + get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) + + assert html_response(conn, 200) + assert contract_address.hash == conn.assigns.address.hash + assert %Token{} = conn.assigns.exchange_rate + end + + test "returns not found for an unverified contract", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = insert(:transaction, from_address: contract_address) |> with_block() + + insert( + :internal_transaction_create, + index: 0, + transaction: transaction, + created_contract_address: contract_address, + block_hash: transaction.block_hash, + block_index: 0 + ) + + conn = + get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) + + assert html_response(conn, 404) + end + end + + def get_eip1967_implementation do + EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]} + end + ) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs new file mode 100644 index 0000000..4551ef0 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_read_proxy_controller_test.exs @@ -0,0 +1,128 @@ +defmodule BlockScoutWeb.AddressReadProxyControllerTest do + use BlockScoutWeb.ConnCase, async: true + use ExUnit.Case, async: false + + alias Explorer.ExchangeRates.Token + alias Explorer.Chain.Address + + import Mox + + describe "GET index/3" do + setup :set_mox_global + + setup do + configuration = Application.get_env(:explorer, :checksum_function) + Application.put_env(:explorer, :checksum_function, :eth) + + :ok + + on_exit(fn -> + Application.put_env(:explorer, :checksum_function, configuration) + end) + end + + test "with invalid address hash", %{conn: conn} do + conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, "invalid_address")) + + assert html_response(conn, 404) + end + + test "with valid address that is not a contract", %{conn: conn} do + address = insert(:address) + + conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash))) + + assert html_response(conn, 404) + end + + test "successfully renders the page when the address is a contract", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = insert(:transaction, from_address: contract_address) |> with_block() + + insert( + :internal_transaction_create, + index: 0, + transaction: transaction, + created_contract_address: contract_address, + block_hash: transaction.block_hash, + block_index: 0 + ) + + insert(:smart_contract, address_hash: contract_address.hash, contract_code_md5: "123") + + get_eip1967_implementation() + + conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) + + assert html_response(conn, 200) + assert contract_address.hash == conn.assigns.address.hash + assert %Token{} = conn.assigns.exchange_rate + end + + test "returns not found for an unverified contract", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = insert(:transaction, from_address: contract_address) |> with_block() + + insert( + :internal_transaction_create, + index: 0, + transaction: transaction, + created_contract_address: contract_address, + block_hash: transaction.block_hash, + block_index: 0 + ) + + conn = get(conn, address_read_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) + + assert html_response(conn, 404) + end + end + + def get_eip1967_implementation do + EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]} + end + ) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_token_balance_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_token_balance_controller_test.exs new file mode 100644 index 0000000..cdafe88 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_token_balance_controller_test.exs @@ -0,0 +1,46 @@ +defmodule BlockScoutWeb.AddressTokenBalanceControllerTest do + use BlockScoutWeb.ConnCase + + alias Explorer.Chain.Address + alias Explorer.Factory + + describe "GET index/3" do + test "without AJAX", %{conn: conn} do + %Address{hash: hash} = Factory.insert(:address) + + response_conn = get(conn, address_token_balance_path(conn, :index, Address.checksum(hash))) + + assert html_response(response_conn, 404) + end + + test "with AJAX without valid address", %{conn: conn} do + ajax_conn = ajax(conn) + + response_conn = get(ajax_conn, address_token_balance_path(ajax_conn, :index, "invalid_address")) + + assert html_response(response_conn, 404) + end + + test "with AJAX with valid address without address still returns token balances", %{conn: conn} do + ajax_conn = ajax(conn) + + response_conn = get(ajax_conn, address_token_balance_path(ajax_conn, :index, Address.checksum(address_hash()))) + + assert html_response(response_conn, 200) + end + + test "with AJAX with valid address with address returns token balances", %{conn: conn} do + %Address{hash: hash} = Factory.insert(:address) + + ajax_conn = ajax(conn) + + response_conn = get(ajax_conn, address_token_balance_path(ajax_conn, :index, Address.checksum(hash))) + + assert html_response(response_conn, 200) + end + end + + defp ajax(conn) do + put_req_header(conn, "x-requested-with", "XMLHttpRequest") + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_token_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_token_controller_test.exs new file mode 100644 index 0000000..c547886 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_token_controller_test.exs @@ -0,0 +1,172 @@ +defmodule BlockScoutWeb.AddressTokenControllerTest do + use BlockScoutWeb.ConnCase, async: true + use ExUnit.Case, async: false + + import BlockScoutWeb.WebRouter.Helpers, only: [address_token_path: 3] + import Mox + + alias Explorer.Chain.{Address, Token} + + describe "GET index/2" do + setup :set_mox_global + + setup do + configuration = Application.get_env(:explorer, :checksum_function) + Application.put_env(:explorer, :checksum_function, :eth) + + :ok + + on_exit(fn -> + Application.put_env(:explorer, :checksum_function, configuration) + end) + end + + test "with invalid address hash", %{conn: conn} do + conn = get(conn, address_token_path(conn, :index, "invalid_address")) + + assert html_response(conn, 422) + end + + test "with valid address hash without address", %{conn: conn} do + conn = get(conn, address_token_path(conn, :index, Address.checksum("0x8bf38d4764929064f2d4d3a56520a76ab3df415b"))) + + assert html_response(conn, 404) + end + + test "returns tokens that have balance for the address", %{conn: conn} do + address = insert(:address) + + token1 = + :token + |> insert(name: "token1") + + token2 = + :token + |> insert(name: "token2") + + insert( + :address_current_token_balance, + address: address, + token_contract_address_hash: token1.contract_address_hash, + value: 1000 + ) + + insert( + :address_current_token_balance, + address: address, + token_contract_address_hash: token2.contract_address_hash, + value: 0 + ) + + insert( + :token_transfer, + token_contract_address: token1.contract_address, + from_address: address, + to_address: build(:address) + ) + + insert( + :token_transfer, + token_contract_address: token2.contract_address, + from_address: build(:address), + to_address: address + ) + + conn = get(conn, address_token_path(conn, :index, Address.checksum(address)), type: "JSON") + + {:ok, %{"items" => items}} = + conn.resp_body + |> Poison.decode() + + assert json_response(conn, 200) + assert Enum.any?(items, fn item -> String.contains?(item, to_string(token1.contract_address_hash)) end) + refute Enum.any?(items, fn item -> String.contains?(item, to_string(token2.contract_address_hash)) end) + end + + test "returns next page of results based on last seen token", %{conn: conn} do + address = insert(:address) + + second_page_tokens = + 1..50 + |> Enum.reduce([], fn i, acc -> + token = insert(:token, name: "A Token#{i}", type: "ERC-20") + + insert( + :address_current_token_balance, + token_contract_address_hash: token.contract_address_hash, + address: address, + value: 1000 + ) + + acc ++ [token.name] + end) + |> Enum.sort() + + token = insert(:token, name: "Another Token", type: "ERC-721") + + insert( + :address_current_token_balance, + token_contract_address_hash: token.contract_address_hash, + address: address, + value: 1000 + ) + + %Token{name: name, type: type, inserted_at: _inserted_at} = token + + conn = + get(conn, address_token_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash)), %{ + "token_name" => name, + "token_type" => type, + "value" => 1000, + "type" => "JSON" + }) + + {:ok, %{"items" => items}} = + conn.resp_body + |> Poison.decode() + + assert Enum.any?(items, fn item -> + Enum.any?(second_page_tokens, fn token_name -> String.contains?(item, token_name) end) + end) + end + + test "next_page_params exists if not on last page", %{conn: conn} do + address = insert(:address) + + Enum.each(1..51, fn i -> + token = insert(:token, name: "A Token#{i}", type: "ERC-20") + + insert( + :address_current_token_balance, + token_contract_address_hash: token.contract_address_hash, + address: address, + value: 1000 + ) + + insert(:token_transfer, token_contract_address: token.contract_address, from_address: address) + end) + + conn = get(conn, address_token_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash)), type: "JSON") + + {:ok, %{"next_page_path" => next_page_path}} = + conn.resp_body + |> Poison.decode() + + assert next_page_path + end + + test "next_page_params are empty if on last page", %{conn: conn} do + address = insert(:address) + token = insert(:token) + insert(:token_transfer, token_contract_address: token.contract_address, from_address: address) + + conn = get(conn, address_token_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash)), type: "JSON") + + {:ok, %{"next_page_path" => next_page_path}} = + conn.resp_body + |> Poison.decode() + + refute next_page_path + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_token_transfer_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_token_transfer_controller_test.exs new file mode 100644 index 0000000..f8a6602 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_token_transfer_controller_test.exs @@ -0,0 +1,274 @@ +defmodule BlockScoutWeb.AddressTokenTransferControllerTest do + use BlockScoutWeb.ConnCase + + import BlockScoutWeb.WebRouter.Helpers, + only: [address_token_transfers_path: 4, address_token_transfers_path: 5] + + alias Explorer.Chain.{Address, Token} + + describe "GET index/2" do + test "with invalid address hash", %{conn: conn} do + token_hash = "0xc8982771dd50285389c352c175ada74d074427c7" + conn = get(conn, address_token_transfers_path(conn, :index, "invalid_address", token_hash)) + + assert html_response(conn, 422) + end + + test "with invalid token hash", %{conn: conn} do + address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + + conn = get(conn, address_token_transfers_path(conn, :index, Address.checksum(address_hash), "invalid_address")) + + assert html_response(conn, 422) + end + + test "with an address that doesn't exist in our database", %{conn: conn} do + address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + %Token{contract_address_hash: token_hash} = insert(:token) + + conn = + get( + conn, + address_token_transfers_path(conn, :index, Address.checksum(address_hash), Address.checksum(token_hash)) + ) + + assert html_response(conn, 404) + end + + test "with an token that doesn't exist in our database", %{conn: conn} do + %Address{hash: address_hash} = insert(:address) + token_hash = Address.checksum("0x8bf38d4764929064f2d4d3a56520a76ab3df415b") + conn = get(conn, address_token_transfers_path(conn, :index, Address.checksum(address_hash), token_hash)) + + assert html_response(conn, 404) + end + end + + describe "GET index/2 JSON" do + test "without token transfers for a token", %{conn: conn} do + %Address{hash: address_hash} = insert(:address) + %Token{contract_address_hash: token_hash} = insert(:token) + + conn = + get( + conn, + address_token_transfers_path(conn, :index, Address.checksum(address_hash), Address.checksum(token_hash)), + %{ + type: "JSON" + } + ) + + assert json_response(conn, 200) == %{"items" => [], "next_page_path" => nil} + end + + test "returns the correct number of transactions", %{conn: conn} do + address = insert(:address) + token = insert(:token) + + inserted_transactions = + Enum.map(1..5, fn index -> + block = insert(:block, number: 1000 - index) + + transaction = + :transaction + |> insert() + |> with_block(block) + + insert( + :token_transfer, + to_address: address, + transaction: transaction, + token_contract_address: token.contract_address + ) + + transaction + end) + + conn = + get( + conn, + address_token_transfers_path( + conn, + :index, + Address.checksum(address.hash), + Address.checksum(token.contract_address_hash) + ), + %{type: "JSON"} + ) + + response_items = + conn + |> json_response(200) + |> Map.get("items") + + items_length = length(response_items) + + assert items_length == 5 + + assert Enum.all?(inserted_transactions, fn transaction -> + Enum.any?(response_items, fn item -> + String.contains?( + item, + "data-identifier-hash=\"#{to_string(transaction.hash)}\"" + ) + end) + end) + end + + test "returns next_page_path as null when there are no more pages", %{conn: conn} do + address = insert(:address) + token = insert(:token) + + block = insert(:block, number: 1000) + + transaction = + :transaction + |> insert() + |> with_block(block) + + insert( + :token_transfer, + to_address: address, + transaction: transaction, + token_contract_address: token.contract_address + ) + + conn = + get( + conn, + address_token_transfers_path( + conn, + :index, + Address.checksum(address.hash), + Address.checksum(token.contract_address_hash) + ), + %{type: "JSON"} + ) + + assert Map.get(json_response(conn, 200), "next_page_path") == nil + end + + test "returns next_page_path when there are more items", %{conn: conn} do + address = insert(:address) + token = insert(:token) + + page_last_transfer = + 1..50 + |> Enum.map(fn index -> + block = insert(:block, number: 1000 - index) + + transaction = + :transaction + |> insert() + |> with_block(block) + + insert( + :token_transfer, + to_address: address, + transaction: transaction, + token_contract_address: token.contract_address + ) + + transaction + end) + |> List.last() + + Enum.each(51..60, fn index -> + block = insert(:block, number: 1000 - index) + + transaction = + :transaction + |> insert() + |> with_block(block) + + insert( + :token_transfer, + to_address: address, + transaction: transaction, + token_contract_address: token.contract_address + ) + end) + + conn = + get( + conn, + address_token_transfers_path( + conn, + :index, + Address.checksum(address.hash), + Address.checksum(token.contract_address_hash) + ), + %{type: "JSON"} + ) + + expected_path = + address_token_transfers_path( + conn, + :index, + Address.checksum(address.hash), + Address.checksum(token.contract_address_hash), + %{ + block_number: page_last_transfer.block_number, + index: page_last_transfer.index, + items_count: "50" + } + ) + + assert Map.get(json_response(conn, 200), "next_page_path") == expected_path + end + + test "with invalid address hash", %{conn: conn} do + token_hash = "0xc8982771dd50285389c352c175ada74d074427c7" + + conn = + get(conn, address_token_transfers_path(conn, :index, "invalid_address", token_hash), %{ + type: "JSON" + }) + + assert html_response(conn, 422) + end + + test "with invalid token hash", %{conn: conn} do + address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + + conn = + get(conn, address_token_transfers_path(conn, :index, Address.checksum(address_hash), "invalid_address"), %{ + type: "JSON" + }) + + assert html_response(conn, 422) + end + + test "with an address that doesn't exist in our database", %{conn: conn} do + address_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + %Token{contract_address_hash: token_hash} = insert(:token) + + conn = + get( + conn, + address_token_transfers_path(conn, :index, Address.checksum(address_hash), Address.checksum(token_hash)), + %{ + type: "JSON" + } + ) + + assert html_response(conn, 404) + end + + test "with a token that doesn't exist in our database", %{conn: conn} do + %Address{hash: address_hash} = insert(:address) + token_hash = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + + conn = + get( + conn, + address_token_transfers_path(conn, :index, Address.checksum(address_hash), Address.checksum(token_hash)), + %{ + type: "JSON" + } + ) + + assert html_response(conn, 404) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs new file mode 100644 index 0000000..159c1be --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs @@ -0,0 +1,405 @@ +defmodule BlockScoutWeb.AddressTransactionControllerTest do + use BlockScoutWeb.ConnCase, async: true + use ExUnit.Case, async: false + + import BlockScoutWeb.WebRouter.Helpers, only: [address_transaction_path: 3, address_transaction_path: 4] + import Mox + + alias Explorer.Chain.{Address, Transaction} + alias Explorer.ExchangeRates.Token + + setup :verify_on_exit! + + describe "GET index/2" do + setup :set_mox_global + + setup do + configuration = Application.get_env(:explorer, :checksum_function) + Application.put_env(:explorer, :checksum_function, :eth) + + :ok + + on_exit(fn -> + Application.put_env(:explorer, :checksum_function, configuration) + end) + end + + test "with invalid address hash", %{conn: conn} do + conn = get(conn, address_transaction_path(conn, :index, "invalid_address")) + + assert html_response(conn, 422) + end + + test "with valid address hash without address in the DB", %{conn: conn} do + conn = + get( + conn, + address_transaction_path(conn, :index, Address.checksum("0x8bf38d4764929064f2d4d3a56520a76ab3df415b"), %{ + "type" => "JSON" + }) + ) + + assert json_response(conn, 200) + transaction_tiles = json_response(conn, 200)["items"] + assert transaction_tiles |> length() == 0 + end + + test "returns transactions for the address", %{conn: conn} do + address = insert(:address) + + block = insert(:block) + + from_transaction = + :transaction + |> insert(from_address: address) + |> with_block(block) + + to_transaction = + :transaction + |> insert(to_address: address) + |> with_block(block) + + conn = get(conn, address_transaction_path(conn, :index, Address.checksum(address), %{"type" => "JSON"})) + + transaction_tiles = json_response(conn, 200)["items"] + transaction_hashes = Enum.map([to_transaction.hash, from_transaction.hash], &to_string(&1)) + + assert Enum.all?(transaction_hashes, fn transaction_hash -> + Enum.any?(transaction_tiles, &String.contains?(&1, transaction_hash)) + end) + end + + test "includes USD exchange rate value for address in assigns", %{conn: conn} do + address = insert(:address) + + conn = get(conn, address_transaction_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash))) + + assert %Token{} = conn.assigns.exchange_rate + end + + test "returns next page of results based on last seen transaction", %{conn: conn} do + address = insert(:address) + + second_page_hashes = + 50 + |> insert_list(:transaction, from_address: address) + |> with_block() + |> Enum.map(& &1.hash) + + %Transaction{block_number: block_number, index: index} = + :transaction + |> insert(from_address: address) + |> with_block() + + conn = + get(conn, address_transaction_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash)), %{ + "block_number" => Integer.to_string(block_number), + "index" => Integer.to_string(index), + "type" => "JSON" + }) + + transaction_tiles = json_response(conn, 200)["items"] + + assert Enum.all?(second_page_hashes, fn address_hash -> + Enum.any?(transaction_tiles, &String.contains?(&1, to_string(address_hash))) + end) + end + + test "next_page_params exist if not on last page", %{conn: conn} do + address = insert(:address) + block = insert(:block) + + 60 + |> insert_list(:transaction, from_address: address) + |> with_block(block) + + conn = get(conn, address_transaction_path(conn, :index, Address.checksum(address.hash), %{"type" => "JSON"})) + + assert json_response(conn, 200)["next_page_path"] + end + + test "next_page_params are empty if on last page", %{conn: conn} do + address = insert(:address) + + :transaction + |> insert(from_address: address) + |> with_block() + + conn = get(conn, address_transaction_path(conn, :index, Address.checksum(address.hash), %{"type" => "JSON"})) + + refute json_response(conn, 200)["next_page_path"] + end + + test "returns parent transaction for a contract address", %{conn: conn} do + address = insert(:address, contract_code: data(:address_contract_code)) + block = insert(:block) + + transaction = + :transaction + |> insert(to_address: nil, created_contract_address_hash: address.hash) + |> with_block(block) + |> Explorer.Repo.preload([[created_contract_address: :names], [from_address: :names], :token_transfers]) + + insert( + :internal_transaction_create, + index: 0, + created_contract_address: address, + to_address: nil, + transaction: transaction, + block_hash: block.hash, + block_index: 0 + ) + + conn = get(conn, address_transaction_path(conn, :index, Address.checksum(address)), %{"type" => "JSON"}) + + transaction_tiles = json_response(conn, 200)["items"] + + assert Enum.all?([transaction.hash], fn transaction_hash -> + Enum.any?(transaction_tiles, &String.contains?(&1, to_string(transaction_hash))) + end) + end + end + + describe "GET token-transfers-csv/2" do + test "do not export token transfers to csv without recaptcha recaptcha_response provided", %{conn: conn} do + BlockScoutWeb.TestCaptchaHelper + |> expect(:recaptcha_passed?, fn _captcha_response -> false end) + + address = insert(:address) + + transaction = + :transaction + |> insert(from_address: address) + |> with_block() + + insert(:token_transfer, transaction: transaction, from_address: address, block_number: transaction.block_number) + insert(:token_transfer, transaction: transaction, to_address: address, block_number: transaction.block_number) + + from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime) + to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) + + conn = + get(conn, "/token-transfers-csv", %{ + "address_id" => Address.checksum(address.hash), + "from_period" => from_period, + "to_period" => to_period + }) + + assert conn.status == 404 + end + + test "do not export token transfers to csv without recaptcha passed", %{conn: conn} do + BlockScoutWeb.TestCaptchaHelper + |> expect(:recaptcha_passed?, fn _captcha_response -> false end) + + address = insert(:address) + + transaction = + :transaction + |> insert(from_address: address) + |> with_block() + + insert(:token_transfer, transaction: transaction, from_address: address, block_number: transaction.block_number) + insert(:token_transfer, transaction: transaction, to_address: address, block_number: transaction.block_number) + + from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime) + to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) + + conn = + get(conn, "/token-transfers-csv", %{ + "address_id" => Address.checksum(address.hash), + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => "123" + }) + + assert conn.status == 404 + end + + test "exports token transfers to csv", %{conn: conn} do + BlockScoutWeb.TestCaptchaHelper + |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + + address = insert(:address) + + transaction = + :transaction + |> insert(from_address: address) + |> with_block() + + insert(:token_transfer, transaction: transaction, from_address: address, block_number: transaction.block_number) + insert(:token_transfer, transaction: transaction, to_address: address, block_number: transaction.block_number) + + from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime) + to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) + + conn = + get(conn, "/token-transfers-csv", %{ + "address_id" => Address.checksum(address.hash), + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => "123" + }) + + assert conn.resp_body |> String.split("\n") |> Enum.count() == 4 + end + end + + describe "GET transactions_csv/2" do + test "download csv file with transactions", %{conn: conn} do + BlockScoutWeb.TestCaptchaHelper + |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + + address = insert(:address) + + :transaction + |> insert(from_address: address) + |> with_block() + + :transaction + |> insert(from_address: address) + |> with_block() + + from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime) + to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) + + conn = + get(conn, "/transactions-csv", %{ + "address_id" => Address.checksum(address.hash), + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => "123" + }) + + assert conn.resp_body |> String.split("\n") |> Enum.count() == 4 + end + end + + describe "GET internal_transactions_csv/2" do + test "download csv file with internal transactions", %{conn: conn} do + BlockScoutWeb.TestCaptchaHelper + |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + + address = insert(:address) + + transaction_1 = + :transaction + |> insert() + |> with_block() + + transaction_2 = + :transaction + |> insert() + |> with_block() + + transaction_3 = + :transaction + |> insert() + |> with_block() + + insert(:internal_transaction, + index: 3, + transaction: transaction_1, + from_address: address, + block_number: transaction_1.block_number, + block_hash: transaction_1.block_hash, + block_index: 0, + transaction_index: transaction_1.index + ) + + insert(:internal_transaction, + index: 1, + transaction: transaction_2, + to_address: address, + block_number: transaction_2.block_number, + block_hash: transaction_2.block_hash, + block_index: 1, + transaction_index: transaction_2.index + ) + + insert(:internal_transaction, + index: 2, + transaction: transaction_3, + created_contract_address: address, + block_number: transaction_3.block_number, + block_hash: transaction_3.block_hash, + block_index: 2, + transaction_index: transaction_3.index + ) + + from_period = Timex.format!(Timex.shift(Timex.now(), years: -1), "%Y-%m-%d", :strftime) + to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) + + conn = + get(conn, "/internal-transactions-csv", %{ + "address_id" => Address.checksum(address.hash), + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => "123" + }) + + assert conn.resp_body |> String.split("\n") |> Enum.count() == 5 + end + end + + describe "GET logs_csv/2" do + test "download csv file with logs", %{conn: conn} do + BlockScoutWeb.TestCaptchaHelper + |> expect(:recaptcha_passed?, fn _captcha_response -> true end) + + address = insert(:address) + + transaction_1 = + :transaction + |> insert() + |> with_block() + + insert(:log, + address: address, + index: 3, + transaction: transaction_1, + block: transaction_1.block, + block_number: transaction_1.block_number + ) + + transaction_2 = + :transaction + |> insert() + |> with_block() + + insert(:log, + address: address, + index: 1, + transaction: transaction_2, + block: transaction_2.block, + block_number: transaction_2.block_number + ) + + transaction_3 = + :transaction + |> insert() + |> with_block() + + insert(:log, + address: address, + index: 2, + transaction: transaction_3, + block: transaction_3.block, + block_number: transaction_3.block_number + ) + + from_period = Timex.format!(Timex.shift(Timex.now(), minutes: -1), "%Y-%m-%d", :strftime) + to_period = Timex.format!(Timex.now(), "%Y-%m-%d", :strftime) + + conn = + get(conn, "/logs-csv", %{ + "address_id" => Address.checksum(address.hash), + "from_period" => from_period, + "to_period" => to_period, + "recaptcha_response" => "123" + }) + + assert conn.resp_body |> String.split("\n") |> Enum.count() == 5 + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs new file mode 100644 index 0000000..72b9a8e --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_write_contract_controller_test.exs @@ -0,0 +1,132 @@ +defmodule BlockScoutWeb.AddressWriteContractControllerTest do + use BlockScoutWeb.ConnCase, async: true + use ExUnit.Case, async: false + + alias Explorer.ExchangeRates.Token + alias Explorer.Chain.Address + + use EthereumJSONRPC.Case, async: false + + import Mox + + describe "GET index/3" do + setup :set_mox_global + + setup do + configuration = Application.get_env(:explorer, :checksum_function) + Application.put_env(:explorer, :checksum_function, :eth) + + :ok + + on_exit(fn -> + Application.put_env(:explorer, :checksum_function, configuration) + end) + end + + test "with invalid address hash", %{conn: conn} do + conn = get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, "invalid_address")) + + assert html_response(conn, 404) + end + + test "with valid address that is not a contract", %{conn: conn} do + address = insert(:address) + + conn = get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash))) + + assert html_response(conn, 404) + end + + test "successfully renders the page when the address is a contract", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = insert(:transaction, from_address: contract_address) |> with_block() + + insert( + :internal_transaction_create, + index: 0, + transaction: transaction, + created_contract_address: contract_address, + block_hash: transaction.block_hash, + block_index: 0 + ) + + insert(:smart_contract, address_hash: contract_address.hash, contract_code_md5: "123") + + get_eip1967_implementation() + + conn = + get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) + + assert html_response(conn, 200) + assert contract_address.hash == conn.assigns.address.hash + assert %Token{} = conn.assigns.exchange_rate + end + + test "returns not found for an unverified contract", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = insert(:transaction, from_address: contract_address) |> with_block() + + insert( + :internal_transaction_create, + index: 0, + transaction: transaction, + created_contract_address: contract_address, + block_hash: transaction.block_hash, + block_index: 0 + ) + + conn = + get(conn, address_write_contract_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) + + assert html_response(conn, 404) + end + end + + def get_eip1967_implementation do + EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]} + end + ) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs new file mode 100644 index 0000000..3000235 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/address_write_proxy_controller_test.exs @@ -0,0 +1,130 @@ +defmodule BlockScoutWeb.AddressWriteProxyControllerTest do + use BlockScoutWeb.ConnCase, async: true + use ExUnit.Case, async: false + + alias Explorer.ExchangeRates.Token + alias Explorer.Chain.Address + + import Mox + + describe "GET index/3" do + setup :set_mox_global + + setup do + configuration = Application.get_env(:explorer, :checksum_function) + Application.put_env(:explorer, :checksum_function, :eth) + + :ok + + on_exit(fn -> + Application.put_env(:explorer, :checksum_function, configuration) + end) + end + + test "with invalid address hash", %{conn: conn} do + conn = get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, "invalid_address")) + + assert html_response(conn, 404) + end + + test "with valid address that is not a contract", %{conn: conn} do + address = insert(:address) + + conn = get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(address.hash))) + + assert html_response(conn, 404) + end + + test "successfully renders the page when the address is a contract", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = insert(:transaction, from_address: contract_address) |> with_block() + + insert( + :internal_transaction_create, + index: 0, + transaction: transaction, + created_contract_address: contract_address, + block_hash: transaction.block_hash, + block_index: 0 + ) + + insert(:smart_contract, address_hash: contract_address.hash, contract_code_md5: "123") + + get_eip1967_implementation() + + conn = + get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) + + assert html_response(conn, 200) + assert contract_address.hash == conn.assigns.address.hash + assert %Token{} = conn.assigns.exchange_rate + end + + test "returns not found for an unverified contract", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = insert(:transaction, from_address: contract_address) |> with_block() + + insert( + :internal_transaction_create, + index: 0, + transaction: transaction, + created_contract_address: contract_address, + block_hash: transaction.block_hash, + block_index: 0 + ) + + conn = + get(conn, address_write_proxy_path(BlockScoutWeb.Endpoint, :index, Address.checksum(contract_address.hash))) + + assert html_response(conn, 404) + end + end + + def get_eip1967_implementation do + EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]} + end + ) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/admin/dashboard_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/admin/dashboard_controller_test.exs new file mode 100644 index 0000000..03cc0a3 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/admin/dashboard_controller_test.exs @@ -0,0 +1,26 @@ +defmodule BlockScoutWeb.Admin.DashboardControllerTest do + use BlockScoutWeb.ConnCase + + alias BlockScoutWeb.Router + + describe "index/2" do + setup %{conn: conn} do + admin = insert(:administrator) + + conn = + conn + |> bypass_through(Router, [:browser]) + |> get("/") + |> put_session(:user_id, admin.user.id) + |> send_resp(200, "") + |> recycle() + + {:ok, conn: conn} + end + + test "shows the dashboard page", %{conn: conn} do + result = get(conn, "/admin" <> AdminRoutes.dashboard_path(conn, :index)) + assert html_response(result, 200) =~ "administrator_dashboard" + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/admin/session_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/admin/session_controller_test.exs new file mode 100644 index 0000000..ce6e4e5 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/admin/session_controller_test.exs @@ -0,0 +1,83 @@ +defmodule BlockScoutWeb.Admin.SessionControllerTest do + use BlockScoutWeb.ConnCase + + setup %{conn: conn} do + conn = + conn + |> bypass_through() + |> get("/") + + {:ok, conn: conn} + end + + describe "new/2" do + test "redirects to setup page if not configured", %{conn: conn} do + result = get(conn, AdminRoutes.session_path(conn, :new)) + assert redirected_to(result) == AdminRoutes.setup_path(conn, :configure) + end + + test "shows the admin login page", %{conn: conn} do + insert(:administrator) + result = get(conn, AdminRoutes.session_path(conn, :new)) + assert html_response(result, 200) =~ "administrator_login" + end + end + + describe "create/2" do + test "redirects to setup page if not configured", %{conn: conn} do + result = post(conn, AdminRoutes.session_path(conn, :create), %{}) + assert redirected_to(result) == AdminRoutes.setup_path(conn, :configure) + end + + test "redirects to dashboard on successful admin login", %{conn: conn} do + admin = insert(:administrator) + + params = %{ + "authenticate" => %{ + username: admin.user.username, + password: "password" + } + } + + result = post(conn, AdminRoutes.session_path(conn, :create), params) + assert redirected_to(result) == AdminRoutes.dashboard_path(conn, :index) + end + + test "reshows form if params are invalid", %{conn: conn} do + insert(:administrator) + params = %{"authenticate" => %{}} + + result = post(conn, AdminRoutes.session_path(conn, :create), params) + assert html_response(result, 200) =~ "administrator_login" + end + + test "reshows form if credentials are invalid", %{conn: conn} do + admin = insert(:administrator) + + params = %{ + "authenticate" => %{ + username: admin.user.username, + password: "badpassword" + } + } + + result = post(conn, AdminRoutes.session_path(conn, :create), params) + assert html_response(result, 200) =~ "administrator_login" + end + + test "reshows form if user is not an admin", %{conn: conn} do + insert(:administrator) + user = insert(:user) + + params = %{ + "authenticate" => %{ + username: user.username, + password: "password" + } + } + + result = post(conn, AdminRoutes.session_path(conn, :create), params) + assert html_response(result, 200) =~ "administrator_login" + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/admin/setup_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/admin/setup_controller_test.exs new file mode 100644 index 0000000..55c4753 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/admin/setup_controller_test.exs @@ -0,0 +1,109 @@ +defmodule BlockScoutWeb.Admin.SetupControllerTest do + use BlockScoutWeb.ConnCase + + alias BlockScoutWeb.Admin.SetupController + alias Explorer.Admin + + setup %{conn: conn} do + conn = + conn + |> bypass_through() + |> get("/") + + {:ok, conn: conn} + end + + describe "configure/2" do + test "redirects to session page if already configured", %{conn: conn} do + insert(:administrator) + result = get(conn, AdminRoutes.setup_path(conn, :configure)) + assert redirected_to(result) == AdminRoutes.session_path(conn, :new) + end + end + + describe "configure/2 with no params" do + test "shows the verification page", %{conn: conn} do + result = get(conn, AdminRoutes.setup_path(conn, :configure)) + assert html_response(result, 200) =~ "administrator_verify" + end + end + + describe "configure/2 with state param" do + test "shows verification page when state is invalid", %{conn: conn} do + result = get(conn, AdminRoutes.setup_path(conn, :configure), %{state: ""}) + assert html_response(result, 200) =~ "administrator_verify" + end + + test "shows registration page when state is valid", %{conn: conn} do + state = SetupController.generate_secure_token() + result = get(conn, AdminRoutes.setup_path(conn, :configure), %{state: state}) + assert html_response(result, 200) =~ "administrator_registration" + end + end + + describe "configure_admin/2" do + test "redirects to session page if already configured", %{conn: conn} do + insert(:administrator) + result = post(conn, AdminRoutes.setup_path(conn, :configure), %{}) + assert redirected_to(result) == AdminRoutes.session_path(conn, :new) + end + end + + describe "configure_admin/2 with no params" do + test "reshows the verification page", %{conn: conn} do + result = post(conn, AdminRoutes.setup_path(conn, :configure_admin), %{}) + assert html_response(result, 200) =~ "administrator_verify" + end + end + + describe "configure_admin/2 with verify param" do + test "redirects with valid recovery key", %{conn: conn} do + key = Admin.recovery_key() + params = %{verify: %{recovery_key: key}} + result = post(conn, AdminRoutes.setup_path(conn, :configure_admin), params) + assert redirected_to(result) =~ AdminRoutes.setup_path(conn, :configure, %{state: ""}) + end + + test "reshows the verification page with invalid key", %{conn: conn} do + params = %{verify: %{recovery_key: "bad_key"}} + result = post(conn, AdminRoutes.setup_path(conn, :configure_admin), params) + assert html_response(result, 200) =~ "administrator_verify" + end + end + + describe "configure_admin with state and registration params" do + setup do + [state: SetupController.generate_secure_token()] + end + + test "reshows the verification page when state is invalid", %{conn: conn} do + params = %{state: "invalid_state", registration: %{}} + result = post(conn, AdminRoutes.setup_path(conn, :configure_admin), params) + assert html_response(result, 200) =~ "administrator_verify" + end + + test "reshows the registration page when registration is invalid", %{conn: conn, state: state} do + params = %{state: state, registration: %{}} + result = post(conn, AdminRoutes.setup_path(conn, :configure_admin), params) + response = html_response(result, 200) + assert response =~ "administrator_registration" + assert response =~ "invalid-feedback" + assert response =~ "is-invalid" + end + + test "redirects to dashboard when state and registration are valid", %{conn: conn, state: state} do + params = %{ + state: state, + registration: %{ + username: "admin_user", + email: "admin_user@blockscout", + password: "testpassword", + password_confirmation: "testpassword" + } + } + + result = post(conn, AdminRoutes.setup_path(conn, :configure_admin), params) + assert redirected_to(result) == AdminRoutes.dashboard_path(conn, :index) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs new file mode 100644 index 0000000..1a1b7fa --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs @@ -0,0 +1,3164 @@ +defmodule BlockScoutWeb.API.RPC.AddressControllerTest do + use BlockScoutWeb.ConnCase, async: false + + import Mox + + alias BlockScoutWeb.API.RPC.AddressController + alias Explorer.Chain + alias Explorer.Chain.{Events.Subscriber, Transaction, Wei} + alias Explorer.Counters.{AddressesCounter, AverageBlockTime} + alias Indexer.Fetcher.CoinBalanceOnDemand + alias Explorer.Repo + + setup :set_mox_global + setup :verify_on_exit! + + setup do + mocked_json_rpc_named_arguments = [ + transport: EthereumJSONRPC.Mox, + transport_options: [] + ] + + start_supervised!({Task.Supervisor, name: Indexer.TaskSupervisor}) + start_supervised!(AverageBlockTime) + start_supervised!({CoinBalanceOnDemand, [mocked_json_rpc_named_arguments, [name: CoinBalanceOnDemand]]}) + start_supervised!(AddressesCounter) + + Application.put_env(:explorer, AverageBlockTime, enabled: true) + + on_exit(fn -> + Application.put_env(:explorer, AverageBlockTime, enabled: false) + end) + + :ok + end + + describe "listaccounts" do + setup do + Subscriber.to(:addresses, :on_demand) + Subscriber.to(:address_coin_balances, :on_demand) + + %{params: %{"module" => "account", "action" => "listaccounts"}} + end + + test "with no addresses", %{params: params, conn: conn} do + response = + conn + |> get("/api", params) + |> json_response(200) + + schema = listaccounts_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert response["message"] == "OK" + assert response["status"] == "1" + assert response["result"] == [] + end + + test "with existing addresses", %{params: params, conn: conn} do + first_address = insert(:address, fetched_coin_balance: 10, inserted_at: Timex.shift(Timex.now(), minutes: -10)) + second_address = insert(:address, fetched_coin_balance: 100, inserted_at: Timex.shift(Timex.now(), minutes: -5)) + first_address_hash = to_string(first_address.hash) + second_address_hash = to_string(second_address.hash) + + response = + conn + |> get("/api", params) + |> json_response(200) + + schema = listaccounts_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert response["message"] == "OK" + assert response["status"] == "1" + + assert [ + %{ + "address" => ^first_address_hash, + "balance" => "10" + }, + %{ + "address" => ^second_address_hash, + "balance" => "100" + } + ] = response["result"] + end + + test "sort by hash", %{params: params, conn: conn} do + inserted_at = Timex.shift(Timex.now(), minutes: -10) + + first_address = + insert(:address, + hash: "0x0000000000000000000000000000000000000001", + fetched_coin_balance: 10, + inserted_at: inserted_at + ) + + second_address = + insert(:address, + hash: "0x0000000000000000000000000000000000000002", + fetched_coin_balance: 100, + inserted_at: inserted_at + ) + + first_address_hash = to_string(first_address.hash) + second_address_hash = to_string(second_address.hash) + + first_address_inserted_at = to_string(first_address.inserted_at) + second_address_inserted_at = to_string(second_address.inserted_at) + + response = + conn + |> get("/api", params) + |> json_response(200) + + schema = listaccounts_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert response["message"] == "OK" + assert response["status"] == "1" + + assert [ + %{ + "address" => ^first_address_hash + }, + %{ + "address" => ^second_address_hash + } + ] = response["result"] + end + + test "with a stale balance", %{conn: conn, params: params} do + now = Timex.now() + + mining_address = + insert(:address, + fetched_coin_balance: 0, + fetched_coin_balance_block_number: 102, + inserted_at: Timex.shift(now, minutes: -10) + ) + + mining_address_hash = to_string(mining_address.hash) + # we space these very far apart so that we know it will consider the 0th block stale (it calculates how far + # back we'd need to go to get 24 hours in the past) + Enum.each(0..100, fn i -> + insert(:block, number: i, timestamp: Timex.shift(now, hours: -(102 - i) * 25), miner: mining_address) + end) + + insert(:block, number: 101, timestamp: Timex.shift(now, hours: -25), miner: mining_address) + AverageBlockTime.refresh() + + address = + insert(:address, + fetched_coin_balance: 100, + fetched_coin_balance_block_number: 100, + inserted_at: Timex.shift(now, minutes: -5) + ) + + address_hash = to_string(address.hash) + + expect(EthereumJSONRPC.Mox, :json_rpc, 1, fn [ + %{ + id: id, + method: "eth_getBalance", + params: [^mining_address_hash, "0x65"] + } + ], + _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x02"}]} + end) + + res = eth_block_number_fake_response("0x65") + + expect(EthereumJSONRPC.Mox, :json_rpc, 1, fn [ + %{ + id: 0, + method: "eth_getBlockByNumber", + params: ["0x65", true] + } + ], + _ -> + {:ok, [res]} + end) + + expect(EthereumJSONRPC.Mox, :json_rpc, 1, fn [ + %{ + id: id, + method: "eth_getBalance", + params: [^address_hash, "0x65"] + } + ], + _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x02"}]} + end) + + expect(EthereumJSONRPC.Mox, :json_rpc, 1, fn [ + %{ + id: 0, + method: "eth_getBlockByNumber", + params: ["0x65", true] + } + ], + _ -> + {:ok, [res]} + end) + + response = + conn + |> get("/api", params) + |> json_response(200) + + schema = listaccounts_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert response["message"] == "OK" + assert response["status"] == "1" + + assert [ + %{ + "address" => ^mining_address_hash, + "balance" => "0", + "stale" => false + }, + %{ + "address" => ^address_hash, + "balance" => "100", + "stale" => true + } + ] = response["result"] + + {:ok, expected_wei} = Wei.cast(2) + + assert_receive({:chain_event, :addresses, :on_demand, [received_address]}) + + assert received_address.hash == address.hash + assert received_address.fetched_coin_balance == expected_wei + assert received_address.fetched_coin_balance_block_number == 101 + end + end + + describe "balance" do + test "with missing address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "balance" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert response["message"] =~ "'address' is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with an invalid address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "balance", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert response["message"] =~ "Invalid address hash" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with an address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "balance", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert response["result"] == "0" + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a valid address", %{conn: conn} do + address = insert(:address, fetched_coin_balance: 100) + + params = %{ + "module" => "account", + "action" => "balance", + "address" => "#{address.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert response["result"] == "#{address.fetched_coin_balance.value}" + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with multiple valid addresses", %{conn: conn} do + addresses = + for _ <- 1..2 do + insert(:address, fetched_coin_balance: Enum.random(1..1_000)) + end + + address_param = + addresses + |> Enum.map(&"#{&1.hash}") + |> Enum.join(",") + + params = %{ + "module" => "account", + "action" => "balance", + "address" => address_param + } + + expected_result = + Enum.map(addresses, fn address -> + %{"account" => "#{address.hash}", "balance" => "#{address.fetched_coin_balance.value}", "stale" => false} + end) + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "supports GET and POST requests", %{conn: conn} do + address = insert(:address, fetched_coin_balance: 100) + + params = %{ + "module" => "account", + "action" => "balance", + "address" => "#{address.hash}" + } + + assert get_response = + conn + |> get("/api", params) + |> json_response(200) + + assert post_response = + conn + |> post("/api", params) + |> json_response(200) + + assert get_response == post_response + end + end + + describe "balancemulti" do + test "with an invalid and a valid address hash", %{conn: conn} do + address1 = "invalidhash" + address2 = "0x9bf49d5875030175f3d5d4a67631a87ab4df526b" + + params = %{ + "module" => "account", + "action" => "balancemulti", + "address" => "#{address1},#{address2}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert response["message"] =~ "Invalid address hash" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with multiple addresses that don't exist", %{conn: conn} do + address1 = "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + address2 = "0x9bf49d5875030175f3d5d4a67631a87ab4df526b" + + params = %{ + "module" => "account", + "action" => "balancemulti", + "address" => "#{address1},#{address2}" + } + + expected_result = [ + %{"account" => address1, "balance" => "0", "stale" => false}, + %{"account" => address2, "balance" => "0", "stale" => false} + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert :ok = ExJsonSchema.Validator.validate(schema, response) + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with multiple valid addresses", %{conn: conn} do + addresses = + for _ <- 1..4 do + insert(:address, fetched_coin_balance: Enum.random(1..1_000)) + end + + address_param = + addresses + |> Enum.map(&"#{&1.hash}") + |> Enum.join(",") + + params = %{ + "module" => "account", + "action" => "balancemulti", + "address" => address_param + } + + expected_result = + Enum.map(addresses, fn address -> + %{"account" => "#{address.hash}", "balance" => "#{address.fetched_coin_balance.value}", "stale" => false} + end) + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "with an address that exists and one that doesn't", %{conn: conn} do + address1 = insert(:address, fetched_coin_balance: 100) + address2_hash = "0x9bf49d5875030175f3d5d4a67631a87ab4df526b" + + params = %{ + "module" => "account", + "action" => "balancemulti", + "address" => "#{address1.hash},#{address2_hash}" + } + + expected_result = [ + %{"account" => address2_hash, "balance" => "0", "stale" => false}, + %{"account" => "#{address1.hash}", "balance" => "#{address1.fetched_coin_balance.value}", "stale" => false} + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "up to a maximum of 20 addresses in a single request", %{conn: conn} do + addresses = insert_list(25, :address, fetched_coin_balance: 0) + + address_param = + addresses + |> Enum.map(&"#{&1.hash}") + |> Enum.join(",") + + params = %{ + "module" => "account", + "action" => "balancemulti", + "address" => address_param + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 20 + assert response["status"] == "1" + assert response["message"] == "OK" + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "with a single address", %{conn: conn} do + address = insert(:address, fetched_coin_balance: 100) + + params = %{ + "module" => "account", + "action" => "balancemulti", + "address" => "#{address.hash}" + } + + expected_result = [ + %{"account" => "#{address.hash}", "balance" => "#{address.fetched_coin_balance.value}", "stale" => false} + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + + schema = balance_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "supports GET and POST requests", %{conn: conn} do + addresses = + for _ <- 1..4 do + insert(:address, fetched_coin_balance: Enum.random(1..1_000)) + end + + address_param = + addresses + |> Enum.map(&"#{&1.hash}") + |> Enum.join(",") + + params = %{ + "module" => "account", + "action" => "balancemulti", + "address" => address_param + } + + assert get_response = + conn + |> get("/api", params) + |> json_response(200) + + assert post_response = + conn + |> post("/api", params) + |> json_response(200) + + assert get_response == post_response + end + end + + describe "txlist" do + test "with missing address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "txlist" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "'address' is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with an invalid address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid address format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with an address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == [] + assert response["status"] == "0" + assert response["message"] == "No transactions found" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with a valid address", %{conn: conn} do + address = insert(:address) + + transaction = + %Transaction{block: block} = + :transaction + |> insert(from_address: address) + |> with_block(status: :ok) + + # ^ 'status: :ok' means `isError` in response should be '0' + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}" + } + + expected_result = [ + %{ + "blockNumber" => "#{transaction.block_number}", + "timeStamp" => "#{DateTime.to_unix(block.timestamp)}", + "hash" => "#{transaction.hash}", + "nonce" => "#{transaction.nonce}", + "blockHash" => "#{block.hash}", + "transactionIndex" => "#{transaction.index}", + "from" => "#{transaction.from_address_hash}", + "to" => "#{transaction.to_address_hash}", + "value" => "#{transaction.value.value}", + "gas" => "#{transaction.gas}", + "gasPrice" => "#{transaction.gas_price.value}", + "isError" => "0", + "txreceipt_status" => "1", + "input" => "#{transaction.input}", + "contractAddress" => "#{transaction.created_contract_address_hash}", + "cumulativeGasUsed" => "#{transaction.cumulative_gas_used}", + "gasUsed" => "#{transaction.gas_used}", + "confirmations" => "0" + } + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "includes correct confirmations value", %{conn: conn} do + insert(:block) + address = insert(:address) + + transaction = + %Transaction{hash: hash} = + :transaction + |> insert(from_address: address) + |> with_block() + + insert(:block) + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}" + } + + block_height = Chain.block_height() + expected_confirmations = block_height - transaction.block_number + + assert %{"result" => [returned_transaction]} = + response = + conn + |> get("/api", params) + |> json_response(200) + + assert returned_transaction["confirmations"] == "#{expected_confirmations}" + assert returned_transaction["hash"] == "#{hash}" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "returns '1' for 'isError' with failed transaction", %{conn: conn} do + address = insert(:address) + + %Transaction{hash: hash} = + :transaction + |> insert(from_address: address) + |> with_block(status: :error) + + # ^ 'status: :error' means `isError` in response should be '1' + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}" + } + + assert %{"result" => [returned_transaction]} = + response = + conn + |> get("/api", params) + |> json_response(200) + + assert returned_transaction["isError"] == "1" + assert returned_transaction["txreceipt_status"] == "0" + assert returned_transaction["hash"] == "#{hash}" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with address with multiple transactions", %{conn: conn} do + address1 = insert(:address) + address2 = insert(:address) + + transactions = + 3 + |> insert_list(:transaction, from_address: address1) + |> with_block() + + :transaction + |> insert(from_address: address2) + |> with_block() + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address1.hash}" + } + + expected_transaction_hashes = Enum.map(transactions, &"#{&1.hash}") + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 3 + + for returned_transaction <- response["result"] do + assert returned_transaction["hash"] in expected_transaction_hashes + end + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "orders transactions by block, in ascending order", %{conn: conn} do + first_block = insert(:block) + second_block = insert(:block) + third_block = insert(:block) + address = insert(:address) + + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(second_block) + + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(third_block) + + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(first_block) + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "sort" => "asc" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + block_numbers_order = + Enum.map(response["result"], fn transaction -> + String.to_integer(transaction["blockNumber"]) + end) + + assert block_numbers_order == Enum.sort(block_numbers_order, &(&1 <= &2)) + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "orders transactions by block, in descending order", %{conn: conn} do + first_block = insert(:block) + second_block = insert(:block) + third_block = insert(:block) + address = insert(:address) + + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(second_block) + + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(third_block) + + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(first_block) + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "sort" => "desc" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + block_numbers_order = + Enum.map(response["result"], fn transaction -> + String.to_integer(transaction["blockNumber"]) + end) + + assert block_numbers_order == Enum.sort(block_numbers_order, &(&1 >= &2)) + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "ignores invalid sort option, defaults to ascending", %{conn: conn} do + first_block = insert(:block) + second_block = insert(:block) + third_block = insert(:block) + address = insert(:address) + + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(second_block) + + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(third_block) + + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(first_block) + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "sort" => "invalidsortoption" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + block_numbers_order = + Enum.map(response["result"], fn transaction -> + String.to_integer(transaction["blockNumber"]) + end) + + assert block_numbers_order == Enum.sort(block_numbers_order, &(&1 >= &2)) + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with valid pagination params", %{conn: conn} do + # To get paginated results on this endpoint Etherscan's docs say: + # + # "(To get paginated results use page= and offset=)" + + first_block = insert(:block) + second_block = insert(:block) + third_block = insert(:block) + address = insert(:address) + + _second_block_transactions = + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(second_block) + + first_block_transactions = + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(third_block) + + _third_block_transactions = + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(first_block) + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + # page number + "page" => "1", + # page size + "offset" => "2" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + page1_hashes = Enum.map(response["result"], & &1["hash"]) + + assert length(response["result"]) == 2 + + for transaction <- first_block_transactions do + assert "#{transaction.hash}" in page1_hashes + end + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "ignores pagination params when invalid", %{conn: conn} do + first_block = insert(:block) + second_block = insert(:block) + third_block = insert(:block) + address = insert(:address) + + _second_block_transactions = + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(second_block) + + _third_block_transactions = + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(third_block) + + _first_block_transactions = + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(first_block) + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + # page number + "page" => "invalidpage", + # page size + "offset" => "invalidoffset" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 6 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "ignores offset param if offset is less than 1", %{conn: conn} do + address = insert(:address) + + 6 + |> insert_list(:transaction, from_address: address) + |> with_block() + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + # page number + "page" => "1", + # page size + "offset" => "0" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 6 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "ignores offset param if offset is over 10,000", %{conn: conn} do + address = insert(:address) + + 6 + |> insert_list(:transaction, from_address: address) + |> with_block() + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + # page number + "page" => "1", + # page size + "offset" => "10_500" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 6 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with page number with no results", %{conn: conn} do + first_block = insert(:block) + second_block = insert(:block) + third_block = insert(:block) + address = insert(:address) + + _second_block_transactions = + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(second_block) + + _third_block_transactions = + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(third_block) + + _first_block_transactions = + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(first_block) + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + # page number + "page" => "5", + # page size + "offset" => "2" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == [] + assert response["status"] == "0" + assert response["message"] == "No transactions found" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with start_block and end_block params", %{conn: conn} do + blocks = [_, second_block, third_block, _] = insert_list(4, :block) + address = insert(:address) + + for block <- blocks do + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(block) + end + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "start_block" => "#{second_block.number}", + "end_block" => "#{third_block.number}" + } + + expected_block_numbers = [ + "#{second_block.number}", + "#{third_block.number}" + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 4 + + for transaction <- response["result"] do + assert transaction["blockNumber"] in expected_block_numbers + end + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with start_block but without end_block", %{conn: conn} do + blocks = [_, _, third_block, fourth_block] = insert_list(4, :block) + address = insert(:address) + + for block <- blocks do + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(block) + end + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "start_block" => "#{third_block.number}" + } + + expected_block_numbers = [ + "#{third_block.number}", + "#{fourth_block.number}" + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 4 + + for transaction <- response["result"] do + assert transaction["blockNumber"] in expected_block_numbers + end + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with end_block but without start_block", %{conn: conn} do + blocks = [first_block, second_block, _, _] = insert_list(4, :block) + address = insert(:address) + + for block <- blocks do + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(block) + end + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "end_block" => "#{second_block.number}" + } + + expected_block_numbers = [ + "#{first_block.number}", + "#{second_block.number}" + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 4 + + for transaction <- response["result"] do + assert transaction["blockNumber"] in expected_block_numbers + end + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "ignores invalid start_block and end_block", %{conn: conn} do + blocks = [_, _, _, _] = insert_list(4, :block) + address = insert(:address) + + for block <- blocks do + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(block) + end + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "start_block" => "invalidstart", + "end_block" => "invalidend" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 8 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with start_timestamp and end_timestamp params", %{conn: conn} do + now = Timex.now() + timestamp1 = Timex.shift(now, hours: -6) + timestamp2 = Timex.shift(now, hours: -3) + timestamp3 = Timex.shift(now, hours: -1) + blocks1 = insert_list(2, :block, timestamp: timestamp1) + blocks2 = [third_block, fourth_block] = insert_list(2, :block, timestamp: timestamp2) + blocks3 = insert_list(2, :block, timestamp: timestamp3) + address = insert(:address) + + for block <- Enum.concat([blocks1, blocks2, blocks3]) do + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(block) + end + + start_timestamp = now |> Timex.shift(hours: -4) |> Timex.to_unix() + end_timestamp = now |> Timex.shift(hours: -2) |> Timex.to_unix() + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "start_timestamp" => "#{start_timestamp}", + "end_timestamp" => "#{end_timestamp}" + } + + expected_block_numbers = [ + "#{third_block.number}", + "#{fourth_block.number}" + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 4 + + for transaction <- response["result"] do + assert transaction["blockNumber"] in expected_block_numbers + end + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with start_timestamp but without end_timestamp", %{conn: conn} do + now = Timex.now() + timestamp1 = Timex.shift(now, hours: -6) + timestamp2 = Timex.shift(now, hours: -3) + timestamp3 = Timex.shift(now, hours: -1) + blocks1 = insert_list(2, :block, timestamp: timestamp1) + blocks2 = [third_block, fourth_block] = insert_list(2, :block, timestamp: timestamp2) + blocks3 = [fifth_block, sixth_block] = insert_list(2, :block, timestamp: timestamp3) + address = insert(:address) + + for block <- Enum.concat([blocks1, blocks2, blocks3]) do + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(block) + end + + start_timestamp = now |> Timex.shift(hours: -4) |> Timex.to_unix() + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "start_timestamp" => "#{start_timestamp}" + } + + expected_block_numbers = [ + "#{third_block.number}", + "#{fourth_block.number}", + "#{fifth_block.number}", + "#{sixth_block.number}" + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 8 + + for transaction <- response["result"] do + assert transaction["blockNumber"] in expected_block_numbers + end + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with end_timestamp but without start_timestamp", %{conn: conn} do + now = Timex.now() + timestamp1 = Timex.shift(now, hours: -6) + timestamp2 = Timex.shift(now, hours: -3) + timestamp3 = Timex.shift(now, hours: -1) + blocks1 = [first_block, second_block] = insert_list(2, :block, timestamp: timestamp1) + blocks2 = insert_list(2, :block, timestamp: timestamp2) + blocks3 = insert_list(2, :block, timestamp: timestamp3) + address = insert(:address) + + for block <- Enum.concat([blocks1, blocks2, blocks3]) do + 2 + |> insert_list(:transaction, from_address: address) + |> with_block(block) + end + + end_timestamp = now |> Timex.shift(hours: -5) |> Timex.to_unix() + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "end_timestamp" => "#{end_timestamp}" + } + + expected_block_numbers = [ + "#{first_block.number}", + "#{second_block.number}" + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 4 + + for transaction <- response["result"] do + assert transaction["blockNumber"] in expected_block_numbers + end + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with filter_by=to option", %{conn: conn} do + block = insert(:block) + address = insert(:address) + + insert(:transaction, from_address: address) + |> with_block(block) + + insert(:transaction, to_address: address) + |> with_block(block) + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "filter_by" => "to" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 1 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with filter_by=from option", %{conn: conn} do + block = insert(:block) + address = insert(:address) + + insert(:transaction, from_address: address) + |> with_block(block) + + insert(:transaction, from_address: address) + |> with_block(block) + + insert(:transaction, to_address: address) + |> with_block(block) + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}", + "filter_by" => "from" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 2 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "supports GET and POST requests", %{conn: conn} do + address = insert(:address) + + :transaction + |> insert(from_address: address) + |> with_block() + + params = %{ + "module" => "account", + "action" => "txlist", + "address" => "#{address.hash}" + } + + assert get_response = + conn + |> get("/api", params) + |> json_response(200) + + assert post_response = + conn + |> post("/api", params) + |> json_response(200) + + assert get_response == post_response + end + end + + describe "pendingtxlist" do + test "with missing address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "pendingtxlist" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "'address' is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with an invalid address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "pendingtxlist", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid address format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with an address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "pendingtxlist", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == [] + assert response["status"] == "0" + assert response["message"] == "No transactions found" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with a valid address", %{conn: conn} do + address = insert(:address) + + transaction = + :transaction + |> insert(from_address: address) + + params = %{ + "module" => "account", + "action" => "pendingtxlist", + "address" => "#{address.hash}" + } + + expected_result = [ + %{ + "hash" => "#{transaction.hash}", + "nonce" => "#{transaction.nonce}", + "from" => "#{transaction.from_address_hash}", + "to" => "#{transaction.to_address_hash}", + "value" => "#{transaction.value.value}", + "gas" => "#{transaction.gas}", + "gasPrice" => "#{transaction.gas_price.value}", + "input" => "#{transaction.input}", + "contractAddress" => "#{transaction.created_contract_address_hash}", + "cumulativeGasUsed" => "#{transaction.cumulative_gas_used}", + "gasUsed" => "#{transaction.gas_used}" + } + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with address with multiple transactions", %{conn: conn} do + address1 = insert(:address) + address2 = insert(:address) + + transactions = + 3 + |> insert_list(:transaction, from_address: address1) + + :transaction + |> insert(from_address: address2) + + params = %{ + "module" => "account", + "action" => "pendingtxlist", + "address" => "#{address1.hash}" + } + + expected_transaction_hashes = Enum.map(transactions, &"#{&1.hash}") + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 3 + + for returned_transaction <- response["result"] do + assert returned_transaction["hash"] in expected_transaction_hashes + end + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with valid pagination params", %{conn: conn} do + address = insert(:address) + + _transactions_1 = + 2 + |> insert_list(:transaction, from_address: address) + + _transactions_2 = + 2 + |> insert_list(:transaction, from_address: address) + + transactions_3 = + 2 + |> insert_list(:transaction, from_address: address) + + params = %{ + "module" => "account", + "action" => "pendingtxlist", + "address" => "#{address.hash}", + # page number + "page" => "1", + # page size + "offset" => "2" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + page1_hashes = Enum.map(response["result"], & &1["hash"]) + + assert length(response["result"]) == 2 + + for transaction <- transactions_3 do + assert "#{transaction.hash}" in page1_hashes + end + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "ignores pagination params when invalid", %{conn: conn} do + address = insert(:address) + + _transactions_1 = + 2 + |> insert_list(:transaction, from_address: address) + + _transactions_2 = + 2 + |> insert_list(:transaction, from_address: address) + + _transactions_3 = + 2 + |> insert_list(:transaction, from_address: address) + + params = %{ + "module" => "account", + "action" => "pendingtxlist", + "address" => "#{address.hash}", + # page number + "page" => "invalidpage", + # page size + "offset" => "invalidoffset" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 6 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "ignores offset param if offset is less than 1", %{conn: conn} do + address = insert(:address) + + 6 + |> insert_list(:transaction, from_address: address) + + params = %{ + "module" => "account", + "action" => "pendingtxlist", + "address" => "#{address.hash}", + # page number + "page" => "1", + # page size + "offset" => "0" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 6 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "ignores offset param if offset is over 10,000", %{conn: conn} do + address = insert(:address) + + 6 + |> insert_list(:transaction, from_address: address) + + params = %{ + "module" => "account", + "action" => "pendingtxlist", + "address" => "#{address.hash}", + # page number + "page" => "1", + # page size + "offset" => "10_500" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 6 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "with page number with no results", %{conn: conn} do + address = insert(:address) + + _transactions_1 = + 2 + |> insert_list(:transaction, from_address: address) + + _transactions_2 = + 2 + |> insert_list(:transaction, from_address: address) + + _transactions_3 = + 2 + |> insert_list(:transaction, from_address: address) + + params = %{ + "module" => "account", + "action" => "pendingtxlist", + "address" => "#{address.hash}", + # page number + "page" => "5", + # page size + "offset" => "2" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == [] + assert response["status"] == "0" + assert response["message"] == "No transactions found" + assert :ok = ExJsonSchema.Validator.validate(txlist_schema(), response) + end + + test "supports GET and POST requests", %{conn: conn} do + address = insert(:address) + + :transaction + |> insert(from_address: address) + + params = %{ + "module" => "account", + "action" => "pendingtxlist", + "address" => "#{address.hash}" + } + + assert get_response = + conn + |> get("/api", params) + |> json_response(200) + + assert post_response = + conn + |> post("/api", params) + |> json_response(200) + + assert get_response == post_response + end + end + + describe "txlistinternal" do + test "with missing txhash and address", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "txlistinternal" + } + + response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "txhash or address is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) + end + end + + describe "txlistinternal with txhash" do + test "with an invalid txhash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "txlistinternal", + "txhash" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid txhash format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) + end + + test "with a txhash that doesn't exist", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "txlistinternal", + "txhash" => "0x40eb908387324f2b575b4879cd9d7188f69c8fc9d87c901b9e2daaea4b442170" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == [] + assert response["status"] == "0" + assert response["message"] == "No internal transactions found" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) + end + + test "response includes all the expected fields", %{conn: conn} do + address = insert(:address) + contract_address = insert(:contract_address) + + block = insert(:block) + + transaction = + :transaction + |> insert(from_address: address, to_address: nil) + |> with_contract_creation(contract_address) + |> with_block(block) + + internal_transaction = + :internal_transaction_create + |> insert( + transaction: transaction, + index: 0, + from_address: address, + block_hash: transaction.block_hash, + block_index: 0 + ) + |> with_contract_creation(contract_address) + + params = %{ + "module" => "account", + "action" => "txlistinternal", + "txhash" => "#{transaction.hash}" + } + + expected_result = [ + %{ + "blockNumber" => "#{transaction.block_number}", + "timeStamp" => "#{DateTime.to_unix(block.timestamp)}", + "from" => "#{internal_transaction.from_address_hash}", + "to" => "#{internal_transaction.to_address_hash}", + "value" => "#{internal_transaction.value.value}", + "contractAddress" => "#{contract_address.hash}", + "input" => "", + "type" => "#{internal_transaction.type}", + "callType" => "#{internal_transaction.call_type}", + "gas" => "#{internal_transaction.gas}", + "gasUsed" => "#{internal_transaction.gas_used}", + "index" => "#{internal_transaction.index}", + "transactionHash" => "#{transaction.hash}", + "isError" => "0", + "errCode" => "#{internal_transaction.error}" + } + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) + end + + test "isError is true if internal transaction has an error", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + internal_transaction_details = [ + transaction: transaction, + index: 0, + type: :reward, + error: "some error", + block_hash: transaction.block_hash, + block_index: 0 + ] + + insert(:internal_transaction_create, internal_transaction_details) + + params = %{ + "module" => "account", + "action" => "txlistinternal", + "txhash" => "#{transaction.hash}" + } + + assert %{"result" => [found_internal_transaction]} = + response = + conn + |> get("/api", params) + |> json_response(200) + + assert found_internal_transaction["isError"] == "1" + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) + end + + test "with transaction with multiple internal transactions", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + for index <- 0..2 do + insert(:internal_transaction_create, + transaction: transaction, + index: index, + block_hash: transaction.block_hash, + block_index: index + ) + end + + params = %{ + "module" => "account", + "action" => "txlistinternal", + "txhash" => "#{transaction.hash}" + } + + assert %{"result" => found_internal_transactions} = + response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(found_internal_transactions) == 3 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) + end + end + + describe "txlistinternal with address" do + test "with an invalid address", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "txlistinternal", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid address format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) + end + + test "with a address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "txlistinternal", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == [] + assert response["status"] == "0" + assert response["message"] == "No internal transactions found" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) + end + + test "response includes all the expected fields", %{conn: conn} do + address = insert(:address) + contract_address = insert(:contract_address) + + block = insert(:block) + + transaction = + :transaction + |> insert(from_address: address, to_address: nil) + |> with_contract_creation(contract_address) + |> with_block(block) + + internal_transaction = + :internal_transaction_create + |> insert( + transaction: transaction, + index: 0, + from_address: address, + block_number: block.number, + block_hash: transaction.block_hash, + block_index: 0 + ) + |> with_contract_creation(contract_address) + + params = %{ + "module" => "account", + "action" => "txlistinternal", + "address" => "#{address.hash}" + } + + expected_result = [ + %{ + "blockNumber" => "#{transaction.block_number}", + "timeStamp" => "#{DateTime.to_unix(block.timestamp)}", + "from" => "#{internal_transaction.from_address_hash}", + "to" => "#{internal_transaction.to_address_hash}", + "value" => "#{internal_transaction.value.value}", + "contractAddress" => "#{contract_address.hash}", + "input" => "", + "type" => "#{internal_transaction.type}", + "callType" => "#{internal_transaction.call_type}", + "gas" => "#{internal_transaction.gas}", + "gasUsed" => "#{internal_transaction.gas_used}", + "isError" => "0", + "index" => "#{internal_transaction.index}", + "transactionHash" => "#{transaction.hash}", + "errCode" => "#{internal_transaction.error}" + } + ] + + response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) + end + + test "isError is true if internal transaction has an error", %{conn: conn} do + address = insert(:address) + + transaction = + :transaction + |> insert() + |> with_block() + + internal_transaction_details = [ + from_address: address, + transaction: transaction, + index: 0, + type: :reward, + error: "some error", + block_number: transaction.block_number, + block_hash: transaction.block_hash, + block_index: 0 + ] + + insert(:internal_transaction_create, internal_transaction_details) + + params = %{ + "module" => "account", + "action" => "txlistinternal", + "address" => "#{address.hash}" + } + + assert %{"result" => [found_internal_transaction]} = + response = + conn + |> get("/api", params) + |> json_response(200) + + assert found_internal_transaction["isError"] == "1" + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) + end + + test "with transaction with multiple internal transactions", %{conn: conn} do + address = insert(:address) + + transaction = + :transaction + |> insert() + |> with_block() + + for index <- 0..2 do + internal_transaction_details = %{ + from_address: address, + transaction: transaction, + index: index, + block_number: transaction.block_number, + block_hash: transaction.block_hash, + block_index: index + } + + insert(:internal_transaction_create, internal_transaction_details) + end + + params = %{ + "module" => "account", + "action" => "txlistinternal", + "address" => "#{address.hash}" + } + + assert %{"result" => found_internal_transactions} = + response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(found_internal_transactions) == 3 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(txlistinternal_schema(), response) + end + end + + describe "tokentx" do + test "with missing address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokentx" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "address is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) + end + + test "with an invalid address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokentx", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid address format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) + end + + test "with an address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokentx", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == [] + assert response["status"] == "0" + assert response["message"] == "No token transfers found" + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) + end + + test "has correct value for ERC-721", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + token_address = insert(:contract_address) + insert(:token, %{contract_address: token_address, type: "ERC-721"}) + + token_transfer = + insert(:token_transfer, %{ + token_contract_address: token_address, + token_id: 666, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number + }) + + {:ok, _} = Chain.token_from_address_hash(token_transfer.token_contract_address_hash) + + params = %{ + "module" => "account", + "action" => "tokentx", + "address" => to_string(token_transfer.from_address.hash) + } + + assert response = + %{"result" => [result]} = + conn + |> get("/api", params) + |> json_response(200) + + assert result["tokenID"] == to_string(token_transfer.token_id) + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) + end + + test "returns all the required fields", %{conn: conn} do + transaction = + %{block: block} = + :transaction + |> insert() + |> with_block() + + token_transfer = + insert(:token_transfer, block: transaction.block, transaction: transaction, block_number: block.number) + + {:ok, token} = Chain.token_from_address_hash(token_transfer.token_contract_address_hash) + + params = %{ + "module" => "account", + "action" => "tokentx", + "address" => to_string(token_transfer.from_address.hash) + } + + expected_result = [ + %{ + "blockNumber" => to_string(transaction.block_number), + "timeStamp" => to_string(DateTime.to_unix(block.timestamp)), + "hash" => to_string(token_transfer.transaction_hash), + "nonce" => to_string(transaction.nonce), + "blockHash" => to_string(block.hash), + "from" => to_string(token_transfer.from_address_hash), + "contractAddress" => to_string(token_transfer.token_contract_address_hash), + "to" => to_string(token_transfer.to_address_hash), + "value" => to_string(token_transfer.amount), + "tokenName" => token.name, + "tokenSymbol" => token.symbol, + "tokenDecimal" => to_string(token.decimals), + "transactionIndex" => to_string(transaction.index), + "gas" => to_string(transaction.gas), + "gasPrice" => to_string(transaction.gas_price.value), + "gasUsed" => to_string(transaction.gas_used), + "cumulativeGasUsed" => to_string(transaction.cumulative_gas_used), + "logIndex" => to_string(token_transfer.log_index), + "input" => to_string(transaction.input), + "confirmations" => "0" + } + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) + end + + test "with an invalid contract address", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokentx", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b", + "contractaddress" => "invalid" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid contract address format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) + end + + test "filters results by contract address", %{conn: conn} do + address = insert(:address) + + contract_address = insert(:contract_address) + + insert(:token, contract_address: contract_address) + + transaction = + :transaction + |> insert() + |> with_block() + + insert(:token_transfer, + from_address: address, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number + ) + + insert(:token_transfer, + from_address: address, + token_contract_address: contract_address, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number + ) + + params = %{ + "module" => "account", + "action" => "tokentx", + "address" => to_string(address.hash), + "contractaddress" => to_string(contract_address.hash) + } + + assert response = + %{"result" => [result]} = + conn + |> get("/api", params) + |> json_response(200) + + assert result["contractAddress"] == to_string(contract_address.hash) + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokentx_schema(), response) + end + end + + describe "tokenbalance" do + test "without required params", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokenbalance" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "missing: address, contractaddress" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) + end + + test "with contract address but without address", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokenbalance", + "contractaddress" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "missing: address" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) + end + + test "with address but without contract address", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokenbalance", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "missing: contractaddress" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) + end + + test "with an invalid contract address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokenbalance", + "contractaddress" => "badhash", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid contractaddress format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) + end + + test "with an invalid address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokenbalance", + "contractaddress" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid address format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) + end + + test "with a contract address and address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokenbalance", + "contractaddress" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b", + "address" => "0x9bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == "0" + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) + end + + test "with contract address and address without row in token_balances table", %{conn: conn} do + token = insert(:token) + address = insert(:address) + + params = %{ + "module" => "account", + "action" => "tokenbalance", + "contractaddress" => to_string(token.contract_address_hash), + "address" => to_string(address.hash) + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == "0" + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) + end + + test "with contract address and address with existing balance in token_balances table", %{conn: conn} do + current_token_balance = insert(:address_current_token_balance) + + params = %{ + "module" => "account", + "action" => "tokenbalance", + "contractaddress" => to_string(current_token_balance.token_contract_address_hash), + "address" => to_string(current_token_balance.address_hash) + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == to_string(current_token_balance.value) + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokenbalance_schema(), response) + end + end + + describe "tokenlist" do + test "without address param", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokenlist" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "address is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) + end + + test "with an invalid address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokenlist", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid address format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) + end + + test "with an address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "tokenlist", + "address" => "0x9bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == [] + assert response["status"] == "0" + assert response["message"] == "No tokens found" + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) + end + + test "with an address without row in token_balances table", %{conn: conn} do + address = insert(:address) + + params = %{ + "module" => "account", + "action" => "tokenlist", + "address" => to_string(address.hash) + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == [] + assert response["status"] == "0" + assert response["message"] == "No tokens found" + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) + end + + test "with address with existing balance in token_balances table", %{conn: conn} do + token_balance = :address_current_token_balance |> insert() |> Repo.preload(:token) + + params = %{ + "module" => "account", + "action" => "tokenlist", + "address" => to_string(token_balance.address_hash) + } + + expected_result = [ + %{ + "balance" => to_string(token_balance.value), + "contractAddress" => to_string(token_balance.token_contract_address_hash), + "name" => token_balance.token.name, + "decimals" => to_string(token_balance.token.decimals), + "symbol" => token_balance.token.symbol, + "type" => token_balance.token.type + } + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) + end + + test "with address with multiple tokens", %{conn: conn} do + address = insert(:address) + other_address = insert(:address) + insert(:address_current_token_balance, address: address) + insert(:address_current_token_balance, address: address) + insert(:address_current_token_balance, address: other_address) + + params = %{ + "module" => "account", + "action" => "tokenlist", + "address" => to_string(address.hash) + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(response["result"]) == 2 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokenlist_schema(), response) + end + end + + describe "getminedblocks" do + test "with missing address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "getminedblocks" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "'address' is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) + end + + test "with an invalid address hash", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "getminedblocks", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid address format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) + end + + test "with an address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "account", + "action" => "getminedblocks", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == [] + assert response["status"] == "0" + assert response["message"] == "No blocks found" + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) + end + + test "returns all the required fields", %{conn: conn} do + %{block_range: range} = insert(:emission_reward) + + block = insert(:block, number: Enum.random(Range.new(range.from, range.to))) + + :transaction + |> insert(gas_price: 1) + |> with_block(block, gas_used: 1) + + expected_result = [ + %{ + "blockNumber" => to_string(block.number), + "timeStamp" => to_string(block.timestamp) + } + ] + + params = %{ + "module" => "account", + "action" => "getminedblocks", + "address" => to_string(block.miner_hash) + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) + end + + test "with a block with one transaction", %{conn: conn} do + %{block_range: range} = insert(:emission_reward) + + block = insert(:block, number: Enum.random(Range.new(range.from, range.to))) + + :transaction + |> insert(gas_price: 1) + |> with_block(block, gas_used: 1) + + params = %{ + "module" => "account", + "action" => "getminedblocks", + "address" => to_string(block.miner_hash) + } + + expected_result = [ + %{ + "blockNumber" => to_string(block.number), + "timeStamp" => to_string(block.timestamp) + } + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) + end + + test "with pagination options", %{conn: conn} do + %{block_range: range} = insert(:emission_reward) + + block_numbers = Range.new(range.from, range.to) + + [block_number1, block_number2] = Enum.take(block_numbers, 2) + + address = insert(:address) + + _block1 = insert(:block, number: block_number1, miner: address) + block2 = insert(:block, number: block_number2, miner: address) + + :transaction + |> insert(gas_price: 2) + |> with_block(block2, gas_used: 2) + + params = %{ + "module" => "account", + "action" => "getminedblocks", + "address" => to_string(address.hash), + # page number + "page" => "1", + # page size + "offset" => "1" + } + + expected_result = [ + %{ + "blockNumber" => to_string(block2.number), + "timeStamp" => to_string(block2.timestamp) + } + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(block_schema(), response) + end + end + + describe "optional_params/1" do + test "includes valid optional params in the required format" do + params = %{ + "start_block" => "100", + "end_block" => "120", + "sort" => "asc", + # page number + "page" => "1", + # page size + "offset" => "2", + "filter_by" => "to", + "start_timestamp" => "1539186474", + "end_timestamp" => "1539186474" + } + + optional_params = AddressController.optional_params(params) + + # 1539186474 equals "2018-10-10 15:47:54Z" + {:ok, expected_timestamp, _} = DateTime.from_iso8601("2018-10-10 15:47:54Z") + + assert optional_params.page_number == 1 + assert optional_params.page_size == 2 + assert optional_params.order_by_direction == :asc + assert optional_params.start_block == 100 + assert optional_params.end_block == 120 + assert optional_params.filter_by == "to" + assert optional_params.start_timestamp == expected_timestamp + assert optional_params.end_timestamp == expected_timestamp + end + + test "'sort' values can be 'asc' or 'desc'" do + params1 = %{"sort" => "asc"} + + optional_params = AddressController.optional_params(params1) + + assert optional_params.order_by_direction == :asc + + params2 = %{"sort" => "desc"} + + optional_params = AddressController.optional_params(params2) + + assert optional_params.order_by_direction == :desc + + params3 = %{"sort" => "invalid"} + + assert AddressController.optional_params(params3) == %{} + end + + test "'filter_by' value can be 'to' or 'from'" do + params1 = %{"filter_by" => "to"} + + optional_params1 = AddressController.optional_params(params1) + + assert optional_params1.filter_by == "to" + + params2 = %{"filter_by" => "from"} + + optional_params2 = AddressController.optional_params(params2) + + assert optional_params2.filter_by == "from" + + params3 = %{"filter_by" => "invalid"} + + assert AddressController.optional_params(params3) == %{} + end + + test "only includes optional params when they're given" do + assert AddressController.optional_params(%{}) == %{} + end + + test "ignores invalid optional params, keeps valid ones" do + params1 = %{ + "start_block" => "invalid", + "end_block" => "invalid", + "sort" => "invalid", + "page" => "invalid", + "offset" => "invalid", + "start_timestamp" => "invalid", + "end_timestamp" => "invalid" + } + + assert AddressController.optional_params(params1) == %{} + + params2 = %{ + "start_block" => "4", + "end_block" => "10", + "sort" => "invalid", + "page" => "invalid", + "offset" => "invalid", + "start_timestamp" => "invalid", + "end_timestamp" => "invalid" + } + + optional_params = AddressController.optional_params(params2) + + assert optional_params.start_block == 4 + assert optional_params.end_block == 10 + end + + test "ignores 'page' if less than 1" do + params = %{"page" => "0"} + + assert AddressController.optional_params(params) == %{} + end + + test "ignores 'offset' if less than 1" do + params = %{"offset" => "0"} + + assert AddressController.optional_params(params) == %{} + end + + test "ignores 'offset' if more than 10,000" do + params = %{"offset" => "10001"} + + assert AddressController.optional_params(params) == %{} + end + end + + describe "fetch_required_params/2" do + test "returns error with missing param" do + params = %{"address" => "some address"} + + required_params = ~w(address contractaddress) + + result = AddressController.fetch_required_params(params, required_params) + + assert result == {:required_params, {:error, ["contractaddress"]}} + end + + test "returns ok with all required params" do + params = %{"address" => "some address", "contractaddress" => "some contract"} + + required_params = ~w(address contractaddress) + + result = AddressController.fetch_required_params(params, required_params) + + assert result == {:required_params, {:ok, params}} + end + end + + defp listaccounts_schema do + resolve_schema(%{ + "type" => "array", + "items" => %{ + "type" => "object", + "properties" => %{ + "address" => %{"type" => "string"}, + "balance" => %{"type" => "string"}, + "stale" => %{"type" => "boolean"} + } + } + }) + end + + defp balance_schema do + resolve_schema(%{ + "type" => ["string", "null", "array"], + "items" => %{ + "type" => "object", + "properties" => %{ + "account" => %{"type" => "string"}, + "balance" => %{"type" => "string"}, + "stale" => %{"type" => "boolean"} + } + } + }) + end + + defp txlist_schema do + resolve_schema(%{ + "type" => ["null", "array"], + "items" => %{ + "type" => "object", + "properties" => %{ + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "hash" => %{"type" => "string"}, + "nonce" => %{"type" => "string"}, + "blockHash" => %{"type" => "string"}, + "transactionIndex" => %{"type" => "string"}, + "from" => %{"type" => "string"}, + "to" => %{"type" => "string"}, + "value" => %{"type" => "string"}, + "gas" => %{"type" => "string"}, + "gasPrice" => %{"type" => "string"}, + "isError" => %{"type" => "string"}, + "txreceipt_status" => %{"type" => "string"}, + "input" => %{"type" => "string"}, + "contractAddress" => %{"type" => "string"}, + "cumulativeGasUsed" => %{"type" => "string"}, + "gasUsed" => %{"type" => "string"}, + "confirmations" => %{"type" => "string"} + } + } + }) + end + + defp txlistinternal_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "from" => %{"type" => "string"}, + "to" => %{"type" => "string"}, + "value" => %{"type" => "string"}, + "contractAddress" => %{"type" => "string"}, + "transactionHash" => %{"type" => "string"}, + "index" => %{"type" => "string"}, + "input" => %{"type" => "string"}, + "type" => %{"type" => "string"}, + "gas" => %{"type" => "string"}, + "gasUsed" => %{"type" => "string"}, + "isError" => %{"type" => "string"}, + "errCode" => %{"type" => "string"} + } + } + }) + end + + defp tokentx_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "hash" => %{"type" => "string"}, + "nonce" => %{"type" => "string"}, + "blockHash" => %{"type" => "string"}, + "from" => %{"type" => "string"}, + "contractAddress" => %{"type" => "string"}, + "to" => %{"type" => "string"}, + "logIndex" => %{"type" => "string"}, + "value" => %{"type" => "string"}, + "tokenName" => %{"type" => "string"}, + "tokenID" => %{"type" => "string"}, + "tokenSymbol" => %{"type" => "string"}, + "tokenDecimal" => %{"type" => "string"}, + "transactionIndex" => %{"type" => "string"}, + "gas" => %{"type" => "string"}, + "gasPrice" => %{"type" => "string"}, + "gasUsed" => %{"type" => "string"}, + "cumulativeGasUsed" => %{"type" => "string"}, + "input" => %{"type" => "string"}, + "confirmations" => %{"type" => "string"} + } + } + }) + end + + defp tokenbalance_schema, do: resolve_schema(%{"type" => ["string", "null"]}) + + defp tokenlist_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "balance" => %{"type" => "string"}, + "contractAddress" => %{"type" => "string"}, + "name" => %{"type" => "string"}, + "decimals" => %{"type" => "string"}, + "symbol" => %{"type" => "string"}, + "type" => %{"type" => "string"} + } + } + }) + end + + defp block_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "blockReward" => %{"type" => "string"} + } + } + }) + end + + defp resolve_schema(result) do + %{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"} + } + } + |> put_in(["properties", "result"], result) + |> ExJsonSchema.Schema.resolve() + end + + defp eth_block_number_fake_response(block_quantity) do + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x0000000000000000000000000000000000000000", + "difficulty" => "0x20000", + "extraData" => "0x", + "gasLimit" => "0x663be0", + "gasUsed" => "0x0", + "hash" => "0x5b28c1bfd3a15230c9a46b399cd0f9a6920d432e85381cc6a140b06e8410112f", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x0000000000000000000000000000000000000000", + "number" => block_quantity, + "parentHash" => "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x80", + "0xb8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "size" => "0x215", + "stateRoot" => "0xfad4af258fd11939fae0c6c6eec9d340b1caac0b0196fd9a1bc3f489c5bf00b3", + "step" => "0", + "timestamp" => "0x0", + "totalDifficulty" => "0x20000", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs new file mode 100644 index 0000000..70e9726 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/block_controller_test.exs @@ -0,0 +1,238 @@ +defmodule BlockScoutWeb.API.RPC.BlockControllerTest do + use BlockScoutWeb.ConnCase + + alias Explorer.Chain.{Hash, Wei} + alias BlockScoutWeb.Chain + + describe "getblockreward" do + test "with missing block number", %{conn: conn} do + response = + conn + |> get("/api", %{"module" => "block", "action" => "getblockreward"}) + |> json_response(200) + + assert response["message"] =~ "'blockno' is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "with an invalid block number", %{conn: conn} do + response = + conn + |> get("/api", %{"module" => "block", "action" => "getblockreward", "blockno" => "badnumber"}) + |> json_response(200) + + assert response["message"] =~ "Invalid block number" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "with a block that doesn't exist", %{conn: conn} do + response = + conn + |> get("/api", %{"module" => "block", "action" => "getblockreward", "blockno" => "42"}) + |> json_response(200) + + assert response["message"] =~ "Block does not exist" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "with a valid block", %{conn: conn} do + %{block_range: range} = emission_reward = insert(:emission_reward) + block = insert(:block, number: Enum.random(Range.new(range.from, range.to))) + + :transaction + |> insert(gas_price: 1) + |> with_block(block, gas_used: 1) + + expected_reward = + emission_reward.reward + |> Wei.to(:wei) + |> Decimal.add(Decimal.new(1)) + |> Decimal.to_string(:normal) + + expected_result = %{ + "blockNumber" => "#{block.number}", + "timeStamp" => DateTime.to_unix(block.timestamp), + "blockMiner" => Hash.to_string(block.miner_hash), + "blockReward" => expected_reward, + "uncles" => nil, + "uncleInclusionReward" => nil + } + + assert response = + conn + |> get("/api", %{"module" => "block", "action" => "getblockreward", "blockno" => "#{block.number}"}) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + end + + describe "getblocknobytime" do + test "with missing timestamp param", %{conn: conn} do + response = + conn + |> get("/api", %{"module" => "block", "action" => "getblocknobytime", "closest" => "after"}) + |> json_response(200) + + assert response["message"] =~ "Query parameter 'timestamp' is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "with missing closest param", %{conn: conn} do + response = + conn + |> get("/api", %{"module" => "block", "action" => "getblocknobytime", "timestamp" => "1617019505"}) + |> json_response(200) + + assert response["message"] =~ "Query parameter 'closest' is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "with an invalid timestamp param", %{conn: conn} do + response = + conn + |> get("/api", %{ + "module" => "block", + "action" => "getblocknobytime", + "timestamp" => "invalid", + "closest" => " before" + }) + |> json_response(200) + + assert response["message"] =~ "Invalid `timestamp` param" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "with an invalid closest param", %{conn: conn} do + response = + conn + |> get("/api", %{ + "module" => "block", + "action" => "getblocknobytime", + "timestamp" => "1617019505", + "closest" => "invalid" + }) + |> json_response(200) + + assert response["message"] =~ "Invalid `closest` param" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "with valid params and before", %{conn: conn} do + timestamp_string = "1617020209" + {:ok, timestamp} = Chain.param_to_block_timestamp(timestamp_string) + block = insert(:block, timestamp: timestamp) + + {timestamp_int, _} = Integer.parse(timestamp_string) + + timestamp_in_the_future_str = + (timestamp_int + 1) + |> to_string() + + expected_result = %{ + "blockNumber" => "#{block.number}" + } + + assert response = + conn + |> get("/api", %{ + "module" => "block", + "action" => "getblocknobytime", + "timestamp" => "#{timestamp_in_the_future_str}", + "closest" => "before" + }) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + + test "with valid params and after", %{conn: conn} do + timestamp_string = "1617020209" + {:ok, timestamp} = Chain.param_to_block_timestamp(timestamp_string) + block = insert(:block, timestamp: timestamp) + + {timestamp_int, _} = Integer.parse(timestamp_string) + + timestamp_in_the_past_str = + (timestamp_int - 1) + |> to_string() + + expected_result = %{ + "blockNumber" => "#{block.number}" + } + + assert response = + conn + |> get("/api", %{ + "module" => "block", + "action" => "getblocknobytime", + "timestamp" => "#{timestamp_in_the_past_str}", + "closest" => "after" + }) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + schema = resolve_schema() + assert :ok = ExJsonSchema.Validator.validate(schema, response) + end + end + + defp resolve_schema() do + ExJsonSchema.Schema.resolve(%{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"}, + "result" => %{ + "type" => ["object", "null"], + "properties" => %{ + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "number"}, + "blockMiner" => %{"type" => "string"}, + "blockReward" => %{"type" => "string"}, + "uncles" => %{"type" => "null"}, + "uncleInclusionReward" => %{"type" => "null"} + } + } + } + }) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs new file mode 100644 index 0000000..5c4306b --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs @@ -0,0 +1,971 @@ +defmodule BlockScoutWeb.API.RPC.ContractControllerTest do + use BlockScoutWeb.ConnCase + alias Explorer.Chain.SmartContract + alias Explorer.Chain + # alias Explorer.{Chain, Factory} + + import Mox + + def prepare_contracts do + insert(:contract_address) + {:ok, dt_1, _} = DateTime.from_iso8601("2022-09-20 10:00:00Z") + + contract_1 = + insert(:smart_contract, + contract_code_md5: "123", + name: "Test 1", + optimization: "1", + compiler_version: "v0.6.8+commit.0bbfe453", + abi: [%{foo: "bar"}], + inserted_at: dt_1 + ) + + insert(:contract_address) + {:ok, dt_2, _} = DateTime.from_iso8601("2022-09-22 10:00:00Z") + + contract_2 = + insert(:smart_contract, + contract_code_md5: "12345", + name: "Test 2", + optimization: "0", + compiler_version: "v0.7.5+commit.eb77ed08", + abi: [%{foo: "bar-2"}], + inserted_at: dt_2 + ) + + insert(:contract_address) + {:ok, dt_3, _} = DateTime.from_iso8601("2022-09-24 10:00:00Z") + + contract_3 = + insert(:smart_contract, + contract_code_md5: "1234567", + name: "Test 3", + optimization: "1", + compiler_version: "v0.4.26+commit.4563c3fc", + abi: [%{foo: "bar-3"}], + inserted_at: dt_3 + ) + + [contract_1, contract_2, contract_3] + end + + def result(contract) do + %{ + "ABI" => Jason.encode!(contract.abi), + "Address" => to_string(contract.address_hash), + "CompilerVersion" => contract.compiler_version, + "ContractName" => contract.name, + "OptimizationUsed" => if(contract.optimization, do: "1", else: "0") + } + end + + defp result_not_verified(address_hash) do + %{ + "ABI" => "Contract source code not verified", + "Address" => to_string(address_hash) + } + end + + describe "listcontracts" do + setup do + %{params: %{"module" => "contract", "action" => "listcontracts"}} + end + + test "with an invalid filter value", %{conn: conn, params: params} do + response = + conn + |> get("/api", Map.put(params, "filter", "invalid")) + |> json_response(400) + + assert response["message"] == + "invalid is not a valid value for `filter`. Please use one of: verified, decompiled, unverified, not_decompiled, 1, 2, 3, 4." + + assert response["status"] == "0" + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "with no contracts", %{conn: conn, params: params} do + response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + assert response["result"] == [] + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "with a verified smart contract, all contract information is shown", %{conn: conn, params: params} do + contract = insert(:smart_contract, contract_code_md5: "123") + + response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result(contract)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "with an unverified contract address, only basic information is shown", %{conn: conn, params: params} do + address = insert(:contract_address) + + response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result_not_verified(address.hash)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "filtering for only unverified contracts shows only unverified contracts", %{params: params, conn: conn} do + address = insert(:contract_address) + insert(:smart_contract, contract_code_md5: "123") + + response = + conn + |> get("/api", Map.put(params, "filter", "unverified")) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result_not_verified(address.hash)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "filtering for only unverified contracts does not show self destructed contracts", %{ + params: params, + conn: conn + } do + address = insert(:contract_address) + insert(:smart_contract, contract_code_md5: "123") + insert(:contract_address, contract_code: "0x") + + response = + conn + |> get("/api", Map.put(params, "filter", "unverified")) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result_not_verified(address.hash)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "filtering for only verified contracts shows only verified contracts", %{params: params, conn: conn} do + insert(:contract_address) + contract = insert(:smart_contract, contract_code_md5: "123") + + response = + conn + |> get("/api", Map.put(params, "filter", "verified")) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result(contract)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "filtering for only verified contracts in the date range shows only verified contracts in that range", %{ + params: params, + conn: conn + } do + [contract_1, contract_2, contract_3] = prepare_contracts() + + filter_params = + params + |> Map.put("filter", "verified") + |> Map.put("verified_at_start_timestamp", "1663749418") + |> Map.put("verified_at_end_timestamp", "1663922218") + + response = + conn + |> get("/api", filter_params) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result(contract_2)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "filtering for only verified contracts with start created_at timestamp >= given timestamp shows only verified contracts in that range", + %{ + params: params, + conn: conn + } do + [contract_1, contract_2, contract_3] = prepare_contracts() + + filter_params = + params + |> Map.put("filter", "verified") + |> Map.put("verified_at_start_timestamp", "1663749418") + + response = + conn + |> get("/api", filter_params) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result(contract_2), result(contract_3)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "filtering for only verified contracts with end created_at timestamp < given timestamp shows only verified contracts in that range", + %{ + params: params, + conn: conn + } do + [contract_1, contract_2, contract_3] = prepare_contracts() + + filter_params = + params + |> Map.put("filter", "verified") + |> Map.put("verified_at_end_timestamp", "1663922218") + + response = + conn + |> get("/api", filter_params) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result(contract_1), result(contract_2)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "filtering for only decompiled contracts shows only decompiled contracts", %{params: params, conn: conn} do + insert(:contract_address) + decompiled_smart_contract = insert(:decompiled_smart_contract) + + response = + conn + |> get("/api", Map.put(params, "filter", "decompiled")) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result_not_verified(decompiled_smart_contract.address_hash)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "filtering for only decompiled contracts, with a decompiled with version filter", %{params: params, conn: conn} do + insert(:decompiled_smart_contract, decompiler_version: "foobar") + smart_contract = insert(:decompiled_smart_contract, decompiler_version: "bizbuz") + + response = + conn + |> get("/api", Map.merge(params, %{"filter" => "decompiled", "not_decompiled_with_version" => "foobar"})) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result_not_verified(smart_contract.address_hash)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "filtering for only decompiled contracts, with a decompiled with version filter, where another decompiled version exists", + %{params: params, conn: conn} do + non_match = insert(:decompiled_smart_contract, decompiler_version: "foobar") + insert(:decompiled_smart_contract, decompiler_version: "bizbuz", address_hash: non_match.address_hash) + smart_contract = insert(:decompiled_smart_contract, decompiler_version: "bizbuz") + + response = + conn + |> get("/api", Map.merge(params, %{"filter" => "decompiled", "not_decompiled_with_version" => "foobar"})) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert result_not_verified(smart_contract.address_hash) in response["result"] + + refute to_string(non_match.address_hash) in Enum.map(response["result"], &Map.get(&1, "Address")) + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "filtering for only not_decompiled (and by extension not verified contracts)", %{params: params, conn: conn} do + insert(:decompiled_smart_contract) + insert(:smart_contract, contract_code_md5: "123") + contract_address = insert(:contract_address) + + response = + conn + |> get("/api", Map.put(params, "filter", "not_decompiled")) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result_not_verified(contract_address.hash)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + + test "filtering for only not_decompiled (and by extension not verified contracts) does not show empty contracts", %{ + params: params, + conn: conn + } do + insert(:decompiled_smart_contract) + insert(:smart_contract, contract_code_md5: "123") + insert(:contract_address, contract_code: "0x") + contract_address = insert(:contract_address) + + response = + conn + |> get("/api", Map.put(params, "filter", "not_decompiled")) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [result_not_verified(contract_address.hash)] + + assert :ok = ExJsonSchema.Validator.validate(listcontracts_schema(), response) + end + end + + describe "getabi" do + test "with missing address hash", %{conn: conn} do + params = %{ + "module" => "contract", + "action" => "getabi" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "address is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getabi_schema(), response) + end + + test "with an invalid address hash", %{conn: conn} do + params = %{ + "module" => "contract", + "action" => "getabi", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid address hash" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getabi_schema(), response) + end + + test "with an address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "contract", + "action" => "getabi", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == nil + assert response["status"] == "0" + assert response["message"] == "Contract source code not verified" + assert :ok = ExJsonSchema.Validator.validate(getabi_schema(), response) + end + + test "with a verified contract address", %{conn: conn} do + contract = insert(:smart_contract, contract_code_md5: "123") + + params = %{ + "module" => "contract", + "action" => "getabi", + "address" => to_string(contract.address_hash) + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == Jason.encode!(contract.abi) + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getabi_schema(), response) + end + end + + describe "getsourcecode" do + test "with missing address hash", %{conn: conn} do + params = %{ + "module" => "contract", + "action" => "getsourcecode" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "address is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) + end + + test "with an invalid address hash", %{conn: conn} do + params = %{ + "module" => "contract", + "action" => "getsourcecode", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid address hash" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) + end + + test "with an address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "contract", + "action" => "getsourcecode", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + expected_result = [ + %{ + "Address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) + end + + test "with a verified contract address", %{conn: conn} do + contract = + insert(:smart_contract, + optimization: true, + optimization_runs: 200, + evm_version: "default", + contract_code_md5: "123" + ) + + params = %{ + "module" => "contract", + "action" => "getsourcecode", + "address" => to_string(contract.address_hash) + } + + expected_result = [ + %{ + "Address" => to_string(contract.address_hash), + "SourceCode" => contract.contract_source_code, + "ABI" => Jason.encode!(contract.abi), + "ContractName" => contract.name, + "CompilerVersion" => contract.compiler_version, + # The contract's optimization value is true, so the expected value + # for `OptimizationUsed` is "1". If it was false, the expected value + # would be "0". + "OptimizationUsed" => "true", + "OptimizationRuns" => 200, + "EVMVersion" => "default", + "FileName" => "", + "IsProxy" => "false" + } + ] + + get_implementation() + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) + end + + test "with constructor arguments", %{conn: conn} do + contract = + insert(:smart_contract, + optimization: true, + optimization_runs: 200, + evm_version: "default", + constructor_arguments: + "00000000000000000000000008e7592ce0d7ebabf42844b62ee6a878d4e1913e000000000000000000000000e1b6037da5f1d756499e184ca15254a981c92546", + contract_code_md5: "123" + ) + + params = %{ + "module" => "contract", + "action" => "getsourcecode", + "address" => to_string(contract.address_hash) + } + + expected_result = [ + %{ + "Address" => to_string(contract.address_hash), + "SourceCode" => contract.contract_source_code, + "ABI" => Jason.encode!(contract.abi), + "ContractName" => contract.name, + "CompilerVersion" => contract.compiler_version, + "OptimizationUsed" => "true", + "OptimizationRuns" => 200, + "EVMVersion" => "default", + "ConstructorArguments" => + "00000000000000000000000008e7592ce0d7ebabf42844b62ee6a878d4e1913e000000000000000000000000e1b6037da5f1d756499e184ca15254a981c92546", + "FileName" => "", + "IsProxy" => "false" + } + ] + + get_implementation() + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) + end + + test "with external library", %{conn: conn} do + smart_contract_bytecode = + "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582040d82a7379b1ee1632ad4d8a239954fd940277b25628ead95259a85c5eddb2120029" + + created_contract_address = + insert( + :address, + hash: "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c", + contract_code: smart_contract_bytecode + ) + + transaction = + :transaction + |> insert() + |> with_block() + + insert( + :internal_transaction_create, + transaction: transaction, + index: 0, + created_contract_address: created_contract_address, + created_contract_code: smart_contract_bytecode, + block_number: transaction.block_number, + block_hash: transaction.block_hash, + block_index: 0, + transaction_index: transaction.index + ) + + valid_attrs = %{ + address_hash: "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c", + name: "Test", + compiler_version: "0.4.23", + contract_source_code: + "pragma solidity ^0.4.23; contract SimpleStorage {uint storedData; function set(uint x) public {storedData = x; } function get() public constant returns (uint) {return storedData; } }", + abi: [ + %{ + "constant" => false, + "inputs" => [%{"name" => "x", "type" => "uint256"}], + "name" => "set", + "outputs" => [], + "payable" => false, + "stateMutability" => "nonpayable", + "type" => "function" + }, + %{ + "constant" => true, + "inputs" => [], + "name" => "get", + "outputs" => [%{"name" => "", "type" => "uint256"}], + "payable" => false, + "stateMutability" => "view", + "type" => "function" + } + ], + optimization: true, + optimization_runs: 200, + evm_version: "default" + } + + external_libraries = [ + %SmartContract.ExternalLibrary{:address_hash => "0xb18aed9518d735482badb4e8b7fd8d2ba425ce95", :name => "Test"}, + %SmartContract.ExternalLibrary{:address_hash => "0x283539e1b1daf24cdd58a3e934d55062ea663c3f", :name => "Test2"} + ] + + {:ok, %SmartContract{} = contract} = Chain.create_smart_contract(valid_attrs, external_libraries) + + params = %{ + "module" => "contract", + "action" => "getsourcecode", + "address" => to_string(contract.address_hash) + } + + expected_result = [ + %{ + "Address" => to_string(contract.address_hash), + "SourceCode" => contract.contract_source_code, + "ABI" => Jason.encode!(contract.abi), + "ContractName" => contract.name, + "CompilerVersion" => contract.compiler_version, + "OptimizationUsed" => "true", + "OptimizationRuns" => 200, + "EVMVersion" => "default", + "ExternalLibraries" => [ + %{"name" => "Test", "address_hash" => "0xb18aed9518d735482badb4e8b7fd8d2ba425ce95"}, + %{"name" => "Test2", "address_hash" => "0x283539e1b1daf24cdd58a3e934d55062ea663c3f"} + ], + "FileName" => "", + "IsProxy" => "false" + } + ] + + get_implementation() + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getsourcecode_schema(), response) + end + end + + describe "verify" do + test "verify known on sourcify repo contract", %{conn: conn} do + response = verify(conn) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"]["ABI"] == + "[{\"inputs\":[],\"name\":\"retrieve\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_number\",\"type\":\"uint256\"}],\"name\":\"store\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + + assert response["result"]["CompilerVersion"] == "v0.7.6+commit.7338295f" + assert response["result"]["ContractName"] == "Storage" + assert response["result"]["EVMVersion"] == "istanbul" + assert response["result"]["OptimizationUsed"] == "false" + end + + test "verify already verified contract", %{conn: conn} do + _response = verify(conn) + + params = %{ + "module" => "contract", + "action" => "verify_via_sourcify", + "addressHash" => "0x18d89C12e9463Be6343c35C9990361bA4C42AfC2" + } + + response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] == "Smart-contract already verified." + assert response["status"] == "0" + assert response["result"] == nil + end + + defp verify(conn) do + smart_contract_bytecode = + "0x6080604052348015600f57600080fd5b506004361060325760003560e01c80632e64cec11460375780636057361d146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506087565b005b60008054905090565b806000819055505056fea26469706673582212205afbc4864a2486ec80f10e5eceeaac30e88c9b3dfcd1bfadd6cdf6e6cb6e1fd364736f6c63430007060033" + + _created_contract_address = + insert( + :address, + hash: "0x18d89C12e9463Be6343c35C9990361bA4C42AfC2", + contract_code: smart_contract_bytecode + ) + + params = %{ + "module" => "contract", + "action" => "verify_via_sourcify", + "addressHash" => "0x18d89C12e9463Be6343c35C9990361bA4C42AfC2" + } + + get_implementation() + + conn + |> get("/api", params) + |> json_response(200) + end + + # flaky test + # test "with an address that doesn't exist", %{conn: conn} do + # contract_code_info = Factory.contract_code_info() + + # contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + # insert(:transaction, created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) + + # params = %{ + # "module" => "contract", + # "action" => "verify", + # "addressHash" => to_string(contract_address.hash), + # "name" => contract_code_info.name, + # "compilerVersion" => contract_code_info.version, + # "optimization" => contract_code_info.optimized, + # "contractSourceCode" => contract_code_info.source_code + # } + + # response = + # conn + # |> get("/api", params) + # |> json_response(200) + + # verified_contract = Chain.address_hash_to_smart_contract(contract_address.hash) + + # expected_result = %{ + # "Address" => to_string(contract_address.hash), + # "SourceCode" => + # "/**\n* Submitted for verification at blockscout.com on #{verified_contract.inserted_at}\n*/\n" <> + # contract_code_info.source_code, + # "ABI" => Jason.encode!(contract_code_info.abi), + # "ContractName" => contract_code_info.name, + # "CompilerVersion" => contract_code_info.version, + # "OptimizationUsed" => "false", + # "EVMVersion" => nil + # } + + # assert response["status"] == "1" + # assert response["result"] == expected_result + # assert response["message"] == "OK" + # assert :ok = ExJsonSchema.Validator.validate(verify_schema(), response) + # end + + # flaky test + # test "with external libraries", %{conn: conn} do + # contract_data = + # "#{File.cwd!()}/test/support/fixture/smart_contract/contract_with_lib.json" + # |> File.read!() + # |> Jason.decode!() + # |> List.first() + + # %{ + # "compiler_version" => compiler_version, + # "external_libraries" => external_libraries, + # "name" => name, + # "optimize" => optimize, + # "contract" => contract_source_code, + # "expected_bytecode" => expected_bytecode, + # "tx_input" => tx_input + # } = contract_data + + # contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) + # insert(:transaction, created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input) + + # params = %{ + # "module" => "contract", + # "action" => "verify", + # "addressHash" => to_string(contract_address.hash), + # "name" => name, + # "compilerVersion" => compiler_version, + # "optimization" => optimize, + # "contractSourceCode" => contract_source_code + # } + + # params_with_external_libraries = + # external_libraries + # |> Enum.with_index() + # |> Enum.reduce(params, fn {{name, address}, index}, acc -> + # name_key = "library#{index + 1}Name" + # address_key = "library#{index + 1}Address" + + # acc + # |> Map.put(name_key, name) + # |> Map.put(address_key, address) + # end) + + # response = + # conn + # |> get("/api", params_with_external_libraries) + # |> json_response(200) + + # assert response["status"] == "1" + # assert response["message"] == "OK" + + # result = response["result"] + + # verified_contract = Chain.address_hash_to_smart_contract(contract_address.hash) + + # assert result["Address"] == to_string(contract_address.hash) + + # assert result["SourceCode"] == + # "/**\n* Submitted for verification at blockscout.com on #{verified_contract.inserted_at}\n*/\n" <> + # contract_source_code + + # assert result["ContractName"] == name + # assert result["DecompiledSourceCode"] == nil + # assert result["DecompilerVersion"] == nil + # assert result["OptimizationUsed"] == "true" + # assert :ok = ExJsonSchema.Validator.validate(verify_schema(), response) + # end + end + + defp listcontracts_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "Address" => %{"type" => "string"}, + "ABI" => %{"type" => "string"}, + "ContractName" => %{"type" => "string"}, + "CompilerVersion" => %{"type" => "string"}, + "OptimizationUsed" => %{"type" => "string"} + } + } + }) + end + + defp getabi_schema do + resolve_schema(%{ + "type" => ["string", "null"] + }) + end + + defp getsourcecode_schema do + resolve_schema(%{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "Address" => %{"type" => "string"}, + "SourceCode" => %{"type" => "string"}, + "ABI" => %{"type" => "string"}, + "ContractName" => %{"type" => "string"}, + "CompilerVersion" => %{"type" => "string"}, + "OptimizationUsed" => %{"type" => "string"}, + "DecompiledSourceCode" => %{"type" => "string"}, + "DecompilerVersion" => %{"type" => "string"} + } + } + }) + end + + # defp verify_schema do + # resolve_schema(%{ + # "type" => "object", + # "properties" => %{ + # "Address" => %{"type" => "string"}, + # "SourceCode" => %{"type" => "string"}, + # "ABI" => %{"type" => "string"}, + # "ContractName" => %{"type" => "string"}, + # "CompilerVersion" => %{"type" => "string"}, + # "DecompiledSourceCode" => %{"type" => "string"}, + # "DecompilerVersion" => %{"type" => "string"}, + # "OptimizationUsed" => %{"type" => "string"} + # } + # }) + # end + + defp resolve_schema(result) do + %{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"} + } + } + |> put_in(["properties", "result"], result) + |> ExJsonSchema.Schema.resolve() + end + + def get_implementation do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs new file mode 100644 index 0000000..4dee425 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/eth_controller_test.exs @@ -0,0 +1,540 @@ +defmodule BlockScoutWeb.API.RPC.EthControllerTest do + use BlockScoutWeb.ConnCase, async: false + + alias Explorer.Counters.{AddressesCounter, AverageBlockTime} + alias Explorer.Repo + alias Indexer.Fetcher.CoinBalanceOnDemand + + setup do + mocked_json_rpc_named_arguments = [ + transport: EthereumJSONRPC.Mox, + transport_options: [] + ] + + start_supervised!({Task.Supervisor, name: Indexer.TaskSupervisor}) + start_supervised!(AverageBlockTime) + start_supervised!({CoinBalanceOnDemand, [mocked_json_rpc_named_arguments, [name: CoinBalanceOnDemand]]}) + start_supervised!(AddressesCounter) + + Application.put_env(:explorer, AverageBlockTime, enabled: true) + + on_exit(fn -> + Application.put_env(:explorer, AverageBlockTime, enabled: false) + end) + + :ok + end + + defp params(api_params, params), do: Map.put(api_params, "params", params) + + describe "eth_get_logs" do + setup do + %{ + api_params: %{ + "method" => "eth_getLogs", + "jsonrpc" => "2.0", + "id" => 0 + } + } + end + + test "with an invalid address", %{conn: conn, api_params: api_params} do + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [%{"address" => "badhash"}])) + |> json_response(200) + + assert %{"error" => "invalid address"} = response + end + + test "address with no logs", %{conn: conn, api_params: api_params} do + insert(:block) + address = insert(:address) + + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [%{"address" => to_string(address.hash)}])) + |> json_response(200) + + assert %{"result" => []} = response + end + + test "address but no logs and no toBlock provided", %{conn: conn, api_params: api_params} do + address = insert(:address) + + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [%{"address" => to_string(address.hash)}])) + |> json_response(200) + + assert %{"result" => []} = response + end + + test "with a matching address", %{conn: conn, api_params: api_params} do + address = insert(:address) + + block = insert(:block, number: 0) + + transaction = insert(:transaction, from_address: address) |> with_block(block) + insert(:log, block: block, address: address, transaction: transaction, data: "0x010101") + + params = params(api_params, [%{"address" => to_string(address.hash)}]) + + assert response = + conn + |> post("/api/eth-rpc", params) + |> json_response(200) + + assert %{"result" => [%{"data" => "0x010101"}]} = response + end + + test "with a matching address and matching topic", %{conn: conn, api_params: api_params} do + address = insert(:address) + + block = insert(:block, number: 0) + + transaction = insert(:transaction, from_address: address) |> with_block(block) + insert(:log, block: block, address: address, transaction: transaction, data: "0x010101", first_topic: "0x01") + + params = params(api_params, [%{"address" => to_string(address.hash), "topics" => ["0x01"]}]) + + assert response = + conn + |> post("/api/eth-rpc", params) + |> json_response(200) + + assert %{"result" => [%{"data" => "0x010101"}]} = response + end + + test "with a matching address and multiple topic matches", %{conn: conn, api_params: api_params} do + address = insert(:address) + + block = insert(:block, number: 0) + + transaction = insert(:transaction, from_address: address) |> with_block(block) + insert(:log, address: address, block: block, transaction: transaction, data: "0x010101", first_topic: "0x01") + insert(:log, address: address, block: block, transaction: transaction, data: "0x020202", first_topic: "0x00") + + params = params(api_params, [%{"address" => to_string(address.hash), "topics" => [["0x01", "0x00"]]}]) + + assert response = + conn + |> post("/api/eth-rpc", params) + |> json_response(200) + + assert [%{"data" => "0x010101"}, %{"data" => "0x020202"}] = Enum.sort_by(response["result"], &Map.get(&1, "data")) + end + + test "paginates logs", %{conn: conn, api_params: api_params} do + contract_address = insert(:contract_address) + block = insert(:block) + + transaction = + :transaction + |> insert(to_address: contract_address) + |> with_block(block) + + inserted_records = + insert_list(2000, :log, block: block, address: contract_address, transaction: transaction, first_topic: "0x01") + + params = params(api_params, [%{"address" => to_string(contract_address), "topics" => [["0x01"]]}]) + + assert response = + conn + |> post("/api/eth-rpc", params) + |> json_response(200) + + assert Enum.count(response["result"]) == 1000 + + {last_log_index, ""} = Integer.parse(List.last(response["result"])["logIndex"], 16) + + next_page_params = %{ + "blockNumber" => Integer.to_string(transaction.block_number, 16), + "transactionIndex" => transaction.index, + "logIndex" => Integer.to_string(last_log_index, 16) + } + + new_params = + params(api_params, [ + %{"paging_options" => next_page_params, "address" => to_string(contract_address), "topics" => [["0x01"]]} + ]) + + assert new_response = + conn + |> post("/api/eth-rpc", new_params) + |> json_response(200) + + assert Enum.count(response["result"]) == 1000 + + all_found_logs = response["result"] ++ new_response["result"] + + assert Enum.all?(inserted_records, fn record -> + Enum.any?(all_found_logs, fn found_log -> + {index, ""} = Integer.parse(found_log["logIndex"], 16) + + record.index == index + end) + end) + end + + test "with a matching address and multiple topic matches in different positions", %{ + conn: conn, + api_params: api_params + } do + address = insert(:address) + + block = insert(:block, number: 0) + + transaction = insert(:transaction, from_address: address) |> with_block(block) + + insert(:log, + address: address, + transaction: transaction, + data: "0x010101", + first_topic: "0x01", + second_topic: "0x02", + block: block + ) + + insert(:log, block: block, address: address, transaction: transaction, data: "0x020202", first_topic: "0x01") + + params = params(api_params, [%{"address" => to_string(address.hash), "topics" => ["0x01", "0x02"]}]) + + assert response = + conn + |> post("/api/eth-rpc", params) + |> json_response(200) + + assert [%{"data" => "0x010101"}] = response["result"] + end + + test "with a matching address and multiple topic matches in different positions and multiple matches in the second position", + %{conn: conn, api_params: api_params} do + address = insert(:address) + + block = insert(:block, number: 0) + + transaction = insert(:transaction, from_address: address) |> with_block(block) + + insert(:log, + address: address, + transaction: transaction, + data: "0x010101", + first_topic: "0x01", + second_topic: "0x02", + block: block + ) + + insert(:log, + address: address, + transaction: transaction, + data: "0x020202", + first_topic: "0x01", + second_topic: "0x03", + block: block + ) + + params = params(api_params, [%{"address" => to_string(address.hash), "topics" => ["0x01", ["0x02", "0x03"]]}]) + + assert response = + conn + |> post("/api/eth-rpc", params) + |> json_response(200) + + assert [%{"data" => "0x010101"}, %{"data" => "0x020202"}] = Enum.sort_by(response["result"], &Map.get(&1, "data")) + end + + test "with a block range filter", + %{conn: conn, api_params: api_params} do + address = insert(:address) + + block1 = insert(:block, number: 0) + block2 = insert(:block, number: 1) + block3 = insert(:block, number: 2) + block4 = insert(:block, number: 3) + + transaction1 = insert(:transaction, from_address: address) |> with_block(block1) + transaction2 = insert(:transaction, from_address: address) |> with_block(block2) + transaction3 = insert(:transaction, from_address: address) |> with_block(block3) + transaction4 = insert(:transaction, from_address: address) |> with_block(block4) + + insert(:log, address: address, transaction: transaction1, data: "0x010101") + + insert(:log, address: address, transaction: transaction2, data: "0x020202") + + insert(:log, address: address, transaction: transaction3, data: "0x030303") + + insert(:log, address: address, transaction: transaction4, data: "0x040404") + + params = params(api_params, [%{"address" => to_string(address.hash), "fromBlock" => 1, "toBlock" => 2}]) + + assert response = + conn + |> post("/api/eth-rpc", params) + |> json_response(200) + + assert [%{"data" => "0x020202"}, %{"data" => "0x030303"}] = Enum.sort_by(response["result"], &Map.get(&1, "data")) + end + + test "with a block hash filter", + %{conn: conn, api_params: api_params} do + address = insert(:address) + + block1 = insert(:block, number: 0) + block2 = insert(:block, number: 1) + block3 = insert(:block, number: 2) + + transaction1 = insert(:transaction, from_address: address) |> with_block(block1) + transaction2 = insert(:transaction, from_address: address) |> with_block(block2) + transaction3 = insert(:transaction, from_address: address) |> with_block(block3) + + insert(:log, address: address, transaction: transaction1, data: "0x010101") + + insert(:log, address: address, transaction: transaction2, data: "0x020202") + + insert(:log, address: address, transaction: transaction3, data: "0x030303") + + params = params(api_params, [%{"address" => to_string(address.hash), "blockHash" => to_string(block2.hash)}]) + + assert response = + conn + |> post("/api/eth-rpc", params) + |> json_response(200) + + assert [%{"data" => "0x020202"}] = response["result"] + end + + test "with an earliest block filter", + %{conn: conn, api_params: api_params} do + address = insert(:address) + + block1 = insert(:block, number: 0) + block2 = insert(:block, number: 1) + block3 = insert(:block, number: 2) + + transaction1 = insert(:transaction, from_address: address) |> with_block(block1) + transaction2 = insert(:transaction, from_address: address) |> with_block(block2) + transaction3 = insert(:transaction, from_address: address) |> with_block(block3) + + insert(:log, address: address, transaction: transaction1, data: "0x010101") + + insert(:log, address: address, transaction: transaction2, data: "0x020202") + + insert(:log, address: address, transaction: transaction3, data: "0x030303") + + params = + params(api_params, [%{"address" => to_string(address.hash), "fromBlock" => "earliest", "toBlock" => "earliest"}]) + + assert response = + conn + |> post("/api/eth-rpc", params) + |> json_response(200) + + assert [%{"data" => "0x010101"}] = response["result"] + end + + test "with a pending block filter", + %{conn: conn, api_params: api_params} do + address = insert(:address) + + block1 = insert(:block, number: 0) + block2 = insert(:block, number: 1) + block3 = insert(:block, number: 2) + + transaction1 = insert(:transaction, from_address: address) |> with_block(block1) + transaction2 = insert(:transaction, from_address: address) |> with_block(block2) + transaction3 = insert(:transaction, from_address: address) |> with_block(block3) + + insert(:log, block: block1, address: address, transaction: transaction1, data: "0x010101") + + insert(:log, block: block2, address: address, transaction: transaction2, data: "0x020202") + + insert(:log, block: block3, address: address, transaction: transaction3, data: "0x030303") + + changeset = Ecto.Changeset.change(block3, %{consensus: false}) + + Repo.update!(changeset) + + params = + params(api_params, [%{"address" => to_string(address.hash), "fromBlock" => "pending", "toBlock" => "pending"}]) + + assert response = + conn + |> post("/api/eth-rpc", params) + |> json_response(200) + + assert [%{"data" => "0x030303"}] = response["result"] + end + end + + describe "eth_get_balance" do + setup do + %{ + api_params: %{ + "method" => "eth_getBalance", + "jsonrpc" => "2.0", + "id" => 0 + } + } + end + + test "with an invalid address", %{conn: conn, api_params: api_params} do + assert response = + conn + |> post("/api/eth-rpc", params(api_params, ["badHash"])) + |> json_response(200) + + assert %{"error" => "Query parameter 'address' is invalid"} = response + end + + test "with a valid address that has no balance", %{conn: conn, api_params: api_params} do + address = insert(:address) + + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [to_string(address.hash)])) + |> json_response(200) + + assert %{"error" => "Balance not found"} = response + end + + test "with a valid address that has a balance", %{conn: conn, api_params: api_params} do + block = insert(:block) + address = insert(:address) + + insert(:fetched_balance, block_number: block.number, address_hash: address.hash, value: 1) + + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [to_string(address.hash)])) + |> json_response(200) + + assert %{"result" => "0x1"} = response + end + + test "with a valid address that has no earliest balance", %{conn: conn, api_params: api_params} do + block = insert(:block, number: 1) + address = insert(:address) + + insert(:fetched_balance, block_number: block.number, address_hash: address.hash, value: 1) + + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [to_string(address.hash), "earliest"])) + |> json_response(200) + + assert response["error"] == "Balance not found" + end + + test "with a valid address that has an earliest balance", %{conn: conn, api_params: api_params} do + block = insert(:block, number: 0) + address = insert(:address) + + insert(:fetched_balance, block_number: block.number, address_hash: address.hash, value: 1) + + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [to_string(address.hash), "earliest"])) + |> json_response(200) + + assert response["result"] == "0x1" + end + + test "with a valid address and no pending balance", %{conn: conn, api_params: api_params} do + block = insert(:block, number: 1, consensus: true) + address = insert(:address) + + insert(:fetched_balance, block_number: block.number, address_hash: address.hash, value: 1) + + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [to_string(address.hash), "pending"])) + |> json_response(200) + + assert response["error"] == "Balance not found" + end + + test "with a valid address and a pending balance", %{conn: conn, api_params: api_params} do + block = insert(:block, number: 1, consensus: false) + address = insert(:address) + + insert(:fetched_balance, block_number: block.number, address_hash: address.hash, value: 1) + + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [to_string(address.hash), "pending"])) + |> json_response(200) + + assert response["result"] == "0x1" + end + + test "with a valid address and a pending balance after a consensus block", %{conn: conn, api_params: api_params} do + insert(:block, number: 1, consensus: true) + block = insert(:block, number: 2, consensus: false) + address = insert(:address) + + insert(:fetched_balance, block_number: block.number, address_hash: address.hash, value: 1) + + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [to_string(address.hash), "pending"])) + |> json_response(200) + + assert response["result"] == "0x1" + end + + test "with a block provided", %{conn: conn, api_params: api_params} do + address = insert(:address) + + insert(:fetched_balance, block_number: 1, address_hash: address.hash, value: 1) + insert(:fetched_balance, block_number: 2, address_hash: address.hash, value: 2) + insert(:fetched_balance, block_number: 3, address_hash: address.hash, value: 3) + + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [to_string(address.hash), "2"])) + |> json_response(200) + + assert response["result"] == "0x2" + end + + test "with a block provided and no balance", %{conn: conn, api_params: api_params} do + address = insert(:address) + + insert(:fetched_balance, block_number: 3, address_hash: address.hash, value: 3) + + assert response = + conn + |> post("/api/eth-rpc", params(api_params, [to_string(address.hash), "2"])) + |> json_response(200) + + assert response["error"] == "Balance not found" + end + + test "with a batch of requests", %{conn: conn} do + address = insert(:address) + + insert(:fetched_balance, block_number: 1, address_hash: address.hash, value: 1) + insert(:fetched_balance, block_number: 2, address_hash: address.hash, value: 2) + insert(:fetched_balance, block_number: 3, address_hash: address.hash, value: 3) + + params = [ + %{"id" => 0, "params" => [to_string(address.hash), "1"], "jsonrpc" => "2.0", "method" => "eth_getBalance"}, + %{"id" => 1, "params" => [to_string(address.hash), "2"], "jsonrpc" => "2.0", "method" => "eth_getBalance"}, + %{"id" => 2, "params" => [to_string(address.hash), "3"], "jsonrpc" => "2.0", "method" => "eth_getBalance"} + ] + + assert response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/eth-rpc", Jason.encode!(params)) + |> json_response(200) + + assert [ + %{"id" => 0, "result" => "0x1"}, + %{"id" => 1, "result" => "0x2"}, + %{"id" => 2, "result" => "0x3"} + ] = response + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs new file mode 100644 index 0000000..ec4337e --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/logs_controller_test.exs @@ -0,0 +1,820 @@ +defmodule BlockScoutWeb.API.RPC.LogsControllerTest do + use BlockScoutWeb.ConnCase + + alias BlockScoutWeb.API.RPC.LogsController + alias Explorer.Chain.{Log, Transaction} + + describe "getLogs" do + test "without fromBlock, toBlock, address, and topic{x}", %{conn: conn} do + params = %{ + "module" => "logs", + "action" => "getLogs" + } + + expected_message = "Required query parameters missing: fromBlock, toBlock, address and/or topic{x}" + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] == expected_message + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "without fromBlock", %{conn: conn} do + params = %{ + "module" => "logs", + "action" => "getLogs", + "toBlock" => "10", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + expected_message = "Required query parameters missing: fromBlock" + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] == expected_message + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "without toBlock", %{conn: conn} do + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "5", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + expected_message = "Required query parameters missing: toBlock" + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] == expected_message + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "without address and topic{x}", %{conn: conn} do + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "5", + "toBlock" => "10" + } + + expected_message = "Required query parameters missing: address and/or topic{x}" + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] == expected_message + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "without topic{x}_{x}_opr", %{conn: conn} do + conditions = %{ + ["topic0", "topic1"] => "topic0_1_opr", + ["topic0", "topic2"] => "topic0_2_opr", + ["topic0", "topic3"] => "topic0_3_opr", + ["topic1", "topic2"] => "topic1_2_opr", + ["topic1", "topic3"] => "topic1_3_opr", + ["topic2", "topic3"] => "topic2_3_opr" + } + + for {[key1, key2], expectation} <- conditions do + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "5", + "toBlock" => "10", + key1 => "some topic", + key2 => "some other topic" + } + + expected_message = "Required query parameters missing: #{expectation}" + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] == expected_message + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + end + + test "without multiple topic{x}_{x}_opr", %{conn: conn} do + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "5", + "toBlock" => "10", + "topic0" => "some topic", + "topic1" => "some other topic", + "topic2" => "some extra topic", + "topic3" => "some different topic" + } + + expected_message = + "Required query parameters missing: " <> + "topic0_1_opr, topic0_2_opr, topic0_3_opr, topic1_2_opr, topic1_3_opr, topic2_3_opr" + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] == expected_message + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "with invalid fromBlock", %{conn: conn} do + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "invalid", + "toBlock" => "10", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid fromBlock format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "with invalid toBlock", %{conn: conn} do + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "5", + "toBlock" => "invalid", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid toBlock format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "with an invalid address hash", %{conn: conn} do + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "5", + "toBlock" => "10", + "address" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid address format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "with invalid topic{x}_{x}_opr", %{conn: conn} do + conditions = %{ + ["topic0", "topic1"] => "topic0_1_opr", + ["topic0", "topic2"] => "topic0_2_opr", + ["topic0", "topic3"] => "topic0_3_opr", + ["topic1", "topic2"] => "topic1_2_opr", + ["topic1", "topic3"] => "topic1_3_opr", + ["topic2", "topic3"] => "topic2_3_opr" + } + + for {[key1, key2], expectation} <- conditions do + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "5", + "toBlock" => "10", + key1 => "some topic", + key2 => "some other topic", + expectation => "invalid" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid #{expectation} format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + end + + test "with an address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "5", + "toBlock" => "10", + "address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == [] + assert response["status"] == "0" + assert response["message"] == "No logs found" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "with a valid contract address", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = + %Transaction{block: block} = + :transaction + |> insert(to_address: contract_address) + |> with_block() + + log = insert(:log, address: contract_address, transaction: transaction) + + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "#{block.number}", + "toBlock" => "#{block.number}", + "address" => "#{contract_address.hash}" + } + + expected_result = [ + %{ + "address" => "#{contract_address.hash}", + "topics" => get_topics(log), + "data" => "#{log.data}", + "blockNumber" => integer_to_hex(transaction.block_number), + "timeStamp" => datetime_to_hex(block.timestamp), + "gasPrice" => decimal_to_hex(transaction.gas_price.value), + "gasUsed" => decimal_to_hex(transaction.gas_used), + "logIndex" => integer_to_hex(log.index), + "transactionHash" => "#{transaction.hash}", + "transactionIndex" => integer_to_hex(transaction.index) + } + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "ignores logs with block below fromBlock", %{conn: conn} do + first_block = insert(:block) + second_block = insert(:block) + + contract_address = insert(:contract_address) + + transaction_block1 = + %Transaction{} = + :transaction + |> insert(to_address: contract_address) + |> with_block(first_block) + + transaction_block2 = + %Transaction{} = + :transaction + |> insert(to_address: contract_address) + |> with_block(second_block) + + insert(:log, address: contract_address, transaction: transaction_block1) + insert(:log, address: contract_address, transaction: transaction_block2) + + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "#{second_block.number}", + "toBlock" => "#{second_block.number}", + "address" => "#{contract_address.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + + [found_log] = response["result"] + + assert found_log["blockNumber"] == integer_to_hex(second_block.number) + assert found_log["transactionHash"] == "#{transaction_block2.hash}" + end + + test "ignores logs with block above toBlock", %{conn: conn} do + first_block = insert(:block) + second_block = insert(:block) + + contract_address = insert(:contract_address) + + transaction_block1 = + %Transaction{} = + :transaction + |> insert(to_address: contract_address) + |> with_block(first_block) + + transaction_block2 = + %Transaction{} = + :transaction + |> insert(to_address: contract_address) + |> with_block(second_block) + + insert(:log, address: contract_address, transaction: transaction_block1) + insert(:log, address: contract_address, transaction: transaction_block2) + + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "#{first_block.number}", + "toBlock" => "#{first_block.number}", + "address" => "#{contract_address.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + + [found_log] = response["result"] + + assert found_log["blockNumber"] == integer_to_hex(first_block.number) + assert found_log["transactionHash"] == "#{transaction_block1.hash}" + end + + test "with a valid topic{x}", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = + %Transaction{block: block} = + :transaction + |> insert() + |> with_block() + + log1_details = [ + address: contract_address, + transaction: transaction, + first_topic: "some topic" + ] + + log2_details = [ + address: contract_address, + transaction: transaction, + first_topic: "some other topic" + ] + + log1 = insert(:log, log1_details) + _log2 = insert(:log, log2_details) + + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "#{block.number}", + "toBlock" => "#{block.number}", + "topic0" => log1.first_topic + } + + expected_result = [ + %{ + "address" => "#{contract_address.hash}", + "topics" => get_topics(log1), + "data" => "#{log1.data}", + "blockNumber" => integer_to_hex(transaction.block_number), + "timeStamp" => datetime_to_hex(block.timestamp), + "gasPrice" => decimal_to_hex(transaction.gas_price.value), + "gasUsed" => decimal_to_hex(transaction.gas_used), + "logIndex" => integer_to_hex(log1.index), + "transactionHash" => "#{transaction.hash}", + "transactionIndex" => integer_to_hex(transaction.index) + } + ] + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "with a topic{x} AND another", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = + %Transaction{block: block} = + :transaction + |> insert() + |> with_block() + + log1_details = [ + address: contract_address, + transaction: transaction, + first_topic: "some topic", + second_topic: "some second topic" + ] + + log2_details = [ + address: contract_address, + transaction: transaction, + first_topic: "some other topic", + second_topic: "some other second topic" + ] + + log1 = insert(:log, log1_details) + _log2 = insert(:log, log2_details) + + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "#{block.number}", + "toBlock" => "#{block.number}", + "topic0" => log1.first_topic, + "topic1" => log1.second_topic, + "topic0_1_opr" => "and" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert [found_log] = response["result"] + assert found_log["logIndex"] == integer_to_hex(log1.index) + assert found_log["topics"] == get_topics(log1) + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "with a topic{x} OR another", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = + %Transaction{block: block} = + :transaction + |> insert() + |> with_block() + + log1_details = [ + address: contract_address, + transaction: transaction, + first_topic: "some topic", + second_topic: "some second topic" + ] + + log2_details = [ + address: contract_address, + transaction: transaction, + first_topic: "some other topic", + second_topic: "some other second topic" + ] + + log1 = insert(:log, log1_details) + log2 = insert(:log, log2_details) + + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "#{block.number}", + "toBlock" => "#{block.number}", + "topic0" => log1.first_topic, + "topic1" => log2.second_topic, + "topic0_1_opr" => "or" + } + + assert %{"result" => result} = + response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(result) == 2 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + + test "with all available 'topic{x}'s and 'topic{x}_{x}_opr's", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = + %Transaction{block: block} = + :transaction + |> insert() + |> with_block() + + log1_details = [ + address: contract_address, + transaction: transaction, + first_topic: "some topic", + second_topic: "some second topic", + third_topic: "some third topic", + fourth_topic: "some fourth topic" + ] + + log2_details = [ + address: contract_address, + transaction: transaction, + first_topic: "some topic", + second_topic: "some second topic", + third_topic: "some third topic", + fourth_topic: "some other fourth topic" + ] + + log1 = insert(:log, log1_details) + log2 = insert(:log, log2_details) + + params = %{ + "module" => "logs", + "action" => "getLogs", + "fromBlock" => "#{block.number}", + "toBlock" => "#{block.number}", + "topic0" => log1.first_topic, + "topic1" => log1.second_topic, + "topic2" => log1.third_topic, + "topic3" => log2.fourth_topic, + "topic0_1_opr" => "and", + "topic0_2_opr" => "and", + "topic0_3_opr" => "or", + "topic1_2_opr" => "and", + "topic1_3_opr" => "or", + "topic2_3_opr" => "or" + } + + assert %{"result" => result} = + response = + conn + |> get("/api", params) + |> json_response(200) + + assert length(result) == 2 + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(getlogs_schema(), response) + end + end + + describe "fetch_required_params/1" do + test "without any required params" do + params = %{} + + {_, {:error, missing_params}} = LogsController.fetch_required_params(params) + + assert missing_params == ["fromBlock", "toBlock", "address and/or topic{x}"] + end + + test "without fromBlock" do + params = %{ + "toBlock" => "5", + "address" => "some address" + } + + {_, {:error, [missing_param]}} = LogsController.fetch_required_params(params) + + assert missing_param == "fromBlock" + end + + test "without toBlock" do + params = %{ + "fromBlock" => "5", + "address" => "some address" + } + + {_, {:error, [missing_param]}} = LogsController.fetch_required_params(params) + + assert missing_param == "toBlock" + end + + test "without fromBlock or toBlock" do + params = %{ + "address" => "some address" + } + + {_, {:error, missing_params}} = LogsController.fetch_required_params(params) + + assert missing_params == ["fromBlock", "toBlock"] + end + + test "without address or topic{x}" do + params = %{ + "toBlock" => "5", + "fromBlock" => "5" + } + + {_, {:error, [missing_param]}} = LogsController.fetch_required_params(params) + + assert missing_param == "address and/or topic{x}" + end + + test "with address" do + params = %{ + "fromBlock" => "5", + "toBlock" => "5", + "address" => "some address" + } + + {_, {:ok, fetched_params}} = LogsController.fetch_required_params(params) + + assert fetched_params == params + end + + test "with topic{x}" do + for topic <- ["topic0", "topic1", "topic2", "topic3"] do + params = %{ + "fromBlock" => "5", + "toBlock" => "5", + topic => "some topic" + } + + {_, {:ok, fetched_params}} = LogsController.fetch_required_params(params) + + assert fetched_params == params + end + end + + test "with address and topic{x}" do + params = %{ + "fromBlock" => "5", + "toBlock" => "5", + "address" => "some address", + "topic0" => "some topic" + } + + {_, {:ok, fetched_params}} = LogsController.fetch_required_params(params) + + assert fetched_params == params + end + end + + describe "to_valid_format/1" do + test "with invalid fromBlock" do + params = %{"fromBlock" => "invalid"} + + assert {_, {:error, "fromBlock"}} = LogsController.to_valid_format(params) + end + + test "with invalid toBlock" do + params = %{ + "fromBlock" => "5", + "toBlock" => "invalid" + } + + assert {_, {:error, "toBlock"}} = LogsController.to_valid_format(params) + end + + test "with invalid address" do + params = %{ + "fromBlock" => "5", + "toBlock" => "10", + "address" => "invalid" + } + + assert {_, {:error, "address"}} = LogsController.to_valid_format(params) + end + + test "address_hash returns as nil when missing" do + params = %{ + "fromBlock" => "5", + "toBlock" => "10" + } + + assert {_, {:ok, validated_params}} = LogsController.to_valid_format(params) + refute validated_params.address_hash + end + + test "fromBlock and toBlock support use of 'latest'" do + params = %{ + "fromBlock" => "latest", + "toBlock" => "latest" + } + + # Without any blocks in the db we want to return {:error, :not_found} + assert {_, {:error, :not_found}} = LogsController.to_valid_format(params) + + # We insert a block, try again, and assert 'latest' points to the latest + # block number. + insert(:block) + {:ok, max_consensus_block_number} = Explorer.Chain.max_consensus_block_number() + + assert {_, {:ok, validated_params}} = LogsController.to_valid_format(params) + assert validated_params.from_block == max_consensus_block_number + assert validated_params.to_block == max_consensus_block_number + end + end + + defp get_topics(%Log{ + first_topic: first_topic, + second_topic: second_topic, + third_topic: third_topic, + fourth_topic: fourth_topic + }) do + [first_topic, second_topic, third_topic, fourth_topic] + end + + defp integer_to_hex(integer), do: "0x" <> String.downcase(Integer.to_string(integer, 16)) + + defp decimal_to_hex(decimal) do + decimal + |> Decimal.to_integer() + |> integer_to_hex() + end + + defp datetime_to_hex(datetime) do + datetime + |> DateTime.to_unix() + |> integer_to_hex() + end + + defp getlogs_schema do + ExJsonSchema.Schema.resolve(%{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"}, + "result" => %{ + "type" => ["array", "null"], + "items" => %{ + "type" => "object", + "properties" => %{ + "address" => %{"type" => "string"}, + "topics" => %{"type" => "array", "items" => %{"type" => ["string", "null"]}}, + "data" => %{"type" => "string"}, + "blockNumber" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "gasPrice" => %{"type" => "string"}, + "gasUsed" => %{"type" => "string"}, + "logIndex" => %{"type" => "string"}, + "transactionHash" => %{"type" => "string"}, + "transactionIndex" => %{"type" => "string"} + } + } + } + } + }) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/rpc_translator_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/rpc_translator_test.exs new file mode 100644 index 0000000..a5b23b2 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/rpc_translator_test.exs @@ -0,0 +1,82 @@ +defmodule BlockScoutWeb.API.RPC.RPCTranslatorTest do + use BlockScoutWeb.ConnCase + + alias BlockScoutWeb.API.RPC.RPCTranslator + alias Plug.Conn + + defmodule TestController do + use BlockScoutWeb, :controller + + def test_action(conn, _) do + json(conn, %{}) + end + end + + setup %{conn: conn} do + conn = Phoenix.Controller.accepts(conn, ["json"]) + {:ok, conn: conn} + end + + test "init/1" do + assert RPCTranslator.init([]) == [] + end + + describe "call" do + test "with a bad module", %{conn: conn} do + conn = %Conn{conn | params: %{"module" => "test", "action" => "test"}, request_path: "/api"} + + result = RPCTranslator.call(conn, %{}) + assert result.halted + assert response = json_response(result, 400) + assert response["message"] =~ "Unknown action" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with a bad action atom", %{conn: conn} do + conn = %Conn{ + conn + | params: %{"module" => "test", "action" => "some_atom_that_should_not_exist"}, + request_path: "/api" + } + + result = RPCTranslator.call(conn, %{"test" => {TestController, []}}) + assert result.halted + assert response = json_response(result, 400) + assert response["message"] =~ "Unknown action" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with an invalid controller action", %{conn: conn} do + conn = %Conn{conn | params: %{"module" => "test", "action" => "index"}, request_path: "/api"} + + result = RPCTranslator.call(conn, %{"test" => {TestController, []}}) + assert result.halted + assert response = json_response(result, 400) + assert response["message"] =~ "Unknown action" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with missing params", %{conn: conn} do + result = RPCTranslator.call(conn, %{"test" => {TestController, []}}) + assert result.halted + assert response = json_response(result, 400) + assert response["message"] =~ "'module' and 'action' are required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with a valid request", %{conn: conn} do + conn = %Conn{conn | params: %{"module" => "test", "action" => "test_action"}, request_path: "/api"} + + result = RPCTranslator.call(conn, %{"test" => {TestController, []}}) + assert json_response(result, 200) == %{} + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs new file mode 100644 index 0000000..e92ba9b --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs @@ -0,0 +1,247 @@ +defmodule BlockScoutWeb.API.RPC.StatsControllerTest do + use BlockScoutWeb.ConnCase + + import Mox + + alias Explorer.ExchangeRates + alias Explorer.ExchangeRates.Token + alias Explorer.ExchangeRates.Source.TestSource + + describe "tokensupply" do + test "with missing contract address", %{conn: conn} do + params = %{ + "module" => "stats", + "action" => "tokensupply" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "contract address is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) + end + + test "with an invalid contract address hash", %{conn: conn} do + params = %{ + "module" => "stats", + "action" => "tokensupply", + "contractaddress" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid contract address format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) + end + + test "with a contract address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "stats", + "action" => "tokensupply", + "contractaddress" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "contract address not found" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) + end + + test "with valid contract address", %{conn: conn} do + token = insert(:token) + + params = %{ + "module" => "stats", + "action" => "tokensupply", + "contractaddress" => to_string(token.contract_address_hash) + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == to_string(token.total_supply) + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(tokensupply_schema(), response) + end + end + + describe "ethsupplyexchange" do + test "returns total supply from exchange", %{conn: conn} do + params = %{ + "module" => "stats", + "action" => "ethsupplyexchange" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == "252460800000000000000000000" + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(ethsupplyexchange_schema(), response) + end + end + + # todo: Temporarily disable this test because of unstable work in CI + # describe "ethsupply" do + # test "returns total supply from DB", %{conn: conn} do + # params = %{ + # "module" => "stats", + # "action" => "ethsupply" + # } + + # assert response = + # conn + # |> get("/api", params) + # |> json_response(200) + + # assert response["result"] == "0" + # assert response["status"] == "1" + # assert response["message"] == "OK" + # assert :ok = ExJsonSchema.Validator.validate(ethsupply_schema(), response) + # end + # end + + # describe "coinsupply" do + # test "returns total supply minus a burnt number from DB in coins denomination", %{conn: conn} do + # params = %{ + # "module" => "stats", + # "action" => "coinsupply" + # } + + # assert response = + # conn + # |> get("/api", params) + # |> json_response(200) + + # assert response == 0.0 + # end + # end + + describe "coinprice" do + setup :set_mox_global + + setup do + # Use TestSource mock for this test set + configuration = Application.get_env(:explorer, Explorer.ExchangeRates) + Application.put_env(:explorer, Explorer.ExchangeRates, source: TestSource) + Application.put_env(:explorer, Explorer.ExchangeRates, table_name: :rates) + Application.put_env(:explorer, Explorer.ExchangeRates, enabled: true) + + ExchangeRates.init([]) + + :ok + + on_exit(fn -> + Application.put_env(:explorer, Explorer.ExchangeRates, configuration) + end) + end + + test "returns the configured coin's price information", %{conn: conn} do + symbol = Application.get_env(:explorer, :coin) + + eth = %Token{ + available_supply: Decimal.new("1000000.0"), + total_supply: Decimal.new("1000000.0"), + btc_value: Decimal.new("1.000"), + id: "test", + last_updated: DateTime.utc_now(), + market_cap_usd: Decimal.new("1000000.0"), + name: "test", + symbol: symbol, + usd_value: Decimal.new("1.0"), + volume_24h_usd: Decimal.new("1000.0") + } + + ExchangeRates.handle_info({nil, {:ok, [eth]}}, %{}) + + params = %{ + "module" => "stats", + "action" => "coinprice" + } + + expected_timestamp = eth.last_updated |> DateTime.to_unix() |> to_string() + + expected_result = %{ + "coin_btc" => to_string(eth.btc_value), + "coin_btc_timestamp" => expected_timestamp, + "coin_usd" => to_string(eth.usd_value), + "coin_usd_timestamp" => expected_timestamp + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + assert :ok = ExJsonSchema.Validator.validate(coinprice_schema(), response) + end + end + + defp tokensupply_schema do + resolve_schema(%{ + "type" => ["string", "null"] + }) + end + + # defp ethsupply_schema do + # resolve_schema(%{ + # "type" => ["string", "null"] + # }) + # end + + defp ethsupplyexchange_schema do + resolve_schema(%{ + "type" => ["string", "null"] + }) + end + + defp coinprice_schema do + resolve_schema(%{ + "type" => "object", + "properties" => %{ + "coin_btc" => %{"type" => "string"}, + "coin_btc_timestamp" => %{"type" => "string"}, + "coin_usd" => %{"type" => "string"}, + "coin_usd_timestamp" => %{"type" => "string"} + } + }) + end + + defp resolve_schema(result) do + %{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"} + } + } + |> put_in(["properties", "result"], result) + |> ExJsonSchema.Schema.resolve() + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/token_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/token_controller_test.exs new file mode 100644 index 0000000..f33dff9 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/token_controller_test.exs @@ -0,0 +1,109 @@ +defmodule BlockScoutWeb.API.RPC.TokenControllerTest do + use BlockScoutWeb.ConnCase + + describe "gettoken" do + test "with missing contract address", %{conn: conn} do + params = %{ + "module" => "token", + "action" => "getToken" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "contract address is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with an invalid contract address hash", %{conn: conn} do + params = %{ + "module" => "token", + "action" => "getToken", + "contractaddress" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid contract address hash" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with a contract address that doesn't exist", %{conn: conn} do + params = %{ + "module" => "token", + "action" => "getToken", + "contractaddress" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "contract address not found" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "response includes all required fields", %{conn: conn} do + token = insert(:token) + + params = %{ + "module" => "token", + "action" => "getToken", + "contractaddress" => to_string(token.contract_address_hash) + } + + expected_result = %{ + "name" => token.name, + "symbol" => token.symbol, + "totalSupply" => to_string(token.total_supply), + "decimals" => to_string(token.decimals), + "type" => token.type, + "cataloged" => token.cataloged, + "contractAddress" => to_string(token.contract_address_hash) + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + end + end + + # defp gettoken_schema do + # ExJsonSchema.Schema.resolve(%{ + # "type" => "object", + # "properties" => %{ + # "message" => %{"type" => "string"}, + # "status" => %{"type" => "string"}, + # "result" => %{ + # "type" => "object", + # "properties" => %{ + # "name" => %{"type" => "string"}, + # "symbol" => %{"type" => "string"}, + # "totalSupply" => %{"type" => "string"}, + # "decimals" => %{"type" => "string"}, + # "type" => %{"type" => "string"}, + # "cataloged" => %{"type" => "string"}, + # "contractAddress" => %{"type" => "string"} + # } + # } + # } + # }) + # end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs new file mode 100644 index 0000000..d46071d --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs @@ -0,0 +1,773 @@ +defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do + use BlockScoutWeb.ConnCase + + import Mox + + @moduletag capture_log: true + + describe "gettxreceiptstatus" do + test "with missing txhash", %{conn: conn} do + params = %{ + "module" => "transaction", + "action" => "gettxreceiptstatus" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = resolve_schema() + assert ExJsonSchema.Validator.valid?(schema, response) + assert response["message"] =~ "txhash is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with an invalid txhash", %{conn: conn} do + params = %{ + "module" => "transaction", + "action" => "gettxreceiptstatus", + "txhash" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = resolve_schema() + assert ExJsonSchema.Validator.valid?(schema, response) + assert response["message"] =~ "Invalid txhash format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with a txhash that doesn't exist", %{conn: conn} do + params = %{ + "module" => "transaction", + "action" => "gettxreceiptstatus", + "txhash" => "0x40eb908387324f2b575b4879cd9d7188f69c8fc9d87c901b9e2daaea4b442170" + } + + schema = + resolve_schema(%{ + "type" => "object", + "properties" => %{ + "status" => %{"type" => "string"} + } + }) + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert ExJsonSchema.Validator.valid?(schema, response) + assert response["result"] == %{"status" => ""} + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with ok status", %{conn: conn} do + block = insert(:block) + + transaction = + :transaction + |> insert() + |> with_block(block, status: :ok) + + params = %{ + "module" => "transaction", + "action" => "gettxreceiptstatus", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == %{"status" => "1"} + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with error status", %{conn: conn} do + block = insert(:block) + + transaction = + :transaction + |> insert() + |> with_block(block, status: :error) + + params = %{ + "module" => "transaction", + "action" => "gettxreceiptstatus", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == %{"status" => "0"} + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with nil status", %{conn: conn} do + transaction = insert(:transaction, status: nil) + + params = %{ + "module" => "transaction", + "action" => "gettxreceiptstatus", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == %{"status" => ""} + assert response["status"] == "1" + assert response["message"] == "OK" + end + end + + describe "getstatus" do + test "with missing txhash", %{conn: conn} do + params = %{ + "module" => "transaction", + "action" => "getstatus" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = resolve_schema() + assert ExJsonSchema.Validator.valid?(schema, response) + assert response["message"] =~ "txhash is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with an invalid txhash", %{conn: conn} do + params = %{ + "module" => "transaction", + "action" => "getstatus", + "txhash" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = resolve_schema() + assert ExJsonSchema.Validator.valid?(schema, response) + assert response["message"] =~ "Invalid txhash format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with a txhash that doesn't exist", %{conn: conn} do + params = %{ + "module" => "transaction", + "action" => "getstatus", + "txhash" => "0x40eb908387324f2b575b4879cd9d7188f69c8fc9d87c901b9e2daaea4b442170" + } + + expected_result = %{ + "isError" => "0", + "errDescription" => "" + } + + schema = + resolve_schema(%{ + "type" => "object", + "properties" => %{ + "isError" => %{"type" => "string"}, + "errDescription" => %{"type" => "string"} + } + }) + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert ExJsonSchema.Validator.valid?(schema, response) + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with ok status", %{conn: conn} do + block = insert(:block) + + transaction = + :transaction + |> insert() + |> with_block(block, status: :ok) + + params = %{ + "module" => "transaction", + "action" => "getstatus", + "txhash" => "#{transaction.hash}" + } + + expected_result = %{ + "isError" => "0", + "errDescription" => "" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with error", %{conn: conn} do + error = "some error" + + transaction_details = [ + status: :error, + error: error + ] + + transaction = + :transaction + |> insert() + |> with_block(transaction_details) + + internal_transaction_details = [ + transaction: transaction, + index: 0, + type: :reward, + error: error, + block_hash: transaction.block_hash, + block_index: 0 + ] + + insert(:internal_transaction, internal_transaction_details) + + params = %{ + "module" => "transaction", + "action" => "getstatus", + "txhash" => "#{transaction.hash}" + } + + expected_result = %{ + "isError" => "1", + "errDescription" => error + } + + response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with failed status but awaiting internal transactions", %{conn: conn} do + transaction_details = [ + status: :error, + error: nil + ] + + transaction = + :transaction + |> insert() + |> with_block(transaction_details) + + params = %{ + "module" => "transaction", + "action" => "getstatus", + "txhash" => "#{transaction.hash}" + } + + expected_result = %{ + "isError" => "1", + "errDescription" => "awaiting internal transactions" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with nil status", %{conn: conn} do + transaction = insert(:transaction, status: nil) + + params = %{ + "module" => "transaction", + "action" => "getstatus", + "txhash" => "#{transaction.hash}" + } + + expected_result = %{ + "isError" => "0", + "errDescription" => "" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + end + end + + describe "gettxinfo" do + test "with missing txhash", %{conn: conn} do + params = %{ + "module" => "transaction", + "action" => "gettxinfo" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + schema = resolve_schema() + assert ExJsonSchema.Validator.valid?(schema, response) + assert response["message"] =~ "txhash is required" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with an invalid txhash", %{conn: conn} do + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "badhash" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Invalid txhash format" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "with a txhash that doesn't exist", %{conn: conn} do + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "0x40eb908387324f2b575b4879cd9d7188f69c8fc9d87c901b9e2daaea4b442170" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["message"] =~ "Transaction not found" + assert response["status"] == "0" + assert Map.has_key?(response, "result") + refute response["result"] + end + + test "paginates logs", %{conn: conn} do + block = insert(:block, hash: "0x30d522bcf2d8e0cabc286e6e40623c475c3bc05d0ec484ea239c103b1ac0ded9", number: 99) + + transaction = + :transaction + |> insert(hash: "0x13b6bb8e06322096dc83e8d7e6332ca19919ea642212cd259c6b20e7523a0599") + |> with_block(block, status: :ok) + + address = insert(:address) + + Enum.each(1..100, fn _ -> + insert(:log, + address: address, + transaction: transaction, + first_topic: "first topic", + second_topic: "second topic", + block: block, + block_number: block.number + ) + end) + + params1 = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + schema = + resolve_schema(%{ + "type" => "object", + "properties" => %{ + "next_page_params" => %{ + "type" => ["object", "null"], + "properties" => %{ + "action" => %{"type" => "string"}, + "index" => %{"type" => "number"}, + "module" => %{"type" => "string"}, + "txhash" => %{"type" => "string"} + } + }, + "logs" => %{ + "type" => "array", + "items" => %{"type" => "object"} + } + } + }) + + assert response1 = + conn + |> get("/api", params1) + |> json_response(200) + + assert ExJsonSchema.Validator.valid?(schema, response1) + assert response1["status"] == "1" + assert response1["message"] == "OK" + + assert %{ + "action" => "gettxinfo", + "index" => _, + "module" => "transaction", + "txhash" => _ + } = response1["result"]["next_page_params"] + + params2 = response1["result"]["next_page_params"] + + assert response2 = + conn + |> get("/api", params2) + |> json_response(200) + + assert ExJsonSchema.Validator.valid?(schema, response2) + assert response2["status"] == "1" + assert response2["message"] == "OK" + assert is_nil(response2["result"]["next_page_params"]) + assert response1["result"]["logs"] != response2["result"]["logs"] + end + + test "with a txhash with ok status", %{conn: conn} do + block = insert(:block) + + transaction = + :transaction + |> insert() + |> with_block(block, status: :ok) + + address = insert(:address) + + log = + insert(:log, + address: address, + transaction: transaction, + first_topic: "first topic", + second_topic: "second topic", + block: block, + block_number: block.number + ) + + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + expected_result = %{ + "hash" => "#{transaction.hash}", + "timeStamp" => "#{DateTime.to_unix(transaction.block.timestamp)}", + "blockNumber" => "#{transaction.block_number}", + "confirmations" => "0", + "success" => true, + "from" => "#{transaction.from_address_hash}", + "to" => "#{transaction.to_address_hash}", + "value" => "#{transaction.value.value}", + "input" => "#{transaction.input}", + "gasLimit" => "#{transaction.gas}", + "gasUsed" => "#{transaction.gas_used}", + "gasPrice" => "#{transaction.gas_price.value}", + "logs" => [ + %{ + "address" => "#{address.hash}", + "data" => "#{log.data}", + "topics" => ["first topic", "second topic", nil, nil], + "index" => "#{log.index}" + } + ], + "next_page_params" => nil, + "revertReason" => "" + } + + schema = + resolve_schema(%{ + "type" => "object", + "properties" => %{ + "hash" => %{"type" => "string"}, + "timeStamp" => %{"type" => "string"}, + "blockNumber" => %{"type" => "string"}, + "confirmations" => %{"type" => "string"}, + "success" => %{"type" => "boolean"}, + "from" => %{"type" => "string"}, + "to" => %{"type" => "string"}, + "value" => %{"type" => "string"}, + "input" => %{"type" => "string"}, + "gasLimit" => %{"type" => "string"}, + "gasUsed" => %{"type" => "string"}, + "gasPrice" => %{"type" => "string"}, + "logs" => %{ + "type" => "array", + "items" => %{ + "type" => "object", + "properties" => %{ + "address" => %{"type" => "string"}, + "data" => %{"type" => "string"}, + "topics" => %{ + "type" => "array", + "items" => %{"type" => ["string", "null"]} + }, + "index" => %{"type" => "string"} + } + } + }, + "next_page_params" => %{ + "type" => ["object", "null"], + "properties" => %{ + "action" => %{"type" => "string"}, + "index" => %{"type" => "number"}, + "module" => %{"type" => "string"}, + "txhash" => %{"type" => "string"} + } + } + } + }) + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert ExJsonSchema.Validator.valid?(schema, response) + assert response["result"] == expected_result + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with revert reason from DB", %{conn: conn} do + block = insert(:block, number: 100) + + transaction = + :transaction + |> insert(revert_reason: "No credit of that type") + |> with_block(block) + + insert(:address) + + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"]["revertReason"] == "No credit of that type" + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with empty revert reason from DB", %{conn: conn} do + block = insert(:block, number: 100) + + transaction = + :transaction + |> insert(revert_reason: "") + |> with_block(block) + + insert(:address) + + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"]["revertReason"] == "" + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with revert reason from the archive node", %{conn: conn} do + block = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") + + transaction = + :transaction + |> insert( + error: "Reverted", + status: :error, + block_hash: block.hash, + block_number: block.number, + cumulative_gas_used: 884_322, + gas_used: 106_025, + index: 0, + hash: "0xac2a7dab94d965893199e7ee01649e2d66f0787a4c558b3118c09e80d4df8269" + ) + + insert(:address) + + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn _json, [] -> + {:error, %{code: -32015, message: "VM execution error.", data: "revert: No credit of that type"}} + end + ) + + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"]["revertReason"] == "No credit of that type" + assert response["status"] == "1" + assert response["message"] == "OK" + end + end + + test "with a txhash with empty revert reason from the archive node", %{conn: conn} do + block = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") + + transaction = + :transaction + |> insert( + error: "Reverted", + status: :error, + block_hash: block.hash, + block_number: block.number, + cumulative_gas_used: 884_322, + gas_used: 106_025, + index: 0, + hash: "0xac2a7dab94d965893199e7ee01649e2d66f0787a4c558b3118c09e80d4df8269" + ) + + insert(:address) + + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn _json, [] -> + {:error, %{code: -32015, message: "VM execution error.", data: ""}} + end + ) + + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"]["revertReason"] == "" + assert response["status"] == "1" + assert response["message"] == "OK" + end + + test "with a txhash with empty revert reason from DB if eth_call doesn't return an error", %{conn: conn} do + block = insert(:block, number: 100, hash: "0x3e51328bccedee581e8ba35190216a61a5d67fd91ca528f3553142c0c7d18391") + + transaction = + :transaction + |> insert( + error: "Reverted", + status: :error, + block_hash: block.hash, + block_number: block.number, + cumulative_gas_used: 884_322, + gas_used: 106_025, + index: 0, + hash: "0xac2a7dab94d965893199e7ee01649e2d66f0787a4c558b3118c09e80d4df8269" + ) + + insert(:address) + + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn _json, [] -> + {:ok} + end + ) + + params = %{ + "module" => "transaction", + "action" => "gettxinfo", + "txhash" => "#{transaction.hash}" + } + + assert response = + conn + |> get("/api", params) + |> json_response(200) + + assert response["result"]["revertReason"] == "" + assert response["status"] == "1" + assert response["message"] == "OK" + end + + defp resolve_schema(result \\ %{}) do + %{ + "type" => "object", + "properties" => %{ + "message" => %{"type" => "string"}, + "status" => %{"type" => "string"} + } + } + |> put_in(["properties", "result"], result) + |> ExJsonSchema.Schema.resolve() + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller_test.exs new file mode 100644 index 0000000..8f511da --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/decompiled_smart_contract_controller_test.exs @@ -0,0 +1,121 @@ +defmodule BlockScoutWeb.API.V1.DecompiledControllerTest do + use BlockScoutWeb.ConnCase + + alias Explorer.Repo + alias Explorer.Chain.{Address, DecompiledSmartContract} + + import Ecto.Query, + only: [from: 2] + + @secret "secret" + + describe "when used authorized" do + setup %{conn: conn} = context do + Application.put_env(:block_scout_web, :decompiled_smart_contract_token, @secret) + + auth_conn = conn |> put_req_header("auth_token", @secret) + + {:ok, Map.put(context, :conn, auth_conn)} + end + + test "returns unprocessable_entity status when params are invalid", %{conn: conn} do + request = post(conn, api_v1_decompiled_smart_contract_path(conn, :create)) + + assert request.status == 422 + assert request.resp_body == "{\"error\":\"address_hash is invalid\"}" + end + + test "returns unprocessable_entity when code is empty", %{conn: conn} do + decompiler_version = "test_decompiler" + address = insert(:address) + + params = %{ + "address_hash" => to_string(address.hash), + "decompiler_version" => decompiler_version + } + + request = post(conn, api_v1_decompiled_smart_contract_path(conn, :create), params) + + assert request.status == 422 + assert request.resp_body == "{\"decompiled_source_code\":\"can't be blank\"}" + end + + test "can not update code for the same decompiler version", %{conn: conn} do + address_hash = to_string(insert(:address, hash: "0x0000000000000000000000000000000000000001").hash) + decompiler_version = "test_decompiler" + decompiled_source_code = "hello world" + + insert(:decompiled_smart_contract, + address_hash: address_hash, + decompiler_version: decompiler_version, + decompiled_source_code: decompiled_source_code + ) + + params = %{ + "address_hash" => address_hash, + "decompiler_version" => decompiler_version, + "decompiled_source_code" => decompiled_source_code + } + + request = post(conn, api_v1_decompiled_smart_contract_path(conn, :create), params) + + assert request.status == 422 + + assert request.resp_body == "{\"error\":\"decompiled code already exists for the decompiler version\"}" + end + + test "creates decompiled smart contract", %{conn: conn} do + address_hash = to_string(insert(:address, hash: "0x0000000000000000000000000000000000000001").hash) + decompiler_version = "test_decompiler" + decompiled_source_code = "hello world" + + params = %{ + "address_hash" => address_hash, + "decompiler_version" => decompiler_version, + "decompiled_source_code" => decompiled_source_code + } + + request = post(conn, api_v1_decompiled_smart_contract_path(conn, :create), params) + + assert request.status == 201 + + assert request.resp_body == + "{\"address_hash\":\"0x0000000000000000000000000000000000000001\",\"decompiler_version\":\"test_decompiler\",\"decompiled_source_code\":\"hello world\"}" + + decompiled_smart_contract = Repo.one!(from(d in DecompiledSmartContract, where: d.address_hash == ^address_hash)) + assert to_string(decompiled_smart_contract.address_hash) == address_hash + assert decompiled_smart_contract.decompiler_version == decompiler_version + assert decompiled_smart_contract.decompiled_source_code == decompiled_source_code + end + + test "updates the address to be decompiled", %{conn: conn} do + address_hash = to_string(insert(:address, hash: "0x0000000000000000000000000000000000000001").hash) + decompiler_version = "test_decompiler" + decompiled_source_code = "hello world" + + params = %{ + "address_hash" => address_hash, + "decompiler_version" => decompiler_version, + "decompiled_source_code" => decompiled_source_code + } + + request = post(conn, api_v1_decompiled_smart_contract_path(conn, :create), params) + + assert request.status == 201 + + assert Repo.get!(Address, address_hash).decompiled + end + end + + describe "when user is not authorized" do + test "returns forbedden", %{conn: conn} do + request = post(conn, api_v1_decompiled_smart_contract_path(conn, :create)) + + assert request.status == 403 + end + end + + defp api_v1_decompiled_smart_contract_path(conn, action) do + "/api" <> ApiRoutes.api_v1_decompiled_smart_contract_path(conn, action) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v1/health_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/health_controller_test.exs new file mode 100644 index 0000000..54197c5 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/health_controller_test.exs @@ -0,0 +1,94 @@ +defmodule BlockScoutWeb.API.V1.HealthControllerTest do + use BlockScoutWeb.ConnCase + + alias Explorer.{Chain, PagingOptions} + + setup do + Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.Blocks.child_id()) + Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Blocks.child_id()) + + :ok + end + + describe "GET last_block_status/0" do + test "returns error when there are no blocks in db", %{conn: conn} do + request = get(conn, api_v1_health_path(conn, :health)) + + assert request.status == 500 + + assert request.resp_body == + "{\"error_code\":5002,\"error_description\":\"There are no blocks in the DB\",\"error_title\":\"no blocks in db\",\"healthy\":false}" + end + + test "returns error when last block is stale", %{conn: conn} do + insert(:block, consensus: true, timestamp: Timex.shift(DateTime.utc_now(), hours: -50)) + + request = get(conn, api_v1_health_path(conn, :health)) + + assert request.status == 500 + + assert %{ + "healthy" => false, + "error_code" => 5001, + "error_title" => "blocks fetching is stuck", + "error_description" => + "There are no new blocks in the DB for the last 5 mins. Check the healthiness of Ethereum archive node or the Blockscout DB instance", + "data" => %{ + "latest_block_number" => _, + "latest_block_inserted_at" => _ + } + } = Poison.decode!(request.resp_body) + end + + test "returns ok when last block is not stale", %{conn: conn} do + block1 = insert(:block, consensus: true, timestamp: DateTime.utc_now(), number: 2) + insert(:block, consensus: true, timestamp: DateTime.utc_now(), number: 1) + + request = get(conn, api_v1_health_path(conn, :health)) + + assert request.status == 200 + + result = Poison.decode!(request.resp_body) + + assert result["healthy"] == true + + assert %{ + "latest_block_number" => to_string(block1.number), + "latest_block_inserted_at" => to_string(block1.timestamp), + "cache_latest_block_number" => to_string(block1.number), + "cache_latest_block_inserted_at" => to_string(block1.timestamp) + } == result["data"] + end + end + + test "return error when cache is stale", %{conn: conn} do + stale_block = insert(:block, consensus: true, timestamp: Timex.shift(DateTime.utc_now(), hours: -50), number: 3) + state_block_hash = stale_block.hash + + assert [%{hash: ^state_block_hash}] = Chain.list_blocks(paging_options: %PagingOptions{page_size: 1}) + + insert(:block, consensus: true, timestamp: DateTime.utc_now(), number: 1) + + assert [%{hash: ^state_block_hash}] = Chain.list_blocks(paging_options: %PagingOptions{page_size: 1}) + + request = get(conn, api_v1_health_path(conn, :health)) + + assert request.status == 500 + + assert %{ + "healthy" => false, + "error_code" => 5001, + "error_title" => "blocks fetching is stuck", + "error_description" => + "There are no new blocks in the DB for the last 5 mins. Check the healthiness of Ethereum archive node or the Blockscout DB instance", + "data" => %{ + "latest_block_number" => _, + "latest_block_inserted_at" => _ + } + } = Poison.decode!(request.resp_body) + end + + defp api_v1_health_path(conn, action) do + "/api" <> ApiRoutes.api_v1_health_path(conn, action) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v1/supply_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/supply_controller_test.exs new file mode 100644 index 0000000..b521838 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/supply_controller_test.exs @@ -0,0 +1,17 @@ +defmodule BlockScoutWeb.API.V1.SupplyControllerTest do + use BlockScoutWeb.ConnCase + + alias Explorer.Chain + + test "supply", %{conn: conn} do + request = get(conn, api_v1_supply_path(conn, :supply)) + assert response = json_response(request, 200) + + assert response["total_supply"] == Chain.total_supply() + assert response["circulating_supply"] == Chain.circulating_supply() + end + + def api_v1_supply_path(conn, action) do + "/api" <> ApiRoutes.api_v1_supply_path(conn, action) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs new file mode 100644 index 0000000..5726a85 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v1/verified_smart_contract_controller_test.exs @@ -0,0 +1,82 @@ +defmodule BlockScoutWeb.API.V1.VerifiedControllerTest do + use BlockScoutWeb.ConnCase + + # alias Explorer.Factory + + # alias Explorer.Chain.DecompiledSmartContract + + # import Ecto.Query, + # only: [from: 2] + + # flaky test + # test "verifying a standard smart contract", %{conn: conn} do + # contract_code_info = Factory.contract_code_info() + + # contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) + # insert(:transaction, created_contract_address_hash: contract_address.hash, input: contract_code_info.tx_input) + + # params = %{ + # "address_hash" => to_string(contract_address.hash), + # "name" => contract_code_info.name, + # "compiler_version" => contract_code_info.version, + # "optimization" => contract_code_info.optimized, + # "contract_source_code" => contract_code_info.source_code + # } + + # response = post(conn, api_v1_verified_smart_contract_path(conn, :create), params) + + # assert response.status == 201 + # assert Jason.decode!(response.resp_body) == %{"status" => "success"} + # end + + # flaky test + # test "verifying a smart contract with external libraries", %{conn: conn} do + # contract_data = + # "#{File.cwd!()}/test/support/fixture/smart_contract/contract_with_lib.json" + # |> File.read!() + # |> Jason.decode!() + # |> List.first() + + # %{ + # "compiler_version" => compiler_version, + # "external_libraries" => external_libraries, + # "name" => name, + # "optimize" => optimize, + # "contract" => contract_source_code, + # "tx_input" => tx_input, + # "expected_bytecode" => expected_bytecode + # } = contract_data + + # contract_address = insert(:contract_address, contract_code: "0x" <> expected_bytecode) + # insert(:transaction, created_contract_address_hash: contract_address.hash, input: "0x" <> tx_input) + + # params = %{ + # "address_hash" => to_string(contract_address.hash), + # "name" => name, + # "compiler_version" => compiler_version, + # "optimization" => optimize, + # "contract_source_code" => contract_source_code + # } + + # params_with_external_libraries = + # external_libraries + # |> Enum.with_index() + # |> Enum.reduce(params, fn {{name, address}, index}, acc -> + # name_key = "library#{index + 1}_name" + # address_key = "library#{index + 1}_address" + + # acc + # |> Map.put(name_key, name) + # |> Map.put(address_key, address) + # end) + + # response = post(conn, api_v1_verified_smart_contract_path(conn, :create), params_with_external_libraries) + + # assert response.status == 201 + # assert Jason.decode!(response.resp_body) == %{"status" => "success"} + # end + + # defp api_v1_verified_smart_contract_path(conn, action) do + # "/api" <> ApiRoutes.api_v1_verified_smart_contract_path(conn, action) + # end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api_docs_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api_docs_controller_test.exs new file mode 100644 index 0000000..3b2cb99 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/api_docs_controller_test.exs @@ -0,0 +1,18 @@ +defmodule BlockScoutWeb.APIDocsControllerTest do + use BlockScoutWeb.ConnCase + + import BlockScoutWeb.Router.Helpers, only: [api_docs_path: 2] + + describe "GET index/2" do + test "renders documentation tiles for each API module#action", %{conn: conn} do + conn = get(conn, api_docs_path(BlockScoutWeb.Endpoint, :index)) + + documentation = BlockScoutWeb.Etherscan.get_documentation() + + for module <- documentation, action <- module.actions do + assert html_response(conn, 200) =~ action.name + assert html_response(conn, 200) =~ action.description + end + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/block_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/block_controller_test.exs new file mode 100644 index 0000000..b407e0a --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/block_controller_test.exs @@ -0,0 +1,193 @@ +defmodule BlockScoutWeb.BlockControllerTest do + use BlockScoutWeb.ConnCase + alias Explorer.Chain.Block + + setup do + Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.Blocks.child_id()) + Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Blocks.child_id()) + Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.Uncles.child_id()) + Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Uncles.child_id()) + + :ok + end + + describe "GET show/2" do + test "with block redirects to block transactions route", %{conn: conn} do + insert(:block, number: 3) + conn = get(conn, "/blocks/3") + assert redirected_to(conn) =~ "/block/3/transactions" + end + + test "with uncle block redirects to block_hash route", %{conn: conn} do + uncle = insert(:block, consensus: false) + + conn = get(conn, block_path(conn, :show, uncle)) + assert redirected_to(conn) =~ "/block/#{to_string(uncle.hash)}/transactions" + end + end + + describe "GET index/2" do + test "returns all blocks", %{conn: conn} do + 4 + |> insert_list(:block) + |> Stream.map(& &1.number) + |> Enum.reverse() + + conn = get(conn, blocks_path(conn, :index), %{"type" => "JSON"}) + + items = Map.get(json_response(conn, 200), "items") + + assert length(items) == 4 + end + + test "does not include uncles", %{conn: conn} do + blocks = + 4 + |> insert_list(:block) + |> Enum.reverse() + + for index <- 0..3 do + uncle = insert(:block, consensus: false) + insert(:block_second_degree_relation, uncle_hash: uncle.hash, nephew: Enum.at(blocks, index)) + end + + conn = get(conn, blocks_path(conn, :index), %{"type" => "JSON"}) + + items = Map.get(json_response(conn, 200), "items") + + assert length(items) == 4 + end + + test "returns a block with two transactions", %{conn: conn} do + block = insert(:block) + + 2 + |> insert_list(:transaction) + |> with_block(block) + + conn = get(conn, blocks_path(conn, :index), %{"type" => "JSON"}) + + items = Map.get(json_response(conn, 200), "items") + + assert length(items) == 1 + end + + test "returns next page of results based on last seen block", %{conn: conn} do + 50 + |> insert_list(:block) + |> Enum.map(& &1.number) + + block = insert(:block) + + conn = + get(conn, blocks_path(conn, :index), %{ + "type" => "JSON", + "block_number" => Integer.to_string(block.number) + }) + + items = Map.get(json_response(conn, 200), "items") + + assert length(items) == 50 + end + + test "next_page_path exist if not on last page", %{conn: conn} do + %Block{number: number} = + 60 + |> insert_list(:block) + |> Enum.fetch!(10) + + conn = get(conn, blocks_path(conn, :index), %{"type" => "JSON"}) + + expected_path = + blocks_path(conn, :index, %{ + block_number: number, + block_type: "Block", + items_count: "50" + }) + + assert Map.get(json_response(conn, 200), "next_page_path") == expected_path + end + + test "next_page_path is empty if on last page", %{conn: conn} do + insert(:block) + + conn = get(conn, blocks_path(conn, :index), %{"type" => "JSON"}) + + refute conn |> json_response(200) |> Map.get("next_page_path") + end + + test "displays miner primary address name", %{conn: conn} do + miner_name = "POA Miner Pool" + %{address: miner_address} = insert(:address_name, name: miner_name, primary: true) + + insert(:block, miner: miner_address, miner_hash: nil) + + conn = get(conn, blocks_path(conn, :index), %{"type" => "JSON"}) + + items = Map.get(json_response(conn, 200), "items") + + assert List.first(items) =~ miner_name + end + end + + describe "GET reorgs/2" do + test "returns all reorgs", %{conn: conn} do + 4 + |> insert_list(:block, consensus: false) + |> Enum.map(& &1.hash) + + conn = get(conn, reorg_path(conn, :reorg), %{"type" => "JSON"}) + + items = Map.get(json_response(conn, 200), "items") + + assert length(items) == 4 + end + + test "does not include blocks or uncles", %{conn: conn} do + 4 + |> insert_list(:block, consensus: false) + |> Enum.map(& &1.hash) + + insert(:block) + uncle = insert(:block, consensus: false) + insert(:block_second_degree_relation, uncle_hash: uncle.hash) + + conn = get(conn, reorg_path(conn, :reorg), %{"type" => "JSON"}) + + items = Map.get(json_response(conn, 200), "items") + + assert length(items) == 4 + end + end + + describe "GET uncle/2" do + test "returns all uncles", %{conn: conn} do + for _index <- 1..4 do + uncle = insert(:block, consensus: false) + insert(:block_second_degree_relation, uncle_hash: uncle.hash) + end + + conn = get(conn, uncle_path(conn, :uncle), %{"type" => "JSON"}) + + items = Map.get(json_response(conn, 200), "items") + + assert length(items) == 4 + end + + test "does not include blocks or reorgs", %{conn: conn} do + for _index <- 1..4 do + uncle = insert(:block, consensus: false) + insert(:block_second_degree_relation, uncle_hash: uncle.hash) + end + + insert(:block) + insert(:block, consensus: false) + + conn = get(conn, uncle_path(conn, :uncle), %{"type" => "JSON"}) + + items = Map.get(json_response(conn, 200), "items") + + assert length(items) == 4 + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/block_transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/block_transaction_controller_test.exs new file mode 100644 index 0000000..d76a2fc --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/block_transaction_controller_test.exs @@ -0,0 +1,204 @@ +defmodule BlockScoutWeb.BlockTransactionControllerTest do + use BlockScoutWeb.ConnCase + + import BlockScoutWeb.WebRouter.Helpers, only: [block_transaction_path: 3] + + describe "GET index/2" do + test "with invalid block number", %{conn: conn} do + conn = get(conn, block_transaction_path(conn, :index, "unknown")) + + assert html_response(conn, 404) + end + + test "with valid block number below the tip", %{conn: conn} do + insert(:block, number: 666) + + conn = get(conn, block_transaction_path(conn, :index, "1")) + + assert html_response(conn, 404) =~ "This block has not been processed yet." + end + + test "with valid block number above the tip", %{conn: conn} do + block = insert(:block) + + conn = get(conn, block_transaction_path(conn, :index, block.number + 1)) + + assert html_response(conn, 404) =~ "Easy Cowboy! This block does not exist yet!" + end + + test "returns transactions for the block", %{conn: conn} do + block = insert(:block) + + :transaction + |> insert() + |> with_block(block) + + :transaction + |> insert(to_address: nil) + |> with_block(block) + |> with_contract_creation(insert(:contract_address)) + + conn = get(conn, block_transaction_path(BlockScoutWeb.Endpoint, :index, block), %{type: "JSON"}) + + assert json_response(conn, 200) + + {:ok, %{"items" => items}} = + conn.resp_body + |> Poison.decode() + + assert Enum.count(items) == 2 + end + + test "non-consensus block number without consensus blocks is treated as consensus number above tip", %{conn: conn} do + block = insert(:block, consensus: false) + + transaction = insert(:transaction) + insert(:transaction_fork, hash: transaction.hash, uncle_hash: block.hash) + + conn = get(conn, block_transaction_path(conn, :index, block.number)) + + assert_block_above_tip(conn) + end + + test "non-consensus block number above consensus block number is treated as consensus number above tip", %{ + conn: conn + } do + consensus_block = insert(:block, consensus: true, number: 1) + block = insert(:block, consensus: false, number: consensus_block.number + 1) + + transaction = insert(:transaction) + insert(:transaction_fork, hash: transaction.hash, uncle_hash: block.hash) + + conn = get(conn, block_transaction_path(conn, :index, block.number)) + + assert_block_above_tip(conn) + end + + test "returns transactions for consensus block hash", %{conn: conn} do + block = insert(:block, consensus: true) + + :transaction + |> insert() + |> with_block(block) + + conn = get(conn, block_transaction_path(BlockScoutWeb.Endpoint, :index, block), %{type: "JSON"}) + + assert json_response(conn, 200) + + {:ok, %{"items" => items}} = + conn.resp_body + |> Poison.decode() + + assert Enum.count(items) == 1 + end + + test "does not return transactions for non-consensus block hash", %{conn: conn} do + block = insert(:block, consensus: false) + + transaction = insert(:transaction) + insert(:transaction_fork, hash: transaction.hash, uncle_hash: block.hash) + + conn = get(conn, block_transaction_path(BlockScoutWeb.Endpoint, :index, block), %{type: "JSON"}) + + assert json_response(conn, 200) + + {:ok, %{"items" => items}} = + conn.resp_body + |> Poison.decode() + + assert Enum.empty?(items) + end + + test "does not return transactions for invalid block hash", %{conn: conn} do + conn = get(conn, block_transaction_path(conn, :index, "0x0")) + + assert html_response(conn, 404) + end + + test "with valid not-indexed hash", %{conn: conn} do + conn = get(conn, block_transaction_path(conn, :index, block_hash())) + + assert html_response(conn, 404) =~ "Block not found, please try again later." + end + + test "does not return unrelated transactions", %{conn: conn} do + insert(:transaction) + block = insert(:block) + + conn = get(conn, block_transaction_path(BlockScoutWeb.Endpoint, :index, block), %{type: "JSON"}) + + assert json_response(conn, 200) + + {:ok, %{"items" => items}} = + conn.resp_body + |> Poison.decode() + + assert Enum.empty?(items) + end + + test "does not return related transactions without a block", %{conn: conn} do + block = insert(:block) + insert(:transaction) + + conn = get(conn, block_transaction_path(BlockScoutWeb.Endpoint, :index, block), %{type: "JSON"}) + + assert json_response(conn, 200) + + {:ok, %{"items" => items}} = + conn.resp_body + |> Poison.decode() + + assert Enum.empty?(items) + end + + test "next_page_path exists if not on last page", %{conn: conn} do + block = insert(:block) + + 60 + |> insert_list(:transaction) + |> with_block(block) + + conn = get(conn, block_transaction_path(BlockScoutWeb.Endpoint, :index, block), %{type: "JSON"}) + + {:ok, %{"next_page_path" => next_page_path}} = + conn.resp_body + |> Poison.decode() + + assert next_page_path + end + + test "next_page_path is empty if on last page", %{conn: conn} do + block = insert(:block) + + :transaction + |> insert() + |> with_block(block) + + conn = get(conn, block_transaction_path(BlockScoutWeb.Endpoint, :index, block), %{type: "JSON"}) + + {:ok, %{"next_page_path" => next_page_path}} = + conn.resp_body + |> Poison.decode() + + refute next_page_path + end + + test "displays miner primary address name", %{conn: conn} do + miner_name = "POA Miner Pool" + %{address: miner_address} = insert(:address_name, name: miner_name, primary: true) + + block = insert(:block, miner: miner_address, miner_hash: nil) + + conn = get(conn, block_transaction_path(conn, :index, block)) + assert html_response(conn, 200) =~ miner_name + end + end + + defp assert_block_above_tip(conn) do + assert conn + |> html_response(404) + |> Floki.find(~S|.error-descr|) + |> Floki.text() + |> String.trim() == "Easy Cowboy! This block does not exist yet!" + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/chain/market_history_chart_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/chain/market_history_chart_controller_test.exs new file mode 100644 index 0000000..e295880 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/chain/market_history_chart_controller_test.exs @@ -0,0 +1,24 @@ +defmodule BlockScoutWeb.Chain.MarketHistoryChartControllerTest do + use BlockScoutWeb.ConnCase + + describe "GET show/2" do + test "returns error when not an ajax request" do + path = market_history_chart_path(BlockScoutWeb.Endpoint, :show) + + conn = get(build_conn(), path) + + assert conn.status == 422 + end + + test "returns ok when request is ajax" do + path = market_history_chart_path(BlockScoutWeb.Endpoint, :show) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert json_response(conn, 200) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/chain/transaction_history_chart_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/chain/transaction_history_chart_controller_test.exs new file mode 100644 index 0000000..7de3976 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/chain/transaction_history_chart_controller_test.exs @@ -0,0 +1,57 @@ +defmodule BlockScoutWeb.Chain.TransactionHistoryChartControllerTest do + use BlockScoutWeb.ConnCase + + alias BlockScoutWeb.Chain.TransactionHistoryChartController + alias Explorer.Chain.Transaction.History.TransactionStats + alias Explorer.Repo + + describe "GET show/2" do + test "returns error when not an ajax request" do + path = transaction_history_chart_path(BlockScoutWeb.Endpoint, :show) + + conn = get(build_conn(), path) + + assert conn.status == 422 + end + + test "returns ok when request is ajax" do + path = transaction_history_chart_path(BlockScoutWeb.Endpoint, :show) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert json_response(conn, 200) + end + + test "returns appropriate json data" do + latest = Date.utc_today() + dts = [latest, Date.add(latest, -1), Date.add(latest, -2)] + + some_transaction_stats = [ + %{date: Enum.at(dts, 1), number_of_transactions: 20}, + %{date: Enum.at(dts, 2), number_of_transactions: 30} + ] + + {num, _} = Repo.insert_all(TransactionStats, some_transaction_stats) + assert num == 2 + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> TransactionHistoryChartController.show([]) + + # turn conn.resp_body into a map using JSON + response_data = Jason.decode!(conn.resp_body, keys: :atoms) + history_data = Jason.decode!(response_data.history_data, keys: :atoms) + + history_data_with_elixir_dates = + Enum.map(history_data, fn stat -> + Map.put(stat, :date, Date.from_iso8601!(stat.date)) + end) + + assert history_data_with_elixir_dates == some_transaction_stats + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs new file mode 100644 index 0000000..246ab7f --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/chain_controller_test.exs @@ -0,0 +1,241 @@ +defmodule BlockScoutWeb.ChainControllerTest do + use BlockScoutWeb.ConnCase, + # ETS table is shared in `Explorer.Counters.AddressesCounter` + async: false + + import BlockScoutWeb.WebRouter.Helpers, only: [chain_path: 2, block_path: 3, transaction_path: 3, address_path: 3] + + alias Explorer.Chain.Block + alias Explorer.Counters.AddressesCounter + + setup do + Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.Blocks.child_id()) + Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Blocks.child_id()) + Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.Uncles.child_id()) + Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Uncles.child_id()) + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + :ok + end + + describe "GET index/2" do + test "returns a welcome message", %{conn: conn} do + conn = get(conn, chain_path(BlockScoutWeb.Endpoint, :show)) + + assert(html_response(conn, 200) =~ "POA") + end + + test "returns a block" do + insert(:block, %{number: 23}) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get("/chain-blocks") + + response = json_response(conn, 200) + + assert(List.first(response["blocks"])["block_number"] == 23) + end + + test "excludes all but the most recent five blocks" do + old_block = insert(:block) + insert_list(5, :block) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get("/chain-blocks") + + response = json_response(conn, 200) + + refute(Enum.member?(response["blocks"], old_block)) + end + + test "displays miner primary address names" do + miner_name = "POA Miner Pool" + %{address: miner_address} = insert(:address_name, name: miner_name, primary: true) + + insert(:block, miner: miner_address, miner_hash: nil) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get("/chain-blocks") + + response = List.first(json_response(conn, 200)["blocks"]) + + assert response["chain_block_html"] =~ miner_name + end + end + + describe "GET token_autocomplete/2" do + test "finds matching tokens" do + insert(:token, name: "MaGiC") + insert(:token, name: "Evil") + + conn = + build_conn() + |> get("/token-autocomplete?q=magic") + + assert Enum.count(json_response(conn, 200)) == 1 + end + + test "finds two matching tokens" do + insert(:token, name: "MaGiC") + insert(:token, name: "magic") + + conn = + build_conn() + |> get("/token-autocomplete?q=magic") + + assert Enum.count(json_response(conn, 200)) == 2 + end + + test "finds verified contract" do + insert(:smart_contract, name: "SuperToken", contract_code_md5: "123") + + conn = + build_conn() + |> get("/token-autocomplete?q=sup") + + assert Enum.count(json_response(conn, 200)) == 1 + end + + test "finds verified contract and token" do + insert(:smart_contract, name: "MagicContract", contract_code_md5: "123") + insert(:token, name: "magicToken") + + conn = + build_conn() + |> get("/token-autocomplete?q=mag") + + assert Enum.count(json_response(conn, 200)) == 2 + end + + test "finds verified contracts and tokens" do + insert(:smart_contract, name: "something", contract_code_md5: "123") + insert(:smart_contract, name: "MagicContract", contract_code_md5: "123") + insert(:token, name: "Magic3") + insert(:smart_contract, name: "magicContract2", contract_code_md5: "123") + insert(:token, name: "magicToken") + insert(:token, name: "OneMoreToken") + + conn = + build_conn() + |> get("/token-autocomplete?q=mag") + + assert Enum.count(json_response(conn, 200)) == 4 + end + + test "find by several words" do + insert(:token, name: "first Token") + insert(:token, name: "second Token") + + conn = + build_conn() + |> get("/token-autocomplete?q=fir+tok") + + assert Enum.count(json_response(conn, 200)) == 1 + end + + test "find by empty query" do + insert(:token, name: "MaGiCt0k3n") + insert(:smart_contract, name: "MagicContract", contract_code_md5: "123") + + conn = + build_conn() + |> get("/token-autocomplete?q=") + + assert Enum.count(json_response(conn, 200)) == 0 + end + + test "find by non-latin characters" do + insert(:token, name: "someToken") + + conn = + build_conn() + |> get("/token-autocomplete?q=%E0%B8%B5%E0%B8%AB") + + assert Enum.count(json_response(conn, 200)) == 0 + end + end + + describe "GET search/2" do + test "finds a consensus block by block number", %{conn: conn} do + insert(:block, number: 37) + conn = get(conn, "/search?q=37") + + assert redirected_to(conn) == block_path(conn, :show, "37") + end + + test "redirects to search results page even for searching non-consensus block by number", %{conn: conn} do + %Block{number: number} = insert(:block, consensus: false) + + conn = get(conn, "/search?q=#{number}") + + assert conn.status == 302 + end + + test "finds non-consensus block by hash", %{conn: conn} do + %Block{hash: hash} = insert(:block, consensus: false) + + conn = get(conn, "/search?q=#{hash}") + + assert redirected_to(conn) == block_path(conn, :show, hash) + end + + test "finds a transaction by hash", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + conn = get(conn, "/search?q=#{to_string(transaction.hash)}") + + assert redirected_to(conn) == transaction_path(conn, :show, transaction) + end + + test "finds a transaction by hash when there are not 0x prefix", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + "0x" <> non_prefix_hash = to_string(transaction.hash) + + conn = get(conn, "/search?q=#{to_string(non_prefix_hash)}") + + assert redirected_to(conn) == transaction_path(conn, :show, transaction) + end + + test "finds an address by hash", %{conn: conn} do + address = insert(:address) + conn = get(conn, "/search?q=#{to_string(address.hash)}") + + assert redirected_to(conn) == address_path(conn, :show, address) + end + + test "finds an address by hash when there are extra spaces", %{conn: conn} do + address = insert(:address) + conn = get(conn, "/search?q=#{to_string(address.hash)}") + + assert redirected_to(conn) == address_path(conn, :show, address) + end + + test "finds an address by hash when there are not 0x prefix", %{conn: conn} do + address = insert(:address) + "0x" <> non_prefix_hash = to_string(address.hash) + + conn = get(conn, "/search?q=#{to_string(non_prefix_hash)}") + + assert redirected_to(conn) == address_path(conn, :show, address) + end + + test "redirects to result page when it finds nothing", %{conn: conn} do + conn = get(conn, "/search?q=zaphod") + assert conn.status == 302 + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/page_not_found_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/page_not_found_controller_test.exs new file mode 100644 index 0000000..8540358 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/page_not_found_controller_test.exs @@ -0,0 +1,11 @@ +defmodule BlockScoutWeb.PageNotFoundControllerTest do + use BlockScoutWeb.ConnCase + + describe "GET index/2" do + test "returns 404 status", %{conn: conn} do + conn = get(conn, "/wrong", %{}) + + assert html_response(conn, 404) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/pending_transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/pending_transaction_controller_test.exs new file mode 100644 index 0000000..b19bec2 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/pending_transaction_controller_test.exs @@ -0,0 +1,84 @@ +defmodule BlockScoutWeb.PendingTransactionControllerTest do + use BlockScoutWeb.ConnCase + alias Explorer.Chain.{Hash, Transaction} + + import BlockScoutWeb.WebRouter.Helpers, only: [pending_transaction_path: 2, pending_transaction_path: 3] + + describe "GET index/2" do + test "returns no transactions that are in a block", %{conn: conn} do + :transaction + |> insert() + |> with_block() + + conn = get(conn, pending_transaction_path(BlockScoutWeb.Endpoint, :index, %{"type" => "JSON"})) + + assert conn |> json_response(200) |> Map.get("items") |> Enum.empty?() + end + + test "returns pending transactions", %{conn: conn} do + transaction = insert(:transaction) + + conn = get(conn, pending_transaction_path(BlockScoutWeb.Endpoint, :index, %{"type" => "JSON"})) + + assert hd(json_response(conn, 200)["items"]) =~ to_string(transaction.hash) + end + + test "does not show dropped/replaced transactions", %{conn: conn} do + transaction = insert(:transaction) + + dropped_replaced = + :transaction + |> insert(status: 0, error: "dropped/replaced") + |> with_block() + + conn = get(conn, pending_transaction_path(BlockScoutWeb.Endpoint, :index, %{"type" => "JSON"})) + + assert hd(json_response(conn, 200)["items"]) =~ to_string(transaction.hash) + refute hd(json_response(conn, 200)["items"]) =~ to_string(dropped_replaced.hash) + end + + test "works when there are no transactions", %{conn: conn} do + conn = get(conn, pending_transaction_path(conn, :index)) + + assert html_response(conn, 200) + end + + test "returns next page of results based on last seen pending transaction", %{conn: conn} do + second_page_hashes = + 50 + |> insert_list(:transaction) + |> Enum.map(&to_string(&1.hash)) + + %Transaction{inserted_at: inserted_at, hash: hash} = insert(:transaction) + + conn = + get(conn, pending_transaction_path(BlockScoutWeb.Endpoint, :index), %{ + "type" => "JSON", + "inserted_at" => DateTime.to_iso8601(inserted_at), + "hash" => Hash.to_string(hash) + }) + + {:ok, %{"items" => pending_transactions}} = Poison.decode(conn.resp_body) + + assert length(pending_transactions) == length(second_page_hashes) + end + + test "next_page_path exist if not on last page", %{conn: conn} do + 60 + |> insert_list(:transaction) + |> Enum.fetch!(10) + + conn = get(conn, pending_transaction_path(BlockScoutWeb.Endpoint, :index, %{"type" => "JSON"})) + + assert json_response(conn, 200)["next_page_path"] + end + + test "next_page_path is empty if on last page", %{conn: conn} do + insert(:transaction) + + conn = get(conn, pending_transaction_path(BlockScoutWeb.Endpoint, :index, %{"type" => "JSON"})) + + refute json_response(conn, 200)["next_page_path"] + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/recent_transactions_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/recent_transactions_controller_test.exs new file mode 100644 index 0000000..0a2267f --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/recent_transactions_controller_test.exs @@ -0,0 +1,54 @@ +defmodule BlockScoutWeb.RecentTransactionsControllerTest do + use BlockScoutWeb.ConnCase + + import BlockScoutWeb.WebRouter.Helpers, only: [recent_transactions_path: 2] + + alias Explorer.Chain.Hash + + describe "GET index/2" do + test "returns a transaction", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + conn = + conn + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(recent_transactions_path(conn, :index)) + + assert response = json_response(conn, 200)["transactions"] + + response_hashes = Enum.map(response, & &1["transaction_hash"]) + + assert Enum.member?(response_hashes, Hash.to_string(transaction.hash)) + end + + test "only returns transactions with an associated block", %{conn: conn} do + associated = + :transaction + |> insert() + |> with_block() + + unassociated = insert(:transaction) + + conn = + conn + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(recent_transactions_path(conn, :index)) + + assert response = json_response(conn, 200)["transactions"] + + response_hashes = Enum.map(response, & &1["transaction_hash"]) + + assert Enum.member?(response_hashes, Hash.to_string(associated.hash)) + refute Enum.member?(response_hashes, Hash.to_string(unassociated.hash)) + end + + test "only responds to ajax requests", %{conn: conn} do + conn = get(conn, recent_transactions_path(conn, :index)) + + assert conn.status == 422 + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs new file mode 100644 index 0000000..1714841 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/smart_contract_controller_test.exs @@ -0,0 +1,351 @@ +defmodule BlockScoutWeb.SmartContractControllerTest do + use BlockScoutWeb.ConnCase + + import Mox + + alias Explorer.Chain.{Address, Hash} + alias Explorer.Factory + + setup :verify_on_exit! + + describe "GET index/3" do + test "returns not found for nonexistent address" do + nonexistent_address_hash = Hash.to_string(Factory.address_hash()) + path = smart_contract_path(BlockScoutWeb.Endpoint, :index, hash: nonexistent_address_hash) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert html_response(conn, 404) + end + + test "error for invalid address" do + path = smart_contract_path(BlockScoutWeb.Endpoint, :index, hash: "0x00", type: :regular, action: :read) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert conn.status == 422 + end + + test "only responds to ajax requests", %{conn: conn} do + smart_contract = insert(:smart_contract, contract_code_md5: "123") + + path = smart_contract_path(BlockScoutWeb.Endpoint, :index, hash: smart_contract.address_hash) + + conn = get(conn, path) + + assert conn.status == 404 + end + + test "lists the smart contract read only functions" do + token_contract_address = insert(:contract_address) + + insert(:smart_contract, address_hash: token_contract_address.hash, contract_code_md5: "123") + + blockchain_get_code_mock() + blockchain_get_function_mock() + + path = + smart_contract_path(BlockScoutWeb.Endpoint, :index, + hash: token_contract_address.hash, + type: :regular, + action: :read + ) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert conn.status == 200 + refute conn.assigns.read_only_functions == [] + end + + test "lists [] proxy read only functions if no verified implementation" do + token_contract_address = insert(:contract_address) + + insert(:smart_contract, + address_hash: token_contract_address.hash, + abi: [ + %{ + "type" => "function", + "stateMutability" => "view", + "payable" => false, + "outputs" => [%{"type" => "address", "name" => ""}], + "name" => "implementation", + "inputs" => [], + "constant" => true + } + ], + contract_code_md5: "123" + ) + + blockchain_get_code_mock() + + path = + smart_contract_path(BlockScoutWeb.Endpoint, :index, + hash: token_contract_address.hash, + type: :proxy, + action: :read + ) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert conn.status == 200 + assert conn.assigns.read_only_functions == [] + end + + test "lists [] proxy read only functions if no verified eip-1967 implementation" do + token_contract_address = insert(:contract_address) + + insert(:smart_contract, + address_hash: token_contract_address.hash, + abi: [ + %{ + "type" => "function", + "stateMutability" => "nonpayable", + "payable" => false, + "outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}], + "name" => "implementation", + "inputs" => [], + "constant" => false + } + ], + contract_code_md5: "123" + ) + + blockchain_get_code_mock() + blockchain_get_implementation_mock() + + path = + smart_contract_path(BlockScoutWeb.Endpoint, :index, + hash: token_contract_address.hash, + type: :proxy, + action: :read + ) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert conn.status == 200 + assert conn.assigns.read_only_functions == [] + end + + test "lists [] proxy read only functions if no verified eip-1967 implementation and eth_getStorageAt returns not normalized address hash" do + token_contract_address = insert(:contract_address) + + insert(:smart_contract, + address_hash: token_contract_address.hash, + abi: [ + %{ + "type" => "function", + "stateMutability" => "nonpayable", + "payable" => false, + "outputs" => [%{"type" => "address", "name" => "", "internalType" => "address"}], + "name" => "implementation", + "inputs" => [], + "constant" => false + } + ], + contract_code_md5: "123" + ) + + blockchain_get_code_mock() + blockchain_get_implementation_mock_2() + + path = + smart_contract_path(BlockScoutWeb.Endpoint, :index, + hash: token_contract_address.hash, + type: :proxy, + action: :read + ) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert conn.status == 200 + assert conn.assigns.read_only_functions == [] + end + end + + describe "GET show/3" do + test "returns not found for nonexistent address" do + nonexistent_address_hash = Address.checksum(Hash.to_string(Factory.address_hash())) + + path = + smart_contract_path( + BlockScoutWeb.Endpoint, + :show, + nonexistent_address_hash, + function_name: "get", + args: [] + ) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert html_response(conn, 404) + end + + test "error for invalid address" do + path = + smart_contract_path( + BlockScoutWeb.Endpoint, + :show, + "0x00", + function_name: "get", + args: [] + ) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert conn.status == 422 + end + + test "only responds to ajax requests", %{conn: conn} do + smart_contract = insert(:smart_contract, contract_code_md5: "123") + + path = + smart_contract_path( + BlockScoutWeb.Endpoint, + :show, + Address.checksum(smart_contract.address_hash), + function_name: "get", + args: [] + ) + + conn = get(conn, path) + + assert conn.status == 404 + end + + test "fetch the function value from the blockchain" do + address = insert(:contract_address) + smart_contract = insert(:smart_contract, address_hash: address.hash, contract_code_md5: "123") + + blockchain_get_code_mock() + blockchain_get_function_mock() + + path = + smart_contract_path( + BlockScoutWeb.Endpoint, + :show, + Address.checksum(smart_contract.address_hash), + function_name: "get", + method_id: "6d4ce63c", + args_count: 0, + args: [] + ) + + conn = + build_conn() + |> put_req_header("x-requested-with", "xmlhttprequest") + |> get(path) + + assert conn.status == 200 + + assert %{ + function_name: "get", + layout: false, + outputs: [%{"type" => "uint256", "value" => 0}] + } = conn.assigns + end + end + + defp blockchain_get_function_mock do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn [%{id: id, method: _, params: [%{data: _, to: _}, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0000000000000000000000000000000000000000000000000000000000000000"}]} + end + ) + end + + defp blockchain_get_code_mock do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn [%{id: id, method: "eth_getCode", params: [_, _]}], _options -> + {:ok, [%{id: id, jsonrpc: "2.0", result: "0x0"}]} + end + ) + end + + defp blockchain_get_implementation_mock do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn %{id: _, method: _, params: [_, _, _]}, _options -> + {:ok, "0xcebb2CCCFe291F0c442841cBE9C1D06EED61Ca02"} + end + ) + end + + defp blockchain_get_implementation_mock_2 do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn %{id: _, method: _, params: [_, _, _]}, _options -> + {:ok, "0x000000000000000000000000cebb2CCCFe291F0c442841cBE9C1D06EED61Ca02"} + end + ) + end + + def get_eip1967_implementation do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/tokens/holder_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/tokens/holder_controller_test.exs new file mode 100644 index 0000000..45c23d6 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/tokens/holder_controller_test.exs @@ -0,0 +1,93 @@ +defmodule BlockScoutWeb.Tokens.HolderControllerTest do + use BlockScoutWeb.ConnCase, async: true + + alias Explorer.Chain.{Address, Hash} + + describe "GET index/3" do + test "with invalid address hash", %{conn: conn} do + conn = get(conn, token_holder_path(BlockScoutWeb.Endpoint, :index, "invalid_address")) + + assert html_response(conn, 404) + end + + test "with a token that doesn't exist", %{conn: conn} do + address = build(:address) + conn = get(conn, token_holder_path(BlockScoutWeb.Endpoint, :index, address.hash)) + + assert html_response(conn, 404) + end + + test "successfully renders the page", %{conn: conn} do + token = insert(:token) + + insert_list( + 2, + :address_current_token_balance, + token_contract_address_hash: token.contract_address_hash + ) + + conn = + get( + conn, + token_holder_path(BlockScoutWeb.Endpoint, :index, token.contract_address_hash) + ) + + assert html_response(conn, 200) + end + + test "returns next page of results based on last seen token balance", %{conn: conn} do + contract_address = build(:contract_address, hash: "0x6937cb25eb54bc013b9c13c47ab38eb63edd1493") + token = insert(:token, contract_address: contract_address) + + second_page_token_balances = + 1..50 + |> Enum.map( + &insert( + :address_current_token_balance, + token_contract_address_hash: token.contract_address_hash, + value: &1 + 1000 + ) + ) + + token_balance = + insert( + :address_current_token_balance, + token_contract_address_hash: token.contract_address_hash, + value: 50000 + ) + + conn = + get(conn, token_holder_path(conn, :index, token.contract_address_hash), %{ + "value" => Decimal.to_integer(token_balance.value), + "address_hash" => Hash.to_string(token_balance.address_hash), + "type" => "JSON" + }) + + token_balance_tiles = json_response(conn, 200)["items"] + + assert Enum.all?(second_page_token_balances, fn token_balance -> + Enum.any?(token_balance_tiles, fn tile -> + String.contains?(tile, Address.checksum(token_balance.address_hash)) + end) + end) + end + + test "next_page_params exists if not on last page", %{conn: conn} do + contract_address = build(:contract_address, hash: "0x6937cb25eb54bc013b9c13c47ab38eb63edd1493") + token = insert(:token, contract_address: contract_address) + + Enum.each( + 1..51, + &insert( + :address_current_token_balance, + token_contract_address_hash: token.contract_address_hash, + value: &1 + 1000 + ) + ) + + conn = get(conn, token_holder_path(conn, :index, token.contract_address_hash, %{"type" => "JSON"})) + + assert json_response(conn, 200)["next_page_path"] + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance/transfer_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance/transfer_controller_test.exs new file mode 100644 index 0000000..8f4a49c --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance/transfer_controller_test.exs @@ -0,0 +1,30 @@ +defmodule BlockScoutWeb.Tokens.Instance.TransferControllerTest do + use BlockScoutWeb.ConnCase, async: false + + describe "GET token-transfers/2" do + test "fetches the instance", %{conn: conn} do + contract_address = insert(:address) + + insert(:token, contract_address: contract_address) + + contract_address_hash = contract_address.hash + + token_id = Decimal.new(10) + + insert(:token_instance, + token_contract_address_hash: contract_address_hash, + token_id: token_id + ) + + conn = get(conn, "/token/#{contract_address_hash}/instance/#{token_id}/token-transfers") + + assert %{ + assigns: %{ + token_instance: %{ + instance: %{token_contract_address_hash: ^contract_address_hash, token_id: ^token_id} + } + } + } = conn + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs new file mode 100644 index 0000000..7ced36d --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/tokens/instance_controller_test.exs @@ -0,0 +1,46 @@ +defmodule BlockScoutWeb.Tokens.InstanceControllerTest do + use BlockScoutWeb.ConnCase, async: false + + describe "GET show/2" do + test "redirects with valid params", %{conn: conn} do + contract_address = insert(:address) + + insert(:token, contract_address: contract_address) + + token_id = 10 + + insert(:token_transfer, + from_address: contract_address, + token_contract_address: contract_address, + token_id: token_id + ) + + conn = get(conn, token_instance_path(BlockScoutWeb.Endpoint, :show, to_string(contract_address.hash), token_id)) + + assert conn.status == 302 + end + + test "works for ERC-1155 tokens", %{conn: conn} do + contract_address = insert(:address) + + insert(:token, contract_address: contract_address) + + token_id = 10 + + insert(:token_transfer, + from_address: contract_address, + token_contract_address: contract_address, + token_id: nil, + token_ids: [token_id] + ) + + conn = get(conn, token_instance_path(BlockScoutWeb.Endpoint, :show, to_string(contract_address.hash), token_id)) + + assert conn.status == 302 + + assert get_resp_header(conn, "location") == [ + "/token/#{contract_address.hash}/instance/#{token_id}/token-transfers" + ] + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/tokens/inventory_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/tokens/inventory_controller_test.exs new file mode 100644 index 0000000..e1591a4 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/tokens/inventory_controller_test.exs @@ -0,0 +1,141 @@ +defmodule BlockScoutWeb.Tokens.InventoryControllerTest do + use BlockScoutWeb.ConnCase, async: false + + describe "GET index/3" do + test "with invalid address hash", %{conn: conn} do + conn = get(conn, token_inventory_path(conn, :index, "invalid_address")) + + assert html_response(conn, 404) + end + + test "with a token that doesn't exist", %{conn: conn} do + address = build(:address) + conn = get(conn, token_inventory_path(conn, :index, address.hash)) + + assert html_response(conn, 404) + end + + test "successfully renders the page", %{conn: conn} do + token_contract_address = insert(:contract_address) + token = insert(:token, type: "ERC-721", contract_address: token_contract_address) + + transaction = + :transaction + |> insert() + |> with_block() + + insert( + :token_transfer, + transaction: transaction, + token_contract_address: token_contract_address, + token: token + ) + + conn = + get( + conn, + token_inventory_path(conn, :index, token_contract_address.hash) + ) + + assert html_response(conn, 200) + end + + test "returns next page of results based on last seen token balance", %{conn: conn} do + token = insert(:token, type: "ERC-721") + + transaction = + :transaction + |> insert() + |> with_block() + + second_page_token_balances = + Enum.map(1..50, fn i -> + insert( + :token_transfer, + transaction: transaction, + token_contract_address: token.contract_address, + token: token, + token_id: i + 1000 + ) + + insert( + :token_instance, + token_contract_address_hash: token.contract_address.hash, + token_id: i + 1000 + ) + end) + + conn = + get(conn, token_inventory_path(conn, :index, token.contract_address_hash), %{ + "token_id" => "999", + "type" => "JSON" + }) + + conn = get(conn, token_inventory_path(conn, :index, token.contract_address_hash), %{type: "JSON"}) + + {:ok, %{"items" => items}} = + conn.resp_body + |> Poison.decode() + + assert Enum.count(items) == Enum.count(second_page_token_balances) + end + + test "next_page_path exists if not on last page", %{conn: conn} do + token = insert(:token, type: "ERC-721") + + transaction = + :transaction + |> insert() + |> with_block() + + Enum.each(1..51, fn i -> + insert( + :token_transfer, + transaction: transaction, + token_contract_address: token.contract_address, + token: token, + token_id: i + 1000 + ) + + insert( + :token_instance, + token_contract_address_hash: token.contract_address.hash, + token_id: i + 1000 + ) + end) + + conn = get(conn, token_inventory_path(conn, :index, token.contract_address_hash), %{type: "JSON"}) + + {:ok, %{"next_page_path" => next_page_path}} = + conn.resp_body + |> Poison.decode() + + assert next_page_path + end + + test "next_page_path is empty if on last page", %{conn: conn} do + token = insert(:token, type: "ERC-721") + + transaction = + :transaction + |> insert() + |> with_block() + + insert( + :token_transfer, + transaction: transaction, + token_contract_address: token.contract_address, + token: token, + token_id: 1000 + ) + + conn = get(conn, token_inventory_path(conn, :index, token.contract_address_hash), %{type: "JSON"}) + + {:ok, %{"next_page_path" => next_page_path}} = + conn.resp_body + |> Poison.decode() + + refute next_page_path + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/tokens/read_contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/tokens/read_contract_controller_test.exs new file mode 100644 index 0000000..ab5ba16 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/tokens/read_contract_controller_test.exs @@ -0,0 +1,104 @@ +defmodule BlockScoutWeb.Tokens.ContractControllerTest do + use BlockScoutWeb.ConnCase, async: false + + import Mox + + describe "GET index/3" do + test "with invalid address hash", %{conn: conn} do + conn = get(conn, token_read_contract_path(BlockScoutWeb.Endpoint, :index, "invalid_address")) + + assert html_response(conn, 404) + end + + test "with unverified address hash returns not found", %{conn: conn} do + address = insert(:address) + + token = insert(:token, contract_address: address) + + transaction = + :transaction + |> insert() + |> with_block() + + insert( + :token_transfer, + to_address: build(:address), + transaction: transaction, + token_contract_address: address, + token: token + ) + + conn = get(conn, token_read_contract_path(BlockScoutWeb.Endpoint, :index, token.contract_address_hash)) + + assert html_response(conn, 404) + end + + test "successfully renders the page when the token is a verified smart contract", %{conn: conn} do + token_contract_address = insert(:contract_address) + + insert(:smart_contract, address_hash: token_contract_address.hash, contract_code_md5: "123") + + token = insert(:token, contract_address: token_contract_address) + + transaction = + :transaction + |> insert() + |> with_block() + + insert( + :token_transfer, + to_address: build(:address), + transaction: transaction, + token_contract_address: token_contract_address, + token: token + ) + + get_eip1967_implementation() + + conn = get(conn, token_read_contract_path(BlockScoutWeb.Endpoint, :index, token.contract_address_hash)) + + assert html_response(conn, 200) + assert token.contract_address_hash == conn.assigns.token.contract_address_hash + end + end + + def get_eip1967_implementation do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/tokens/token_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/tokens/token_controller_test.exs new file mode 100644 index 0000000..ab5d93f --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/tokens/token_controller_test.exs @@ -0,0 +1,39 @@ +defmodule BlockScoutWeb.Tokens.TokenControllerTest do + use BlockScoutWeb.ConnCase, async: true + + alias Explorer.Chain.Address + + describe "GET show/2" do + test "redirects to transfers page", %{conn: conn} do + contract_address = insert(:address) + + conn = get(conn, token_path(conn, :show, Address.checksum(contract_address.hash))) + + assert conn.status == 302 + end + end + + # describe "GET token-counters/2" do + # # flaky test + # test "returns token counters", %{conn: conn} do + # contract_address = insert(:address) + + # insert(:token, contract_address: contract_address) + + # token_id = 10 + + # insert(:token_transfer, + # from_address: contract_address, + # token_contract_address: contract_address, + # token_id: token_id + # ) + + # conn = get(conn, "/token-counters", %{"id" => Address.checksum(contract_address.hash)}) + + # assert conn.status == 200 + # {:ok, response} = Jason.decode(conn.resp_body) + + # assert %{"token_holder_count" => 0, "transfer_count" => 1} == response + # end + # end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/transaction_controller_test.exs new file mode 100644 index 0000000..c94d095 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/transaction_controller_test.exs @@ -0,0 +1,137 @@ +defmodule BlockScoutWeb.TransactionControllerTest do + use BlockScoutWeb.ConnCase + + import BlockScoutWeb.WebRouter.Helpers, + only: [transaction_path: 3] + + alias Explorer.Chain.Transaction + + describe "GET index/2" do + test "returns a collated transactions", %{conn: conn} do + :transaction + |> insert() + |> with_block() + + conn = get(conn, transaction_path(conn, :index, %{"type" => "JSON"})) + + transactions_html = + conn + |> json_response(200) + |> Map.get("items") + + assert length(transactions_html) == 1 + end + + test "returns a count of transactions", %{conn: conn} do + :transaction + |> insert() + |> with_block() + + conn = get(conn, "/txs") + + assert is_integer(conn.assigns.transaction_estimated_count) + end + + test "excludes pending transactions", %{conn: conn} do + %Transaction{hash: transaction_hash} = + :transaction + |> insert() + |> with_block() + + %Transaction{hash: pending_transaction_hash} = insert(:transaction) + + conn = get(conn, transaction_path(conn, :index, %{"type" => "JSON"})) + + transactions_html = + conn + |> json_response(200) + |> Map.get("items") + + assert Enum.any?(transactions_html, &String.contains?(&1, to_string(transaction_hash))) + refute Enum.any?(transactions_html, &String.contains?(&1, to_string(pending_transaction_hash))) + end + + test "returns next page of results based on last seen transaction", %{conn: conn} do + second_page_hashes = + 50 + |> insert_list(:transaction) + |> with_block() + |> Enum.map(&to_string(&1.hash)) + + %Transaction{block_number: block_number, index: index} = + :transaction + |> insert() + |> with_block() + + conn = + get( + conn, + transaction_path(conn, :index, %{ + "type" => "JSON", + "block_number" => Integer.to_string(block_number), + "index" => Integer.to_string(index) + }) + ) + + transactions_html = + conn + |> json_response(200) + |> Map.get("items") + + assert length(second_page_hashes) == length(transactions_html) + end + + test "next_page_params exist if not on last page", %{conn: conn} do + address = insert(:address) + block = insert(:block) + + 60 + |> insert_list(:transaction, from_address: address) + |> with_block(block) + + conn = get(conn, transaction_path(conn, :index, %{"type" => "JSON"})) + + assert conn |> json_response(200) |> Map.get("next_page_params") + end + + test "next_page_params are empty if on last page", %{conn: conn} do + address = insert(:address) + + :transaction + |> insert(from_address: address) + |> with_block() + + conn = get(conn, transaction_path(conn, :index, %{"type" => "JSON"})) + + refute conn |> json_response(200) |> Map.get("next_page_path") + end + + test "works when there are no transactions", %{conn: conn} do + conn = get(conn, "/txs") + + assert html_response(conn, 200) + end + end + + describe "GET show/3" do + test "responds with 404 with the transaction missing", %{conn: conn} do + hash = transaction_hash() + conn = get(conn, transaction_path(BlockScoutWeb.Endpoint, :show, hash)) + + assert html_response(conn, 404) + end + + test "responds with 422 when the hash is invalid", %{conn: conn} do + conn = get(conn, transaction_path(BlockScoutWeb.Endpoint, :show, "wrong")) + + assert html_response(conn, 422) + end + + test "no redirect from tx page", %{conn: conn} do + transaction = insert(:transaction) + conn = get(conn, transaction_path(BlockScoutWeb.Endpoint, :show, transaction)) + + assert html_response(conn, 200) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/transaction_internal_transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/transaction_internal_transaction_controller_test.exs new file mode 100644 index 0000000..4969db3 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/transaction_internal_transaction_controller_test.exs @@ -0,0 +1,231 @@ +defmodule BlockScoutWeb.TransactionInternalTransactionControllerTest do + use BlockScoutWeb.ConnCase + + import Mox + + import BlockScoutWeb.WebRouter.Helpers, only: [transaction_internal_transaction_path: 3] + + alias Explorer.Chain.InternalTransaction + alias Explorer.ExchangeRates.Token + + describe "GET index/3" do + test "with missing transaction", %{conn: conn} do + hash = transaction_hash() + conn = get(conn, transaction_internal_transaction_path(BlockScoutWeb.Endpoint, :index, hash)) + + assert html_response(conn, 404) + end + + test "with invalid transaction hash", %{conn: conn} do + conn = get(conn, transaction_internal_transaction_path(BlockScoutWeb.Endpoint, :index, "nope")) + + assert html_response(conn, 422) + end + + test "includes transaction data", %{conn: conn} do + block = insert(:block, %{number: 777}) + + transaction = + :transaction + |> insert() + |> with_block(block) + + conn = get(conn, transaction_internal_transaction_path(BlockScoutWeb.Endpoint, :index, transaction.hash)) + + assert html_response(conn, 200) + assert conn.assigns.transaction.hash == transaction.hash + end + + test "includes internal transactions for the transaction", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block(insert(:block, number: 1)) + + insert(:internal_transaction, + transaction: transaction, + index: 0, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 0 + ) + + insert(:internal_transaction, + transaction: transaction, + index: 1, + transaction_index: transaction.index, + block_number: transaction.block_number, + block_hash: transaction.block_hash, + block_index: 1 + ) + + path = transaction_internal_transaction_path(BlockScoutWeb.Endpoint, :index, transaction.hash) + + conn = get(conn, path, %{type: "JSON"}) + + {:ok, %{"items" => items}} = + conn.resp_body + |> Poison.decode() + + assert json_response(conn, 200) + + # excluding of internal transactions with type=call and index=0 + assert Enum.count(items) == 1 + end + + test "includes USD exchange rate value for address in assigns", %{conn: conn} do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn %{id: _id, method: "net_version", params: []}, _options -> + {:ok, "100"} + end) + + transaction = insert(:transaction) + + conn = get(conn, transaction_internal_transaction_path(BlockScoutWeb.Endpoint, :index, transaction.hash)) + + assert %Token{} = conn.assigns.exchange_rate + end + + test "with no to_address_hash overview contains contract create address", %{conn: conn} do + contract_address = insert(:contract_address) + + transaction = + :transaction + |> insert(to_address: nil) + |> with_contract_creation(contract_address) + |> with_block(insert(:block, number: 7000)) + + internal_transaction = + :internal_transaction_create + |> insert( + transaction: transaction, + index: 0, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 0 + ) + |> with_contract_creation(contract_address) + + conn = + get( + conn, + transaction_internal_transaction_path( + BlockScoutWeb.Endpoint, + :index, + internal_transaction.transaction_hash + ) + ) + + refute is_nil(conn.assigns.transaction.created_contract_address_hash) + end + + test "returns next page of results based on last seen internal transaction", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block(insert(:block, number: 7000)) + + %InternalTransaction{index: index} = + insert(:internal_transaction, + transaction: transaction, + index: 0, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 0 + ) + + second_page_indexes = + 1..50 + |> Enum.map(fn index -> + insert(:internal_transaction, + transaction: transaction, + index: index, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: index + ) + end) + |> Enum.map(& &1.index) + + conn = + get(conn, transaction_internal_transaction_path(BlockScoutWeb.Endpoint, :index, transaction.hash), %{ + "index" => Integer.to_string(index), + "type" => "JSON" + }) + + {:ok, %{"items" => items}} = + conn.resp_body + |> Poison.decode() + + assert Enum.count(items) == Enum.count(second_page_indexes) + end + + test "next_page_path exists if not on last page", %{conn: conn} do + block = insert(:block, number: 7000) + + transaction = + :transaction + |> insert() + |> with_block(block) + + 1..60 + |> Enum.map(fn index -> + insert( + :internal_transaction, + transaction: transaction, + index: index, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: index + ) + end) + + conn = + get(conn, transaction_internal_transaction_path(BlockScoutWeb.Endpoint, :index, transaction.hash), %{ + type: "JSON" + }) + + {:ok, %{"next_page_path" => next_page_path}} = + conn.resp_body + |> Poison.decode() + + assert next_page_path + end + + test "next_page_path is empty if on last page", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block(insert(:block, number: 7000)) + + 1..2 + |> Enum.map(fn index -> + insert( + :internal_transaction, + transaction: transaction, + index: index, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: index + ) + end) + + conn = + get(conn, transaction_internal_transaction_path(BlockScoutWeb.Endpoint, :index, transaction.hash), %{ + type: "JSON" + }) + + {:ok, %{"next_page_path" => next_page_path}} = + conn.resp_body + |> Poison.decode() + + refute next_page_path + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/transaction_log_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/transaction_log_controller_test.exs new file mode 100644 index 0000000..fc9ced3 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/transaction_log_controller_test.exs @@ -0,0 +1,188 @@ +defmodule BlockScoutWeb.TransactionLogControllerTest do + use BlockScoutWeb.ConnCase + + import Mox + + import BlockScoutWeb.WebRouter.Helpers, only: [transaction_log_path: 3] + + alias Explorer.Chain.Address + alias Explorer.ExchangeRates.Token + + describe "GET index/2" do + test "with invalid transaction hash", %{conn: conn} do + conn = get(conn, transaction_log_path(conn, :index, "invalid_transaction_string")) + + assert html_response(conn, 422) + end + + test "with valid transaction hash without transaction", %{conn: conn} do + conn = + get( + conn, + transaction_log_path(conn, :index, "0x3a3eb134e6792ce9403ea4188e5e79693de9e4c94e499db132be086400da79e6") + ) + + assert html_response(conn, 404) + end + + test "returns logs for the transaction", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + address = insert(:address) + + insert(:log, + address: address, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number + ) + + conn = get(conn, transaction_log_path(conn, :index, transaction), %{type: "JSON"}) + + {:ok, %{"items" => items}} = conn.resp_body |> Poison.decode() + first_log = List.first(items) + + assert String.contains?(first_log, Address.checksum(address.hash)) + end + + test "returns logs for the transaction with nil to_address", %{conn: conn} do + transaction = + :transaction + |> insert(to_address: nil) + |> with_block() + + address = insert(:address) + + insert(:log, + address: address, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number + ) + + conn = get(conn, transaction_log_path(conn, :index, transaction), %{type: "JSON"}) + + {:ok, %{"items" => items}} = conn.resp_body |> Poison.decode() + first_log = List.first(items) + + assert String.contains?(first_log, Address.checksum(address.hash)) + end + + test "assigns no logs when there are none", %{conn: conn} do + transaction = insert(:transaction) + path = transaction_log_path(conn, :index, transaction) + + conn = get(conn, path, %{type: "JSON"}) + + {:ok, %{"items" => items}} = conn.resp_body |> Poison.decode() + + assert Enum.empty?(items) + end + + test "returns next page of results based on last seen transaction log", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + log = + insert(:log, + transaction: transaction, + index: 1, + block: transaction.block, + block_number: transaction.block_number + ) + + second_page_indexes = + 2..51 + |> Enum.map(fn index -> + insert(:log, + transaction: transaction, + index: index, + block: transaction.block, + block_number: transaction.block_number + ) + end) + |> Enum.map(& &1.index) + + conn = + get(conn, transaction_log_path(conn, :index, transaction), %{ + "index" => Integer.to_string(log.index), + "type" => "JSON" + }) + + {:ok, %{"items" => items}} = conn.resp_body |> Poison.decode() + + assert Enum.count(items) == Enum.count(second_page_indexes) + end + + test "next_page_path exists if not on last page", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + 1..60 + |> Enum.map(fn index -> + insert(:log, + transaction: transaction, + index: index, + block: transaction.block, + block_number: transaction.block_number + ) + end) + + conn = get(conn, transaction_log_path(conn, :index, transaction), %{type: "JSON"}) + + {:ok, %{"next_page_path" => path}} = conn.resp_body |> Poison.decode() + + assert path + end + + test "next_page_path is empty if on last page", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + conn = get(conn, transaction_log_path(conn, :index, transaction), %{type: "JSON"}) + + {:ok, %{"next_page_path" => path}} = conn.resp_body |> Poison.decode() + + refute path + end + end + + test "includes USD exchange rate value for address in assigns", %{conn: conn} do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn %{id: _id, method: "net_version", params: []}, _options -> + {:ok, "100"} + end) + + transaction = insert(:transaction) + + conn = get(conn, transaction_log_path(BlockScoutWeb.Endpoint, :index, transaction.hash)) + + assert %Token{} = conn.assigns.exchange_rate + end + + test "loads for transactions that created a contract", %{conn: conn} do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn %{id: _id, method: "net_version", params: []}, _options -> + {:ok, "100"} + end) + + contract_address = insert(:contract_address) + + transaction = + :transaction + |> insert(to_address: nil) + |> with_contract_creation(contract_address) + + conn = get(conn, transaction_log_path(conn, :index, transaction)) + assert html_response(conn, 200) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/transaction_state_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/transaction_state_controller_test.exs new file mode 100644 index 0000000..a4adb8f --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/transaction_state_controller_test.exs @@ -0,0 +1,187 @@ +defmodule BlockScoutWeb.TransactionStateControllerTest do + use BlockScoutWeb.ConnCase + + import Mox + + import BlockScoutWeb.WebRouter.Helpers, only: [transaction_state_path: 3] + import BlockScoutWeb.WeiHelpers, only: [format_wei_value: 2] + import EthereumJSONRPC, only: [integer_to_quantity: 1] + alias Explorer.Chain.Wei + alias EthereumJSONRPC.Blocks + + describe "GET index/3" do + test "loads existing transaction", %{conn: conn} do + transaction = insert(:transaction) + conn = get(conn, transaction_state_path(conn, :index, transaction.hash)) + + assert html_response(conn, 200) + end + + test "with missing transaction", %{conn: conn} do + hash = transaction_hash() + conn = get(conn, transaction_state_path(BlockScoutWeb.Endpoint, :index, hash)) + + assert html_response(conn, 404) + end + + test "with invalid transaction hash", %{conn: conn} do + conn = get(conn, transaction_state_path(BlockScoutWeb.Endpoint, :index, "nope")) + + assert html_response(conn, 422) + end + + test "with duplicated from, to or miner fields", %{conn: conn} do + address = insert(:address) + + insert(:block) + block = insert(:block, miner: address) + + insert(:fetched_balance, + address_hash: address.hash, + value: 1_000_000, + block_number: block.number - 1 + ) + + transaction = insert(:transaction, from_address: address, to_address: address) |> with_block(block, status: :ok) + + conn = get(conn, transaction_state_path(conn, :index, transaction), %{type: "JSON"}) + {:ok, %{"items" => items}} = conn.resp_body |> Poison.decode() + + assert(items |> Enum.filter(fn item -> item != nil end) |> length() == 1) + end + + test "returns fetched state changes for the transaction with token transfer", %{conn: conn} do + block = insert(:block) + address_a = insert(:address) + address_b = insert(:address) + token = insert(:token, type: "ERC-20") + + insert(:fetched_balance, + address_hash: address_a.hash, + value: 1_000_000_000_000_000_000, + block_number: block.number + ) + + insert(:fetched_balance, + address_hash: address_b.hash, + value: 2_000_000_000_000_000_000, + block_number: block.number + ) + + transaction = + :transaction + |> insert(from_address: address_a, to_address: address_b, value: 1000) + |> with_block(status: :ok) + + insert(:fetched_balance, + address_hash: transaction.block.miner_hash, + value: 2_500_000, + block_number: block.number + ) + + token_transfer = + insert(:token_transfer, + transaction: transaction, + block: transaction.block, + block_number: transaction.block_number, + token: token, + token_contract_address: token.contract_address + ) + + insert( + :token_balance, + address: token_transfer.from_address, + token: token, + token_contract_address_hash: token.contract_address_hash, + value: 3_000_000, + block_number: block.number + ) + + insert( + :token_balance, + address: token_transfer.to_address, + token: token, + token_contract_address_hash: token.contract_address_hash, + value: 1000, + block_number: block.number + ) + + # to check if we can display transaction overview + get(conn, transaction_state_path(conn, :index, transaction)) + conn = get(conn, transaction_state_path(conn, :index, transaction), %{type: "JSON"}) + + {:ok, %{"items" => items}} = conn.resp_body |> Poison.decode() + full_text = Enum.join(items) + + assert(String.contains?(full_text, format_wei_value(%Wei{value: Decimal.new(1, 1, 18)}, :ether))) + + assert(String.contains?(full_text, format_wei_value(%Wei{value: Decimal.new(1, 2, 18)}, :ether))) + + assert(length(items) == 5) + end + + test "fetch coin balances if needed", %{conn: conn} do + EthereumJSONRPC.Mox + |> stub(:json_rpc, fn + [%{id: id, method: "eth_getBalance", params: _}], _options -> + {:ok, [%{id: id, result: integer_to_quantity(123)}]} + + [%{id: id, method: "eth_getBlockByNumber", params: _}], _options -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: %{ + "author" => "0x0000000000000000000000000000000000000000", + "difficulty" => "0x20000", + "extraData" => "0x", + "gasLimit" => "0x663be0", + "gasUsed" => "0x0", + "hash" => "0x5b28c1bfd3a15230c9a46b399cd0f9a6920d432e85381cc6a140b06e8410112f", + "logsBloom" => + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "miner" => "0x0000000000000000000000000000000000000000", + "number" => integer_to_quantity(1), + "parentHash" => "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "sealFields" => [ + "0x80", + "0xb8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + "sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "signature" => + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "size" => "0x215", + "stateRoot" => "0xfad4af258fd11939fae0c6c6eec9d340b1caac0b0196fd9a1bc3f489c5bf00b3", + "step" => "0", + "timestamp" => "0x0", + "totalDifficulty" => "0x20000", + "transactions" => [], + "transactionsRoot" => "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles" => [] + } + } + ]} + end) + + insert(:block) + insert(:block) + address_a = insert(:address) + address_b = insert(:address) + + transaction = + :transaction + |> insert(from_address: address_a, to_address: address_b, value: 1000) + |> with_block(status: :ok) + + conn = get(conn, transaction_state_path(conn, :index, transaction), %{type: "JSON"}) + + {:ok, %{"items" => items}} = conn.resp_body |> Poison.decode() + full_text = Enum.join(items) + + assert(String.contains?(full_text, format_wei_value(%Wei{value: Decimal.new(123)}, :ether))) + assert(length(items) == 3) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs new file mode 100644 index 0000000..6b789a2 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/transaction_token_transfer_controller_test.exs @@ -0,0 +1,217 @@ +defmodule BlockScoutWeb.TransactionTokenTransferControllerTest do + use BlockScoutWeb.ConnCase + + import Mox + + import BlockScoutWeb.WebRouter.Helpers, only: [transaction_token_transfer_path: 3] + + alias Explorer.ExchangeRates.Token + + describe "GET index/3" do + test "load token transfers", %{conn: conn} do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn %{id: _id, method: "net_version", params: []}, _options -> + {:ok, "100"} + end) + + transaction = insert(:transaction) + token_transfer = insert(:token_transfer, transaction: transaction) + + conn = get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash)) + + assigned_token_transfer = List.first(conn.assigns.transaction.token_transfers) + + assert {assigned_token_transfer.transaction_hash, assigned_token_transfer.log_index} == + {token_transfer.transaction_hash, token_transfer.log_index} + end + + test "with missing transaction", %{conn: conn} do + hash = transaction_hash() + conn = get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, hash)) + + assert html_response(conn, 404) + end + + test "with invalid transaction hash", %{conn: conn} do + conn = get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, "nope")) + + assert html_response(conn, 422) + end + + test "includes transaction data", %{conn: conn} do + block = insert(:block, %{number: 777}) + + transaction = + :transaction + |> insert() + |> with_block(block) + + conn = get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash)) + + assert html_response(conn, 200) + assert conn.assigns.transaction.hash == transaction.hash + end + + test "includes token transfers for the transaction", %{conn: conn} do + transaction = insert(:transaction) |> with_block() + + insert(:token_transfer, transaction: transaction, block: transaction.block, block_number: transaction.block_number) + + insert(:token_transfer, transaction: transaction, block: transaction.block, block_number: transaction.block_number) + + path = transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash) + + conn = get(conn, path, %{type: "JSON"}) + + assert json_response(conn, 200) + + {:ok, %{"items" => items}} = conn.resp_body |> Poison.decode() + + assert Enum.count(items) == 2 + end + + test "includes USD exchange rate value for address in assigns", %{conn: conn} do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn %{id: _id, method: "net_version", params: []}, _options -> + {:ok, "100"} + end) + + transaction = insert(:transaction) + + conn = get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash)) + + assert %Token{} = conn.assigns.exchange_rate + end + + test "returns next page of results based on last seen token transfer", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + insert(:token_transfer, transaction: transaction, block_number: 1000, log_index: 1) + + Enum.each(2..5, fn item -> + insert(:token_transfer, transaction: transaction, block_number: item + 1001, log_index: item + 1) + end) + + conn = + get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash), %{ + "block_number" => "1000", + "index" => "1", + "type" => "JSON" + }) + + {:ok, %{"items" => items}} = conn.resp_body |> Poison.decode() + + refute Enum.count(items) == 3 + end + + test "next_page_path exists if not on last page", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + 1..51 + |> Enum.map(fn log_index -> + insert( + :token_transfer, + transaction: transaction, + log_index: log_index, + block: transaction.block, + block_number: transaction.block_number + ) + end) + + conn = + get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash), %{type: "JSON"}) + + {:ok, %{"next_page_path" => path}} = conn.resp_body |> Poison.decode() + + assert path + end + + test "next_page_path is empty if on last page", %{conn: conn} do + transaction = + :transaction + |> insert() + |> with_block() + + 1..2 + |> Enum.map(fn log_index -> + insert( + :token_transfer, + transaction: transaction, + log_index: log_index, + block_number: transaction.block_number, + block: transaction.block + ) + end) + + conn = + get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash), %{type: "JSON"}) + + {:ok, %{"next_page_path" => path}} = conn.resp_body |> Poison.decode() + + refute path + end + + test "preloads to_address smart contract verified", %{conn: conn} do + EthereumJSONRPC.Mox + |> expect( + :json_rpc, + fn %{ + id: _id, + method: "eth_getStorageAt", + params: [ + _, + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end + ) + |> expect( + :json_rpc, + fn %{ + id: _id, + method: "eth_getStorageAt", + params: [ + _, + "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end + ) + |> expect(:json_rpc, fn %{ + id: 0, + method: "eth_getStorageAt", + params: [ + _, + "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3", + "latest" + ] + }, + _options -> + {:ok, "0x0000000000000000000000000000000000000000000000000000000000000000"} + end) + |> expect(:json_rpc, fn %{id: _id, method: "net_version", params: []}, _options -> + {:ok, "100"} + end) + + transaction = insert(:transaction_to_verified_contract) + + conn = get(conn, transaction_token_transfer_path(BlockScoutWeb.Endpoint, :index, transaction.hash)) + + assert html_response(conn, 200) + assert conn.assigns.transaction.hash == transaction.hash + assert conn.assigns.transaction.to_address.smart_contract != nil + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/controllers/verified_contracts_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/verified_contracts_controller_test.exs new file mode 100644 index 0000000..dd5327c --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/verified_contracts_controller_test.exs @@ -0,0 +1,155 @@ +defmodule BlockScoutWeb.VerifiedContractsControllerTest do + use BlockScoutWeb.ConnCase + + import BlockScoutWeb.WebRouter.Helpers, only: [verified_contracts_path: 2, verified_contracts_path: 3] + + alias Explorer.Chain.SmartContract + + alias Explorer.Chain.Cache.{ + ContractsCounter, + NewContractsCounter, + NewVerifiedContractsCounter, + VerifiedContractsCounter + } + + describe "GET index/2" do + test "returns 200", %{conn: conn} do + start_supervised!(ContractsCounter) + ContractsCounter.consolidate() + start_supervised!(NewContractsCounter) + NewContractsCounter.consolidate() + start_supervised!(NewVerifiedContractsCounter) + NewVerifiedContractsCounter.consolidate() + start_supervised!(VerifiedContractsCounter) + VerifiedContractsCounter.consolidate() + + conn = get(conn, verified_contracts_path(conn, :index)) + + assert html_response(conn, 200) + end + + test "returns all contracts", %{conn: conn} do + insert_list(4, :smart_contract) + + conn = get(conn, verified_contracts_path(conn, :index), %{"type" => "JSON"}) + + items = Map.get(json_response(conn, 200), "items") + + assert length(items) == 4 + end + + test "returns next page of results based on last verified contract", %{conn: conn} do + insert_list(50, :smart_contract) + + contract = insert(:smart_contract) + + conn = + get(conn, verified_contracts_path(conn, :index), %{ + "type" => "JSON", + "smart_contract_id" => Integer.to_string(contract.id) + }) + + items = Map.get(json_response(conn, 200), "items") + + assert length(items) == 50 + end + + test "next_page_path exist if not on last page", %{conn: conn} do + %SmartContract{id: id} = + 60 + |> insert_list(:smart_contract) + |> Enum.fetch!(10) + + conn = get(conn, verified_contracts_path(conn, :index), %{"type" => "JSON"}) + + expected_path = + verified_contracts_path(conn, :index, %{ + smart_contract_id: id, + items_count: "50" + }) + + assert Map.get(json_response(conn, 200), "next_page_path") == expected_path + end + + test "next_page_path is empty if on last page", %{conn: conn} do + insert(:smart_contract) + + conn = get(conn, verified_contracts_path(conn, :index), %{"type" => "JSON"}) + + refute conn |> json_response(200) |> Map.get("next_page_path") + end + + test "returns solidity contracts", %{conn: conn} do + insert(:smart_contract, is_vyper_contract: true) + %SmartContract{address_hash: solidity_hash} = insert(:smart_contract, is_vyper_contract: false) + + path = + verified_contracts_path(conn, :index, %{ + "filter" => "solidity", + "type" => "JSON" + }) + + conn = get(conn, path) + + [smart_contracts_tile] = json_response(conn, 200)["items"] + + assert String.contains?(smart_contracts_tile, "data-identifier-hash=\"#{to_string(solidity_hash)}\"") + end + + test "returns vyper contract", %{conn: conn} do + %SmartContract{address_hash: vyper_hash} = insert(:smart_contract, is_vyper_contract: true) + insert(:smart_contract, is_vyper_contract: false) + + path = + verified_contracts_path(conn, :index, %{ + "filter" => "vyper", + "type" => "JSON" + }) + + conn = get(conn, path) + + [smart_contracts_tile] = json_response(conn, 200)["items"] + + assert String.contains?(smart_contracts_tile, "data-identifier-hash=\"#{to_string(vyper_hash)}\"") + end + + test "returns search results by name", %{conn: conn} do + insert(:smart_contract) + insert(:smart_contract) + insert(:smart_contract) + contract_name = "qwertyufhgkhiop" + %SmartContract{address_hash: hash} = insert(:smart_contract, name: contract_name) + + path = + verified_contracts_path(conn, :index, %{ + "search" => contract_name, + "type" => "JSON" + }) + + conn = get(conn, path) + + [smart_contracts_tile] = json_response(conn, 200)["items"] + + assert String.contains?(smart_contracts_tile, "data-identifier-hash=\"#{to_string(hash)}\"") + end + + test "returns search results by address", %{conn: conn} do + insert(:smart_contract) + insert(:smart_contract) + insert(:smart_contract) + %SmartContract{address_hash: hash} = insert(:smart_contract) + + path = + verified_contracts_path(conn, :index, %{ + "search" => to_string(hash), + "type" => "JSON" + }) + + conn = get(conn, path) + + [smart_contracts_tile] = json_response(conn, 200)["items"] + + assert String.contains?(smart_contracts_tile, "data-identifier-hash=\"#{to_string(hash)}\"") + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/address_contract_verification_test.exs b/apps/block_scout_web/test/block_scout_web/features/address_contract_verification_test.exs new file mode 100644 index 0000000..1a82a6d --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/address_contract_verification_test.exs @@ -0,0 +1,70 @@ +defmodule BlockScoutWeb.AddressContractVerificationTest do + use BlockScoutWeb.FeatureCase, async: false + + alias BlockScoutWeb.{AddressContractPage, ContractVerifyPage} + alias Explorer.Factory + alias Plug.Conn + + setup do + bypass = Bypass.open() + + Application.put_env(:explorer, :solc_bin_api_url, "http://localhost:#{bypass.port}") + + {:ok, bypass: bypass} + end + + # wallaby with chrome headles always fails this test + @tag :skip + test "users validates smart contract", %{session: session, bypass: bypass} do + Bypass.expect(bypass, fn conn -> Conn.resp(conn, 200, solc_bin_versions()) end) + + %{name: name, source_code: source_code, bytecode: bytecode, version: version} = Factory.contract_code_info() + + transaction = :transaction |> insert() |> with_block() + address = insert(:address, contract_code: bytecode) + + insert( + :internal_transaction_create, + created_contract_address: address, + created_contract_code: bytecode, + index: 0, + transaction: transaction + ) + + session + |> AddressContractPage.visit_page(address) + |> AddressContractPage.click_verify_and_publish() + |> ContractVerifyPage.fill_form(%{ + contract_name: name, + version: version, + optimization: false, + source_code: source_code, + evm_version: "byzantium" + }) + |> ContractVerifyPage.verify_and_publish() + + assert AddressContractPage.on_page?(session, address) + end + + test "with invalid data shows error messages", %{session: session, bypass: bypass} do + Bypass.expect(bypass, fn conn -> Conn.resp(conn, 200, solc_bin_versions()) end) + + address = insert(:address) + + session + |> ContractVerifyPage.visit_page(address) + |> ContractVerifyPage.fill_form(%{ + contract_name: "name", + version: "default", + optimization: "true", + source_code: "", + evm_version: "byzantium" + }) + |> ContractVerifyPage.verify_and_publish() + |> ContractVerifyPage.has_message?("There was an error validating your contract, please try again.") + end + + defp solc_bin_versions do + File.read!("./test/support/fixture/smart_contract/solc_bin.json") + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/address_contract_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/address_contract_page.ex new file mode 100644 index 0000000..2930e35 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/pages/address_contract_page.ex @@ -0,0 +1,23 @@ +defmodule BlockScoutWeb.AddressContractPage do + @moduledoc false + + use Wallaby.DSL + + import Wallaby.Query, only: [css: 1] + + def on_page?(session, address) do + current_path(session) =~ address_contract_path(address) + end + + def click_verify_and_publish(session) do + click(session, css("[data-test='verify_and_publish']")) + end + + def visit_page(session, address) do + visit(session, address_contract_path(address)) + end + + defp address_contract_path(address) do + "/address/#{address.hash}/contracts" + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/address_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/address_page.ex new file mode 100644 index 0000000..99b66dd --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/pages/address_page.ex @@ -0,0 +1,178 @@ +defmodule BlockScoutWeb.AddressPage do + @moduledoc false + + use Wallaby.DSL + import Wallaby.Query, only: [css: 1, css: 2] + alias Explorer.Chain.{Address, InternalTransaction, Hash, Transaction, Token} + + def apply_filter(session, direction) do + session + |> click(css("[data-test='filter_dropdown']", text: "Filter: All")) + |> click(css("[data-test='filter_option']", text: direction)) + end + + def balance do + css("[data-test='address_balance']") + end + + def token_balance(count: count) do + css("[data-dropdown-token-balance-test]", count: count) + end + + def token_balance_counter(text) do + css("[data-tokens-count]", text: "#{text} tokens") + end + + def token_type(count: count) do + css("[data-token-type]", count: count) + end + + def token_type_count(type: type, text: text) do + css("[data-number-of-tokens-by-type='#{type}']", text: text) + end + + def address(%Address{} = address) do + css("[data-address-hash='#{address}']", text: to_string(address)) + end + + def contract_creator do + css("[data-test='address_contract_creator']") + end + + def click_internal_transactions(session) do + click(session, css("[data-test='internal_transactions_tab_link']")) + end + + def click_tokens(session) do + click(session, css("[data-test='tokens_tab_link']")) + end + + def click_coin_balance_history(session) do + click(session, css("[data-test='coin_balance_tab_link']")) + end + + def click_balance_dropdown_toggle(session) do + click(session, css("[data-dropdown-toggle]")) + end + + def fill_balance_dropdown_search(session, text) do + fill_in(session, css("[data-filter-dropdown-tokens]"), with: text) + end + + def click_outside_of_the_dropdown(session) do + click(session, css("[data-test='outside_of_dropdown']")) + end + + def click_token_transfers(session, %Token{contract_address_hash: contract_address_hash}) do + click(session, css("[data-test='token_transfers_#{contract_address_hash}']")) + end + + def click_show_pending_transactions(session) do + click(session, css("[data-selector='pending-transactions-open']")) + end + + def coin_balances(count: count) do + css("[data-test='coin_balance']", count: count) + end + + def contract_creation(%InternalTransaction{created_contract_address_hash: hash}) do + css("[data-address-hash='#{hash}']", text: to_string(hash)) + end + + def detail_hash(address) do + css("[data-test='address_detail_hash']", text: to_string(address)) + end + + def internal_transaction(%InternalTransaction{transaction_hash: transaction_hash, index: index}) do + css( + "[data-test='internal_transaction']" <> + "[data-internal-transaction-transaction-hash='#{transaction_hash}']" <> + "[data-internal-transaction-index='#{index}']" + ) + end + + def internal_transactions(count: count) do + css("[data-test='internal_transaction']", count: count) + end + + def internal_transaction_address_link( + %InternalTransaction{transaction_hash: transaction_hash, index: index, from_address_hash: address_hash}, + :from + ) do + checksum = Address.checksum(address_hash) + + css( + "[data-internal-transaction-transaction-hash='#{transaction_hash}'][data-internal-transaction-index='#{index}']" <> + " [data-test='address_hash_link']" <> " [data-address-hash='#{checksum}']" + ) + end + + def internal_transaction_address_link( + %InternalTransaction{transaction_hash: transaction_hash, index: index, to_address_hash: address_hash}, + :to + ) do + css( + "[data-internal-transaction-transaction-hash='#{transaction_hash}'][data-internal-transaction-index='#{index}']" <> + " [data-test='address_hash_link']" <> " [data-address-hash='#{address_hash}']" + ) + end + + def pending_transaction(%Transaction{hash: transaction_hash}), do: pending_transaction(transaction_hash) + + def pending_transaction(transaction_hash) do + css("[data-selector='pending-transactions-list'] [data-transaction-hash='#{transaction_hash}']") + end + + def transaction(%Transaction{hash: transaction_hash}), do: transaction(transaction_hash) + + def transaction(%Hash{} = hash) do + hash + |> to_string() + |> transaction() + end + + def transaction(transaction_hash) do + css("[data-identifier-hash='#{transaction_hash}']") + end + + def transaction_address_link(%Transaction{hash: hash, from_address_hash: address_hash}, :from) do + checksum = Address.checksum(address_hash) + + css("[data-identifier-hash='#{hash}'] [data-test='address_hash_link'] [data-address-hash='#{checksum}']") + end + + def transaction_address_link(%Transaction{hash: hash, to_address_hash: address_hash}, :to) do + checksum = Address.checksum(address_hash) + + css("[data-identifier-hash='#{hash}'] [data-test='address_hash_link'] [data-address-hash='#{checksum}']") + end + + def transaction_status(%Transaction{hash: transaction_hash}) do + css("[data-identifier-hash='#{transaction_hash}'] [data-test='transaction_status']") + end + + def token_transfer(%Transaction{hash: transaction_hash}, %Address{} = address, count: count) do + css( + "[data-identifier-hash='#{transaction_hash}'] [data-test='token_transfer'] [data-address-hash='#{address}']", + count: count + ) + end + + def token_transfers(%Transaction{hash: transaction_hash}, count: count) do + css("[data-identifier-hash='#{transaction_hash}'] [data-test='token_transfer']", count: count) + end + + def token_transfers_expansion(%Transaction{hash: transaction_hash}) do + css("[data-identifier-hash='#{transaction_hash}'] [data-test='token_transfers_expansion']") + end + + def visit_page(session, %Address{hash: address_hash}), do: visit_page(session, address_hash) + + def visit_page(session, address_hash) do + visit(session, "/address/#{address_hash}") + end + + def visit_page(session) do + visit(session, "/accounts") + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/app_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/app_page.ex new file mode 100644 index 0000000..98b934a --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/pages/app_page.ex @@ -0,0 +1,19 @@ +defmodule BlockScoutWeb.AppPage do + @moduledoc false + + use Wallaby.DSL + + import Wallaby.Query, only: [css: 1, css: 2] + + def visit_page(session) do + visit(session, "/") + end + + def indexed_status(text) do + css("[data-selector='indexed-status'] [data-indexed-ratio]", text: text) + end + + def still_indexing?() do + css("[data-selector='indexed-status']") + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/block_list_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/block_list_page.ex new file mode 100644 index 0000000..f59cf42 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/pages/block_list_page.ex @@ -0,0 +1,33 @@ +defmodule BlockScoutWeb.BlockListPage do + @moduledoc false + + use Wallaby.DSL + + import Wallaby.Query, only: [css: 1, css: 2] + + alias Explorer.Chain.Block + + def visit_page(session) do + visit(session, "/blocks") + end + + def visit_reorgs_page(session) do + visit(session, "/reorgs") + end + + def visit_uncles_page(session) do + visit(session, "/uncles") + end + + def block(%Block{number: block_number}) do + css("[data-block-number='#{block_number}']") + end + + def place_holder_blocks(count) do + css("[data-selector='place-holder']", count: count) + end + + def blocks(count) do + css("[data-selector='block-tile']", count: count) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/block_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/block_page.ex new file mode 100644 index 0000000..18289e0 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/pages/block_page.ex @@ -0,0 +1,50 @@ +defmodule BlockScoutWeb.BlockPage do + @moduledoc false + + use Wallaby.DSL + + import Wallaby.Query, only: [css: 1, css: 2] + + alias Explorer.Chain.{Address, Block, InternalTransaction, Transaction} + + def contract_creation(%InternalTransaction{created_contract_address_hash: hash}) do + checksum = Address.checksum(hash) + css("[data-address-hash='#{checksum}']") + end + + def detail_number(%Block{number: block_number}) do + css("[data-test='block_detail_number']", text: to_string(block_number)) + end + + def page_type(type) do + css("[data-test='detail_type']", text: type) + end + + def token_transfers(%Transaction{hash: transaction_hash}, count: count) do + css("[data-identifier-hash='#{transaction_hash}'] [data-test='token_transfer']", count: count) + end + + def token_transfers_expansion(%Transaction{hash: transaction_hash}) do + css("[data-identifier-hash='#{transaction_hash}'] [data-test='token_transfers_expansion']") + end + + def transaction(%Transaction{hash: transaction_hash}) do + css("[data-identifier-hash='#{transaction_hash}']") + end + + def transaction_status(%Transaction{hash: transaction_hash}) do + css("[data-identifier-hash='#{transaction_hash}'] [data-test='transaction_status']") + end + + def uncle_link(%Block{hash: hash}) do + css("[data-test='uncle_link'][data-uncle-hash='#{hash}']") + end + + def visit_page(session, %Block{number: block_number, consensus: true}) do + visit(session, "/blocks/#{block_number}") + end + + def visit_page(session, %Block{hash: hash}) do + visit(session, "/blocks/#{hash}") + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/chain_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/chain_page.ex new file mode 100644 index 0000000..7781210 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/pages/chain_page.ex @@ -0,0 +1,48 @@ +defmodule BlockScoutWeb.ChainPage do + @moduledoc false + + use Wallaby.DSL + + import Wallaby.Query, only: [css: 1, css: 2] + + alias Explorer.Chain.{Address, Block, Transaction} + + def block(%Block{number: block_number}) do + css("[data-block-number='#{block_number}']") + end + + def blocks(count: count) do + css("[data-selector='chain-block']", count: count) + end + + def contract_creation(%Transaction{created_contract_address_hash: hash}) do + checksum = Address.checksum(hash) + css("[data-test='contract-creation'] [data-address-hash='#{checksum}']") + end + + def place_holder_blocks(count) do + css("[data-selector='place-holder']", count: count) + end + + def search(session, text) do + session + |> fill_in(css("[data-test='search_input']"), with: text) + |> send_keys([:enter]) + end + + def token_transfers(%Transaction{hash: transaction_hash}, count: count) do + css("[data-identifier-hash='#{transaction_hash}'] [data-test='token_transfer']", count: count) + end + + def token_transfers_expansion(%Transaction{hash: transaction_hash}) do + css("[data-identifier-hash='#{transaction_hash}'] [data-test='token_transfers_expansion']") + end + + def transactions(count: count) do + css("[data-test='chain_transaction']", count: count) + end + + def visit_page(session) do + visit(session, "/") + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/contract_verify_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/contract_verify_page.ex new file mode 100644 index 0000000..f57a723 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/pages/contract_verify_page.ex @@ -0,0 +1,58 @@ +defmodule BlockScoutWeb.ContractVerifyPage do + @moduledoc false + + use Wallaby.DSL + + import Wallaby.Query + + def visit_page(session, address_hash) do + visit(session, "/address/#{address_hash}/verify-via-flattened-code/new") + end + + def fill_form(session, %{ + contract_name: contract_name, + version: version, + optimization: optimization, + source_code: source_code, + evm_version: evm_version + }) do + session + |> fill_in(css("[data-test='contract_name']"), with: contract_name) + |> fill_in(text_field("Enter the Solidity Contract Code"), with: source_code) + + case version do + nil -> nil + _ -> click(session, option(version)) + end + + case evm_version do + nil -> nil + _ -> click(session, option(evm_version)) + end + + case optimization do + true -> + click(session, radio_button("Yes")) + + false -> + click(session, radio_button("No")) + + _ -> + nil + end + + session + end + + def validation_error do + css("[data-test='contract-source-code-error']") + end + + def has_message?(session, message) do + String.contains?(page_source(session), message) + end + + def verify_and_publish(session) do + click(session, button("Verify & publish")) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/token_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/token_page.ex new file mode 100644 index 0000000..23eb418 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/pages/token_page.ex @@ -0,0 +1,27 @@ +defmodule BlockScoutWeb.TokenPage do + @moduledoc false + + use Wallaby.DSL + import Wallaby.Query, only: [css: 1, css: 2] + alias Explorer.Chain.{Address} + + def visit_page(session, %Address{hash: address_hash}) do + visit_page(session, address_hash) + end + + def visit_page(session, contract_address_hash) do + visit(session, "tokens/#{contract_address_hash}/token-holders") + end + + def token_holders_tab(count: count) do + css("[data-test='token_holders_tab']", count: count) + end + + def click_tokens_holders(session) do + click(session, css("[data-test='token_holders_tab']")) + end + + def token_holders(count: count) do + css("[data-test='token_holders']", count: count) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/transaction_list_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/transaction_list_page.ex new file mode 100644 index 0000000..9b3c34f --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/pages/transaction_list_page.ex @@ -0,0 +1,33 @@ +defmodule BlockScoutWeb.TransactionListPage do + @moduledoc false + + use Wallaby.DSL + + import Wallaby.Query, only: [css: 1, css: 2] + + alias Explorer.Chain.Transaction + + def click_transaction(session, %Transaction{hash: transaction_hash}) do + click(session, css("[data-identifier-hash='#{transaction_hash}'] [data-test='transaction_hash_link']")) + end + + def contract_creation(%Transaction{hash: hash}) do + css("[data-identifier-hash='#{hash}'] [data-test='transaction_type']", text: "Contract Creation") + end + + def transaction(%Transaction{hash: transaction_hash}) do + css("[data-identifier-hash='#{transaction_hash}']") + end + + def transaction_status(%Transaction{hash: transaction_hash}) do + css("[data-identifier-hash='#{transaction_hash}'] [data-test='transaction_status']") + end + + def visit_page(session) do + visit(session, "/txs") + end + + def visit_pending_transactions_page(session) do + visit(session, "/pending-transactions") + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/transaction_logs_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/transaction_logs_page.ex new file mode 100644 index 0000000..3969cd5 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/pages/transaction_logs_page.ex @@ -0,0 +1,23 @@ +defmodule BlockScoutWeb.TransactionLogsPage do + @moduledoc false + + use Wallaby.DSL + + import Wallaby.Query, only: [css: 1, css: 2] + import BlockScoutWeb.WebRouter.Helpers, only: [transaction_log_path: 3] + + alias Explorer.Chain.Transaction + alias BlockScoutWeb.Endpoint + + def logs(count: count) do + css("[data-test='transaction_log']", count: count) + end + + def visit_page(session, %Transaction{} = transaction) do + visit(session, transaction_log_path(Endpoint, :index, transaction)) + end + + def click_address(session, address) do + click(session, css("[data-test='log_address_link'][data-address-hash='#{address}']")) + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/transaction_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/transaction_page.ex new file mode 100644 index 0000000..19fe499 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/pages/transaction_page.ex @@ -0,0 +1,25 @@ +defmodule BlockScoutWeb.TransactionPage do + @moduledoc false + + use Wallaby.DSL + + import Wallaby.Query, only: [css: 1, css: 2] + + alias Explorer.Chain.{Transaction, Hash} + + def click_logs(session) do + click(session, css("[data-test='transaction_logs_link']")) + end + + def detail_hash(%Transaction{hash: transaction_hash}) do + css("[data-test='transaction_detail_hash']", text: Hash.to_string(transaction_hash)) + end + + def is_pending() do + css("[data-selector='block-number']", text: "Pending") + end + + def visit_page(session, %Transaction{hash: transaction_hash}) do + visit(session, "/tx/#{transaction_hash}") + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/viewing_addresses_test.exs b/apps/block_scout_web/test/block_scout_web/features/viewing_addresses_test.exs new file mode 100644 index 0000000..6a8fd91 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/viewing_addresses_test.exs @@ -0,0 +1,496 @@ +defmodule BlockScoutWeb.ViewingAddressesTest do + use BlockScoutWeb.FeatureCase, + # Because ETS tables is shared for `Explorer.Counters.*` + async: false + + alias Explorer.Counters.AddressesCounter + alias BlockScoutWeb.{AddressPage, AddressView, Notifier} + + setup do + Application.put_env(:block_scout_web, :checksum_address_hashes, false) + + block = insert(:block, number: 42) + + lincoln = insert(:address, fetched_coin_balance: 5) + taft = insert(:address, fetched_coin_balance: 5) + + from_taft = + :transaction + |> insert(from_address: taft, to_address: lincoln) + |> with_block(block) + + from_lincoln = + :transaction + |> insert(from_address: lincoln, to_address: taft) + |> with_block(block) + + lincoln_reward = + :reward + |> insert( + address_hash: lincoln.hash, + block_hash: block.hash, + address_type: :emission_funds + ) + + taft_reward = + :reward + |> insert( + address_hash: taft.hash, + block_hash: block.hash, + address_type: :validator + ) + + on_exit(fn -> + Application.put_env(:block_scout_web, :checksum_address_hashes, true) + end) + + {:ok, + %{ + addresses: %{lincoln: lincoln, taft: taft}, + block: block, + rewards: {lincoln_reward, taft_reward}, + transactions: %{from_lincoln: from_lincoln, from_taft: from_taft} + }} + end + + describe "viewing top addresses" do + setup do + addresses = Enum.map(150..101, &insert(:address, fetched_coin_balance: &1)) + + {:ok, %{addresses: addresses}} + end + + test "lists top addresses", %{session: session, addresses: addresses} do + [first_address | _] = addresses + [last_address | _] = Enum.reverse(addresses) + + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + session + |> AddressPage.visit_page() + |> assert_has(AddressPage.address(first_address)) + |> assert_has(AddressPage.address(last_address)) + end + end + + test "viewing address overview information", %{session: session} do + address = insert(:address, fetched_coin_balance: 500) + + session + |> AddressPage.visit_page(address) + |> assert_text(AddressPage.balance(), "0.0000000000000005 ETH") + end + + describe "viewing contract creator" do + test "see the contract creator and transaction links", %{session: session} do + address = insert(:address) + contract = insert(:contract_address) + transaction = insert(:transaction, from_address: address, created_contract_address: contract) |> with_block() + + internal_transaction = + insert( + :internal_transaction_create, + index: 1, + transaction: transaction, + from_address: address, + created_contract_address: contract, + block_hash: transaction.block_hash, + block_index: 1 + ) + + address_hash = AddressView.trimmed_hash(address.hash) + transaction_hash = AddressView.trimmed_hash(transaction.hash) + + session + |> AddressPage.visit_page(internal_transaction.created_contract_address) + |> assert_text(AddressPage.contract_creator(), "#{address_hash} at #{transaction_hash}") + end + + test "see the contract creator and transaction links even when the creator is another contract", %{session: session} do + lincoln = insert(:address) + contract = insert(:contract_address) + transaction = insert(:transaction) |> with_block() + another_contract = insert(:contract_address) + + insert( + :internal_transaction, + index: 1, + transaction: transaction, + from_address: lincoln, + to_address: contract, + created_contract_address: contract, + type: :call, + block_hash: transaction.block_hash, + block_index: 1 + ) + + internal_transaction = + insert( + :internal_transaction_create, + index: 2, + transaction: transaction, + from_address: contract, + created_contract_address: another_contract, + block_hash: transaction.block_hash, + block_index: 2 + ) + + contract_hash = AddressView.trimmed_hash(contract.hash) + transaction_hash = AddressView.trimmed_hash(transaction.hash) + + session + |> AddressPage.visit_page(internal_transaction.created_contract_address) + |> assert_text(AddressPage.contract_creator(), "#{contract_hash} at #{transaction_hash}") + end + end + + describe "viewing transactions" do + test "sees all addresses transactions by default", %{ + addresses: addresses, + session: session, + transactions: transactions + } do + session + |> AddressPage.visit_page(addresses.lincoln) + |> assert_has(AddressPage.transaction(transactions.from_taft)) + |> assert_has(AddressPage.transaction(transactions.from_lincoln)) + |> assert_has(AddressPage.transaction_status(transactions.from_lincoln)) + end + + test "can filter to only see transactions from an address", %{ + addresses: addresses, + session: session, + transactions: transactions + } do + session + |> AddressPage.visit_page(addresses.lincoln) + |> AddressPage.apply_filter("From") + |> assert_has(AddressPage.transaction(transactions.from_lincoln)) + |> refute_has(AddressPage.transaction(transactions.from_taft)) + end + + test "can filter to only see transactions to an address", %{ + addresses: addresses, + session: session, + transactions: transactions + } do + session + |> AddressPage.visit_page(addresses.lincoln) + |> AddressPage.apply_filter("To") + |> refute_has(AddressPage.transaction(transactions.from_lincoln)) + |> assert_has(AddressPage.transaction(transactions.from_taft)) + end + + test "only addresses not matching the page are links", %{ + addresses: addresses, + session: session, + transactions: transactions + } do + session + |> AddressPage.visit_page(addresses.lincoln) + |> assert_has(AddressPage.transaction_address_link(transactions.from_lincoln, :to)) + |> refute_has(AddressPage.transaction_address_link(transactions.from_lincoln, :from)) + end + + test "sees rewards to and from an address alongside transactions", %{ + addresses: addresses, + session: session, + transactions: transactions + } do + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, has_emission_funds: true) + + session + |> AddressPage.visit_page(addresses.lincoln) + |> assert_has(AddressPage.transaction(transactions.from_taft)) + |> assert_has(AddressPage.transaction(transactions.from_lincoln)) + + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, has_emission_funds: false) + end + end + + describe "viewing internal transactions" do + setup %{addresses: addresses, transactions: transactions} do + address = addresses.lincoln + transaction = transactions.from_lincoln + + internal_transaction_lincoln_to_address = + insert(:internal_transaction, + transaction: transaction, + to_address: address, + index: 1, + block_number: 7000, + transaction_index: 1, + block_hash: transaction.block_hash, + block_index: 1 + ) + + insert(:internal_transaction, + transaction: transaction, + from_address: address, + index: 2, + block_number: 8000, + transaction_index: 2, + block_hash: transaction.block_hash, + block_index: 2 + ) + + {:ok, %{internal_transaction_lincoln_to_address: internal_transaction_lincoln_to_address}} + end + + test "only addresses not matching the page are links", %{ + addresses: addresses, + internal_transaction_lincoln_to_address: internal_transaction, + session: session + } do + session + |> AddressPage.visit_page(addresses.lincoln) + |> AddressPage.click_internal_transactions() + |> assert_has(AddressPage.internal_transaction_address_link(internal_transaction, :from)) + |> refute_has(AddressPage.internal_transaction_address_link(internal_transaction, :to)) + end + + test "viewing new internal transactions via live update", %{addresses: addresses, session: session} do + transaction = + :transaction + |> insert(from_address: addresses.lincoln) + |> with_block(insert(:block, number: 7000)) + + session + |> AddressPage.visit_page(addresses.lincoln) + |> AddressPage.click_internal_transactions() + |> assert_has(AddressPage.internal_transactions(count: 2)) + + internal_transaction = + insert(:internal_transaction, + transaction: transaction, + index: 2, + from_address: addresses.lincoln, + block_number: transaction.block_number, + transaction_index: transaction.index, + block_hash: transaction.block_hash, + block_index: 2 + ) + + Notifier.handle_event({:chain_event, :internal_transactions, :realtime, [internal_transaction]}) + + session + |> assert_has(AddressPage.internal_transactions(count: 3)) + |> assert_has(AddressPage.internal_transaction(internal_transaction)) + end + + test "can filter to see internal transactions from an address only", %{ + addresses: addresses, + session: session + } do + block = insert(:block, number: 7000) + + from_lincoln = + :transaction + |> insert(from_address: addresses.lincoln) + |> with_block(block) + + from_taft = + :transaction + |> insert(from_address: addresses.taft) + |> with_block(block) + + insert(:internal_transaction, + transaction: from_lincoln, + index: 2, + from_address: addresses.lincoln, + block_number: from_lincoln.block_number, + transaction_index: from_lincoln.index, + block_hash: from_lincoln.block_hash, + block_index: 2 + ) + + session + |> AddressPage.visit_page(addresses.lincoln) + |> AddressPage.apply_filter("From") + |> assert_has(AddressPage.transaction(from_lincoln)) + |> refute_has(AddressPage.transaction(from_taft)) + end + + test "can filter to see internal transactions to an address only", %{ + addresses: addresses, + session: session + } do + block = insert(:block, number: 7000) + + from_lincoln = + :transaction + |> insert(to_address: addresses.lincoln) + |> with_block(block) + + from_taft = + :transaction + |> insert(to_address: addresses.taft) + |> with_block(block) + + insert(:internal_transaction, + transaction: from_lincoln, + index: 2, + from_address: addresses.lincoln, + block_number: from_lincoln.block_number, + transaction_index: from_lincoln.index, + block_hash: from_lincoln.block_hash, + block_index: 2 + ) + + session + |> AddressPage.visit_page(addresses.lincoln) + |> AddressPage.apply_filter("To") + |> assert_has(AddressPage.transaction(from_lincoln)) + |> refute_has(AddressPage.transaction(from_taft)) + end + end + + describe "viewing token transfers from a specific token" do + test "list token transfers related to the address", %{ + addresses: addresses, + block: block, + session: session + } do + lincoln = addresses.lincoln + taft = addresses.taft + + contract_address = insert(:contract_address) + token = insert(:token, contract_address: contract_address) + + transaction = + :transaction + |> insert(from_address: lincoln, to_address: contract_address) + |> with_block(block) + + insert( + :token_transfer, + from_address: lincoln, + to_address: taft, + transaction: transaction, + token_contract_address: contract_address + ) + + insert(:address_current_token_balance, address: lincoln, token_contract_address_hash: contract_address.hash) + + session + |> AddressPage.visit_page(lincoln) + |> AddressPage.click_tokens() + |> AddressPage.click_token_transfers(token) + |> assert_has(AddressPage.token_transfers(transaction, count: 1)) + |> assert_has(AddressPage.token_transfer(transaction, lincoln, count: 1)) + |> assert_has(AddressPage.token_transfer(transaction, taft, count: 1)) + |> refute_has(AddressPage.token_transfers_expansion(transaction)) + end + end + + describe "viewing token balances" do + setup do + block = insert(:block) + lincoln = insert(:address, fetched_coin_balance: 5, fetched_coin_balance_block_number: block.number) + taft = insert(:address, fetched_coin_balance: 5) + + contract_address = insert(:contract_address) + insert(:token, name: "atoken", symbol: "AT", contract_address: contract_address, type: "ERC-721") + + transaction = + :transaction + |> insert(from_address: lincoln, to_address: contract_address) + |> with_block(block) + + insert( + :token_transfer, + from_address: lincoln, + to_address: taft, + transaction: transaction, + token_contract_address: contract_address + ) + + insert(:address_current_token_balance, address: lincoln, token_contract_address_hash: contract_address.hash) + + contract_address_2 = insert(:contract_address) + insert(:token, name: "token2", symbol: "T2", contract_address: contract_address_2, type: "ERC-20") + + transaction_2 = + :transaction + |> insert(from_address: lincoln, to_address: contract_address_2) + |> with_block(block) + + insert( + :token_transfer, + from_address: lincoln, + to_address: taft, + transaction: transaction_2, + token_contract_address: contract_address_2 + ) + + insert(:address_current_token_balance, address: lincoln, token_contract_address_hash: contract_address_2.hash) + + {:ok, lincoln: lincoln} + end + + test "filter tokens balances by token name", %{session: session, lincoln: lincoln} do + next = + session + |> AddressPage.visit_page(lincoln) + + Process.sleep(2_000) + + next + |> AddressPage.click_balance_dropdown_toggle() + |> AddressPage.fill_balance_dropdown_search("ato") + |> assert_has(AddressPage.token_balance(count: 1)) + |> assert_has(AddressPage.token_type(count: 1)) + |> assert_has(AddressPage.token_type_count(type: "ERC-721", text: "1")) + end + + test "filter token balances by token symbol", %{session: session, lincoln: lincoln} do + next = + session + |> AddressPage.visit_page(lincoln) + + Process.sleep(2_000) + + next + |> AddressPage.click_balance_dropdown_toggle() + |> AddressPage.fill_balance_dropdown_search("T2") + |> assert_has(AddressPage.token_balance(count: 1)) + |> assert_has(AddressPage.token_type(count: 1)) + |> assert_has(AddressPage.token_type_count(type: "ERC-20", text: "1")) + end + + test "reset token balances filter when dropdown closes", %{session: session, lincoln: lincoln} do + next = + session + |> AddressPage.visit_page(lincoln) + + Process.sleep(2_000) + + next + |> AddressPage.click_balance_dropdown_toggle() + |> AddressPage.fill_balance_dropdown_search("ato") + |> AddressPage.click_outside_of_the_dropdown() + |> assert_has(AddressPage.token_balance_counter("2")) + end + end + + describe "viewing coin balance history" do + setup do + address = insert(:address, fetched_coin_balance: 5) + noon = Timex.now() |> Timex.beginning_of_day() |> Timex.set(hour: 12) + block = insert(:block, timestamp: noon) + block_one_day_ago = insert(:block, timestamp: Timex.shift(noon, days: -1)) + insert(:fetched_balance, address_hash: address.hash, value: 5, block_number: block.number) + insert(:fetched_balance, address_hash: address.hash, value: 10, block_number: block_one_day_ago.number) + + {:ok, address: address} + end + + test "see list of coin balances", %{session: session, address: address} do + session + |> AddressPage.visit_page(address) + |> AddressPage.click_coin_balance_history() + |> assert_has(AddressPage.coin_balances(count: 2)) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/viewing_app_test.exs b/apps/block_scout_web/test/block_scout_web/features/viewing_app_test.exs new file mode 100644 index 0000000..ad91898 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/viewing_app_test.exs @@ -0,0 +1,144 @@ +defmodule BlockScoutWeb.ViewingAppTest do + @moduledoc false + + # use BlockScoutWeb.FeatureCase, async: true + + # alias BlockScoutWeb.AppPage + # alias BlockScoutWeb.Counters.BlocksIndexedCounter + # alias Explorer.Counters.AddressesCounter + # alias Explorer.{Repo} + # alias Explorer.Chain.PendingBlockOperation + + # setup do + # start_supervised!(AddressesCounter) + # AddressesCounter.consolidate() + + # :ok + # end + + # describe "loading bar when indexing" do + # test "shows blocks indexed percentage", %{session: session} do + # [block | _] = + # for index <- 5..9 do + # insert(:block, number: index) + # end + + # :transaction + # |> insert() + # |> with_block(block) + + # assert Decimal.compare(Explorer.Chain.indexed_ratio_blocks(), Decimal.from_float(0.5)) == :eq + + # insert(:pending_block_operation, block_hash: block.hash, fetch_internal_transactions: true) + + # session + # |> AppPage.visit_page() + # |> assert_has(AppPage.indexed_status("50% Blocks Indexed")) + # end + + # test "shows tokens loading", %{session: session} do + # [block | _] = + # for index <- 0..9 do + # insert(:block, number: index) + # end + + # :transaction + # |> insert() + # |> with_block(block) + + # assert Decimal.compare(Explorer.Chain.indexed_ratio_blocks(), 1) == :eq + + # insert(:pending_block_operation, block_hash: block.hash, fetch_internal_transactions: true) + + # session + # |> AppPage.visit_page() + # |> assert_has(AppPage.indexed_status("Indexing Internal Transactions")) + # end + + # test "updates blocks indexed percentage", %{session: session} do + # [block | _] = + # for index <- 5..9 do + # insert(:block, number: index) + # end + + # :transaction + # |> insert() + # |> with_block(block) + + # BlocksIndexedCounter.calculate_blocks_indexed() + + # assert Decimal.compare(Explorer.Chain.indexed_ratio_blocks(), Decimal.from_float(0.5)) == :eq + + # insert(:pending_block_operation, block_hash: block.hash, fetch_internal_transactions: true) + + # session + # |> AppPage.visit_page() + # |> assert_has(AppPage.indexed_status("50% Blocks Indexed")) + + # insert(:block, number: 4) + + # BlocksIndexedCounter.calculate_blocks_indexed() + + # assert_has(session, AppPage.indexed_status("60% Blocks Indexed")) + # end + + # test "updates when blocks are fully indexed", %{session: session} do + # [block | _] = + # for index <- 1..9 do + # insert(:block, number: index) + # end + + # :transaction + # |> insert() + # |> with_block(block) + + # BlocksIndexedCounter.calculate_blocks_indexed() + + # assert Decimal.compare(Explorer.Chain.indexed_ratio(), Decimal.from_float(0.9)) == :eq + + # insert(:pending_block_operation, block_hash: block.hash, fetch_internal_transactions: true) + + # session + # |> AppPage.visit_page() + # |> assert_has(AppPage.indexed_status("90% Blocks Indexed")) + + # insert(:block, number: 0) + + # BlocksIndexedCounter.calculate_blocks_indexed() + + # assert_has(session, AppPage.indexed_status("Indexing Internal Transactions")) + # end + + # test "removes message when chain is indexed", %{session: session} do + # [block | _] = + # for index <- 0..9 do + # insert(:block, number: index) + # end + + # :transaction + # |> insert() + # |> with_block(block) + + # block_hash = block.hash + + # insert(:pending_block_operation, block_hash: block_hash, fetch_internal_transactions: true) + + # BlocksIndexedCounter.calculate_blocks_indexed() + + # assert Decimal.compare(Explorer.Chain.indexed_ratio_blocks(), 1) == :eq + + # session + # |> AppPage.visit_page() + # |> assert_has(AppPage.indexed_status("Indexing Internal Transactions")) + + # Repo.update_all( + # from(p in PendingBlockOperation, where: p.block_hash == ^block_hash), + # set: [fetch_internal_transactions: false] + # ) + + # BlocksIndexedCounter.calculate_blocks_indexed() + + # refute_has(session, AppPage.still_indexing?()) + # end + # end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/viewing_blocks_test.exs b/apps/block_scout_web/test/block_scout_web/features/viewing_blocks_test.exs new file mode 100644 index 0000000..c1f6688 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/viewing_blocks_test.exs @@ -0,0 +1,185 @@ +defmodule BlockScoutWeb.ViewingBlocksTest do + use BlockScoutWeb.FeatureCase, async: false + + alias BlockScoutWeb.{BlockListPage, BlockPage} + alias Explorer.Chain.Block + + setup do + timestamp = Timex.now() |> Timex.shift(hours: -1) + [oldest_block | _] = Enum.map(308..310, &insert(:block, number: &1, timestamp: timestamp, gas_used: 10)) + + newest_block = + insert(:block, %{ + gas_limit: 5_030_101, + gas_used: 1_010_101, + nonce: 123_456_789, + number: 311, + size: 9_999_999, + timestamp: timestamp + }) + + {:ok, first_shown_block: newest_block, last_shown_block: oldest_block} + end + + describe "block details page" do + test "show block detail page", %{session: session} do + block = insert(:block, number: 42) + + session + |> BlockPage.visit_page(block) + |> assert_has(BlockPage.detail_number(block)) + |> assert_has(BlockPage.page_type("Block Details")) + end + + test "block detail page has transactions", %{session: session} do + block = insert(:block, number: 42) + + transaction = + :transaction + |> insert() + |> with_block(block) + + session + |> BlockPage.visit_page(block) + |> assert_has(BlockPage.detail_number(block)) + |> assert_has(BlockPage.transaction(transaction)) + |> assert_has(BlockPage.transaction_status(transaction)) + end + + test "contract creation is shown for to_address in transaction list", %{session: session} do + block = insert(:block, number: 42) + + contract_address = insert(:contract_address) + + transaction = + :transaction + |> insert(to_address: nil) + |> with_contract_creation(contract_address) + |> with_block(block) + + internal_transaction = + :internal_transaction_create + |> insert( + transaction: transaction, + index: 0, + block_hash: transaction.block_hash, + block_index: 1 + ) + |> with_contract_creation(contract_address) + + session + |> BlockPage.visit_page(block) + |> assert_has(BlockPage.contract_creation(internal_transaction)) + end + + test "transaction with multiple token transfers shows all transfers if expanded", %{ + first_shown_block: block, + session: session + } do + contract_token_address = insert(:contract_address) + insert(:token, contract_address: contract_token_address) + + transaction = + :transaction + |> insert(to_address: contract_token_address) + |> with_block(block) + + insert_list( + 3, + :token_transfer, + transaction: transaction, + token_contract_address: contract_token_address, + block: block + ) + + session + |> BlockPage.visit_page(block) + |> assert_has(BlockPage.token_transfers(transaction, count: 1)) + |> click(BlockPage.token_transfers_expansion(transaction)) + |> assert_has(BlockPage.token_transfers(transaction, count: 3)) + end + + test "show reorg detail page", %{session: session} do + reorg = insert(:block, consensus: false) + + session + |> BlockPage.visit_page(reorg) + |> assert_has(BlockPage.detail_number(reorg)) + |> assert_has(BlockPage.page_type("Reorg Details")) + end + + test "show uncle detail page", %{session: session} do + uncle = insert(:block, consensus: false) + insert(:block_second_degree_relation, uncle_hash: uncle.hash) + + session + |> BlockPage.visit_page(uncle) + |> assert_has(BlockPage.detail_number(uncle)) + |> assert_has(BlockPage.page_type("Uncle Details")) + end + + test "show link to uncle on block detail page", %{session: session} do + block = insert(:block) + uncle = insert(:block, consensus: false) + insert(:block_second_degree_relation, uncle_hash: uncle.hash, nephew: block) + + session + |> BlockPage.visit_page(block) + |> assert_has(BlockPage.detail_number(block)) + |> assert_has(BlockPage.page_type("Block Details")) + |> assert_has(BlockPage.uncle_link(uncle)) + end + end + + describe "viewing blocks list" do + test "viewing the blocks index page", %{first_shown_block: block, session: session} do + session + |> BlockListPage.visit_page() + |> assert_has(BlockListPage.block(block)) + end + + test "inserts place holder blocks on render for out of order blocks", %{session: session} do + insert(:block, number: 315) + + session + |> BlockListPage.visit_page() + |> assert_has(BlockListPage.block(%Block{number: 314})) + |> assert_has(BlockListPage.place_holder_blocks(3)) + end + end + + describe "viewing uncle blocks list" do + setup do + uncles = + for _index <- 1..10 do + uncle = insert(:block, consensus: false) + insert(:block_second_degree_relation, uncle_hash: uncle.hash) + + transaction = insert(:transaction) + insert(:transaction_fork, hash: transaction.hash, uncle_hash: uncle.hash) + + uncle + end + + {:ok, %{uncles: uncles}} + end + + test "lists uncle blocks", %{session: session, uncles: [uncle | _]} do + session + |> BlockListPage.visit_uncles_page() + |> assert_has(BlockListPage.block(uncle)) + |> assert_has(BlockListPage.blocks(10)) + end + end + + describe "viewing reorg blocks list" do + test "lists uncle blocks", %{session: session} do + [reorg | _] = insert_list(10, :block, consensus: false) + + session + |> BlockListPage.visit_reorgs_page() + |> assert_has(BlockListPage.block(reorg)) + |> assert_has(BlockListPage.blocks(10)) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/viewing_chain_test.exs b/apps/block_scout_web/test/block_scout_web/features/viewing_chain_test.exs new file mode 100644 index 0000000..5216623 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/viewing_chain_test.exs @@ -0,0 +1,160 @@ +defmodule BlockScoutWeb.ViewingChainTest do + @moduledoc false + + use BlockScoutWeb.FeatureCase, + # MUST Be false because ETS tables for Counters are shared + async: false + + alias BlockScoutWeb.{AddressPage, BlockPage, ChainPage, TransactionPage} + alias Explorer.Chain.Block + alias Explorer.Counters.AddressesCounter + + setup do + Supervisor.terminate_child(Explorer.Supervisor, Explorer.Chain.Cache.Blocks.child_id()) + Supervisor.restart_child(Explorer.Supervisor, Explorer.Chain.Cache.Blocks.child_id()) + + Enum.map(401..404, &insert(:block, number: &1)) + + block = insert(:block, number: 405) + + 4 + |> insert_list(:transaction) + |> with_block(block) + + :transaction + |> insert() + |> with_block(block) + + {:ok, + %{ + block: block + }} + end + + describe "viewing addresses" do + test "search for address", %{session: session} do + address = insert(:address) + + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + session + |> ChainPage.visit_page() + |> ChainPage.search(to_string(address.hash)) + |> assert_has(AddressPage.detail_hash(address)) + end + end + + describe "viewing blocks" do + test "search for blocks from chain page", %{session: session} do + block = insert(:block, number: 6) + + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + session + |> ChainPage.visit_page() + |> ChainPage.search(to_string(block.number)) + |> assert_has(BlockPage.detail_number(block)) + end + + test "blocks list", %{session: session} do + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + session + |> ChainPage.visit_page() + |> assert_has(ChainPage.blocks(count: 4)) + end + + test "inserts place holder blocks on render for out of order blocks", %{session: session} do + insert(:block, number: 409) + + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + session + |> ChainPage.visit_page() + |> assert_has(ChainPage.block(%Block{number: 408})) + |> assert_has(ChainPage.place_holder_blocks(3)) + end + end + + describe "viewing transactions" do + test "search for transactions", %{session: session} do + block = insert(:block) + + transaction = + insert(:transaction) + |> with_block(block) + + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + session + |> ChainPage.visit_page() + |> ChainPage.search(to_string(transaction.hash)) + |> assert_has(TransactionPage.detail_hash(transaction)) + end + + test "transactions list", %{session: session} do + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + session + |> ChainPage.visit_page() + |> assert_has(ChainPage.transactions(count: 5)) + end + + test "contract creation is shown for to_address", %{session: session, block: block} do + contract_address = insert(:contract_address) + + transaction = + :transaction + |> insert(to_address: nil) + |> with_contract_creation(contract_address) + |> with_block(block) + + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + session + |> ChainPage.visit_page() + |> assert_has(ChainPage.contract_creation(transaction)) + end + + test "transaction with multiple token transfers shows all transfers if expanded", %{ + block: block, + session: session + } do + contract_token_address = insert(:contract_address) + insert(:token, contract_address: contract_token_address) + + transaction = + :transaction + |> insert(to_address: contract_token_address) + |> with_block(block, status: :ok) + + insert_list( + 3, + :token_transfer, + transaction: transaction, + token_contract_address: contract_token_address, + block: block + ) + + start_supervised!(AddressesCounter) + AddressesCounter.consolidate() + + ChainPage.visit_page(session) + + # wait for the `transactions-list` to load + :timer.sleep(1000) + + session + |> assert_has(ChainPage.token_transfers(transaction, count: 1)) + |> click(ChainPage.token_transfers_expansion(transaction)) + |> assert_has(ChainPage.token_transfers(transaction, count: 3)) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/viewing_tokens_test.exs b/apps/block_scout_web/test/block_scout_web/features/viewing_tokens_test.exs new file mode 100644 index 0000000..d896266 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/viewing_tokens_test.exs @@ -0,0 +1,21 @@ +defmodule BlockScoutWeb.ViewingTokensTest do + use BlockScoutWeb.FeatureCase, async: true + + alias BlockScoutWeb.TokenPage + + describe "viewing token holders" do + test "list the token holders", %{session: session} do + token = insert(:token) + + insert_list( + 2, + :address_current_token_balance, + token_contract_address_hash: token.contract_address_hash + ) + + session + |> TokenPage.visit_page(token.contract_address) + |> assert_has(TokenPage.token_holders(count: 2)) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs b/apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs new file mode 100644 index 0000000..33c8fe9 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs @@ -0,0 +1,163 @@ +defmodule BlockScoutWeb.ViewingTransactionsTest do + @moduledoc false + + import Mox + + use BlockScoutWeb.FeatureCase, async: false + + alias BlockScoutWeb.{AddressPage, TransactionListPage, TransactionLogsPage, TransactionPage} + alias Explorer.Chain.Wei + + setup :set_mox_global + + setup do + block = + insert(:block, %{ + timestamp: Timex.now() |> Timex.shift(hours: -2), + gas_used: 123_987 + }) + + 3 + |> insert_list(:transaction) + |> with_block() + + pending = insert(:transaction, block_hash: nil, gas: 5891, index: nil) + pending_contract = insert(:transaction, to_address: nil, block_hash: nil, gas: 5891, index: nil) + + lincoln = insert(:address) + taft = insert(:address) + + # From Lincoln to Taft. + txn_from_lincoln = + :transaction + |> insert(from_address: lincoln, to_address: taft) + |> with_block(block) + + transaction = + :transaction + |> insert( + value: Wei.from(Decimal.new(5656), :ether), + gas: Decimal.new(1_230_000_000_000_123_123), + gas_price: Decimal.new(7_890_000_000_898_912_300_045), + input: "0x000012", + nonce: 99045, + inserted_at: Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}"), + updated_at: Timex.parse!("1980-01-01T00:00:18-00:00", "{ISO:Extended}"), + from_address: taft, + to_address: lincoln + ) + |> with_block(block, gas_used: Decimal.new(1_230_000_000_000_123_000), status: :ok) + + insert(:log, address: lincoln, index: 0, transaction: transaction, block: block, block_number: block.number) + + internal = + insert(:internal_transaction, + index: 0, + transaction: transaction, + block_hash: transaction.block_hash, + block_index: 0 + ) + + {:ok, + %{ + pending: pending, + pending_contract: pending_contract, + internal: internal, + lincoln: lincoln, + taft: taft, + transaction: transaction, + txn_from_lincoln: txn_from_lincoln + }} + end + + describe "viewing transaction lists" do + test "viewing the default transactions tab", %{ + session: session, + transaction: transaction, + pending: pending + } do + session + |> TransactionListPage.visit_page() + |> assert_has(TransactionListPage.transaction(transaction)) + |> assert_has(TransactionListPage.transaction_status(transaction)) + |> refute_has(TransactionListPage.transaction(pending)) + end + + test "viewing the pending tranasctions list", %{ + pending: pending, + pending_contract: pending_contract, + session: session + } do + session + |> TransactionListPage.visit_pending_transactions_page() + |> assert_has(TransactionListPage.transaction(pending)) + |> assert_has(TransactionListPage.transaction(pending_contract)) + |> assert_has(TransactionListPage.transaction_status(pending_contract)) + end + + test "contract creation is shown for to_address on list page", %{session: session} do + contract_address = insert(:contract_address) + + transaction = + :transaction + |> insert(to_address: nil) + |> with_block() + |> with_contract_creation(contract_address) + + :internal_transaction_create + |> insert(transaction: transaction, index: 0, block_hash: transaction.block_hash, block_index: 0) + |> with_contract_creation(contract_address) + + session + |> TransactionListPage.visit_page() + |> assert_has(TransactionListPage.contract_creation(transaction)) + end + end + + describe "viewing a pending transaction page" do + test "can see a pending transaction's details", %{session: session, pending: pending} do + EthereumJSONRPC.Mox + |> expect(:json_rpc, fn %{id: _id, method: "net_version", params: []}, _options -> + {:ok, "100"} + end) + + session + |> TransactionPage.visit_page(pending) + |> assert_has(TransactionPage.detail_hash(pending)) + |> assert_has(TransactionPage.is_pending()) + end + end + + describe "viewing a transaction page" do + test "can navigate to transaction show from list page", %{session: session, transaction: transaction} do + session + |> TransactionListPage.visit_page() + |> TransactionListPage.click_transaction(transaction) + |> assert_has(TransactionPage.detail_hash(transaction)) + end + + test "can see a transaction's details", %{session: session, transaction: transaction} do + session + |> TransactionPage.visit_page(transaction) + |> assert_has(TransactionPage.detail_hash(transaction)) + end + + test "can view a transaction's logs", %{session: session, transaction: transaction} do + session + |> TransactionPage.visit_page(transaction) + |> TransactionPage.click_logs() + |> assert_has(TransactionLogsPage.logs(count: 1)) + end + + test "can visit an address from the transaction logs page", %{ + lincoln: lincoln, + session: session, + transaction: transaction + } do + session + |> TransactionLogsPage.visit_page(transaction) + |> TransactionLogsPage.click_address(lincoln) + |> assert_has(AddressPage.detail_hash(lincoln)) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/models/user_from_auth_test.exs b/apps/block_scout_web/test/block_scout_web/models/user_from_auth_test.exs new file mode 100644 index 0000000..2d13f11 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/models/user_from_auth_test.exs @@ -0,0 +1,107 @@ +defmodule UserFromAuthTest do + use Explorer.DataCase + + alias BlockScoutWeb.Models.UserFromAuth + alias Explorer.Account.Identity + alias Explorer.Account.Watchlist + alias Explorer.Repo + alias Ueberauth.Auth + alias Ueberauth.Auth.Info + alias Ueberauth.Strategy.Auth0 + + describe "get user info" do + test "from github" do + auth = %Auth{ + info: %Info{ + birthday: nil, + description: nil, + email: "john@blockscout.com", + first_name: nil, + image: "https://avatars.githubusercontent.com/u/666666=4", + last_name: nil, + location: nil, + name: "John Snow", + nickname: "johnnny", + phone: nil, + urls: %{profile: nil, website: nil} + }, + provider: :auth0, + strategy: Auth0, + uid: "github|666666" + } + + user_data = UserFromAuth.find_or_create(auth) + + %{ + id: identity_id, + email: "john@blockscout.com", + name: "John Snow", + uid: "github|666666" + } = Identity |> first |> Repo.account_repo().one() + + %{ + id: watchlist_id, + identity_id: ^identity_id, + name: "default" + } = Watchlist |> first |> Repo.account_repo().one() + + assert {:ok, + %{ + avatar: "https://avatars.githubusercontent.com/u/666666=4", + email: "john@blockscout.com", + id: ^identity_id, + name: "John Snow", + nickname: "johnnny", + uid: "github|666666", + watchlist_id: ^watchlist_id + }} = user_data + end + + test "from google" do + auth = %Auth{ + info: %Info{ + birthday: nil, + description: nil, + email: "john@blockscout.com", + first_name: "John", + image: "https://lh3.googleusercontent.com/a/xxx666-yyy777=s99-c", + last_name: "Snow", + location: nil, + name: "John Snow", + nickname: "johnnny", + phone: nil, + urls: %{profile: nil, website: nil} + }, + provider: :auth0, + strategy: Auth0, + uid: "google-oauth2|666666" + } + + user_data = UserFromAuth.find_or_create(auth) + + %{ + id: identity_id, + email: "john@blockscout.com", + name: "John Snow", + uid: "google-oauth2|666666" + } = Identity |> first |> Repo.account_repo().one() + + %{ + id: watchlist_id, + identity_id: ^identity_id, + name: "default" + } = Watchlist |> first |> Repo.account_repo().one() + + assert {:ok, + %{ + avatar: "https://lh3.googleusercontent.com/a/xxx666-yyy777=s99-c", + email: "john@blockscout.com", + id: ^identity_id, + name: "John Snow", + nickname: "johnnny", + uid: "google-oauth2|666666", + watchlist_id: ^watchlist_id + }} = user_data + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/plug/admin/check_owner_registered_test.exs b/apps/block_scout_web/test/block_scout_web/plug/admin/check_owner_registered_test.exs new file mode 100644 index 0000000..2eb7b98 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/plug/admin/check_owner_registered_test.exs @@ -0,0 +1,27 @@ +defmodule BlockScoutWeb.Plug.Admin.CheckOwnerRegisteredTest do + use BlockScoutWeb.ConnCase + + alias BlockScoutWeb.Plug.Admin.CheckOwnerRegistered + alias Explorer.Admin + + test "init/1" do + assert CheckOwnerRegistered.init([]) == [] + end + + describe "call/2" do + test "redirects if owner user isn't configured", %{conn: conn} do + assert {:error, _} = Admin.owner() + result = CheckOwnerRegistered.call(conn, []) + assert redirected_to(result) == AdminRoutes.setup_path(conn, :configure) + assert result.halted + end + + test "continues if owner user is configured", %{conn: conn} do + insert(:administrator) + assert {:ok, _} = Admin.owner() + result = CheckOwnerRegistered.call(conn, []) + assert result.state == :unset + refute result.halted + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/plug/admin/require_admin_role_test.exs b/apps/block_scout_web/test/block_scout_web/plug/admin/require_admin_role_test.exs new file mode 100644 index 0000000..45bcdc2 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/plug/admin/require_admin_role_test.exs @@ -0,0 +1,42 @@ +defmodule BlockScoutWeb.Plug.Admin.RequireAdminRoleTest do + use BlockScoutWeb.ConnCase + + import Plug.Conn, only: [put_session: 3, assign: 3] + + alias BlockScoutWeb.Router + alias BlockScoutWeb.Plug.Admin.RequireAdminRole + + test "init/1" do + assert RequireAdminRole.init([]) == [] + end + + describe "call/2" do + setup %{conn: conn} do + conn = + conn + |> bypass_through(Router, [:browser]) + |> get("/") + + {:ok, conn: conn} + end + + test "redirects if user in conn isn't an admin", %{conn: conn} do + result = RequireAdminRole.call(conn, []) + assert redirected_to(result) == AdminRoutes.session_path(conn, :new) + assert result.halted + end + + test "continues if user in assigns is an admin", %{conn: conn} do + administrator = insert(:administrator) + + result = + conn + |> put_session(:user_id, administrator.user.id) + |> assign(:user, administrator.user) + |> RequireAdminRole.call([]) + + refute result.halted + assert result.state == :unset + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/plug/fetch_user_from_session_test.exs b/apps/block_scout_web/test/block_scout_web/plug/fetch_user_from_session_test.exs new file mode 100644 index 0000000..ed00ce6 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/plug/fetch_user_from_session_test.exs @@ -0,0 +1,46 @@ +defmodule BlockScoutWeb.Plug.FetchUserFromSessionTest do + use BlockScoutWeb.ConnCase + + import Plug.Conn, only: [put_session: 3] + + alias BlockScoutWeb.Plug.FetchUserFromSession + alias BlockScoutWeb.Router + alias Explorer.Accounts.User + + test "init/1" do + assert FetchUserFromSession.init([]) == [] + end + + describe "call/2" do + setup %{conn: conn} do + conn = + conn + |> bypass_through(Router, [:browser]) + |> get("/") + + {:ok, conn: conn} + end + + test "loads user if valid user id in session", %{conn: conn} do + user = insert(:user) + + result = + conn + |> put_session(:user_id, user.id) + |> FetchUserFromSession.call([]) + + assert %User{} = result.assigns.user + end + + test "returns conn if user id is invalid in session", %{conn: conn} do + conn = put_session(conn, :user_id, 1) + result = FetchUserFromSession.call(conn, []) + + assert conn == result + end + + test "returns conn if no user id is in session", %{conn: conn} do + assert FetchUserFromSession.call(conn, []) == conn + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs new file mode 100644 index 0000000..134d1b7 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/schema/query/address_test.exs @@ -0,0 +1,594 @@ +defmodule BlockScoutWeb.Schema.Query.AddressTest do + use BlockScoutWeb.ConnCase + + describe "address field" do + test "with valid argument 'hash', returns all expected fields", %{conn: conn} do + address = insert(:address, fetched_coin_balance: 100) + + query = """ + query ($hash: AddressHash!) { + address(hash: $hash) { + hash + fetched_coin_balance + fetched_coin_balance_block_number + contract_code + } + } + """ + + variables = %{"hash" => to_string(address.hash)} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "address" => %{ + "hash" => to_string(address.hash), + "fetched_coin_balance" => to_string(address.fetched_coin_balance.value), + "fetched_coin_balance_block_number" => address.fetched_coin_balance_block_number, + "contract_code" => nil + } + } + } + end + + test "with contract address, `contract_code` is serialized as expected", %{conn: conn} do + address = insert(:contract_address, fetched_coin_balance: 100) + + query = """ + query ($hash: AddressHash!) { + address(hash: $hash) { + contract_code + } + } + """ + + variables = %{"hash" => to_string(address.hash)} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "address" => %{ + "contract_code" => to_string(address.contract_code) + } + } + } + end + + test "smart_contract returns all expected fields", %{conn: conn} do + address = insert(:address, fetched_coin_balance: 100) + smart_contract = insert(:smart_contract, address_hash: address.hash, contract_code_md5: "123") + + query = """ + query ($hash: AddressHash!) { + address(hash: $hash) { + fetched_coin_balance + smart_contract { + name + compiler_version + optimization + contract_source_code + abi + address_hash + } + } + } + """ + + variables = %{"hash" => to_string(address.hash)} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "address" => %{ + "fetched_coin_balance" => to_string(address.fetched_coin_balance.value), + "smart_contract" => %{ + "name" => smart_contract.name, + "compiler_version" => smart_contract.compiler_version, + "optimization" => smart_contract.optimization, + "contract_source_code" => smart_contract.contract_source_code, + "abi" => Jason.encode!(smart_contract.abi), + "address_hash" => to_string(address.hash) + } + } + } + } + end + + test "errors for non-existent address hash", %{conn: conn} do + address = build(:address) + + query = """ + query ($hash: AddressHash!) { + address(hash: $hash) { + fetched_coin_balance + } + } + """ + + variables = %{"hash" => to_string(address.hash)} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] =~ ~s(Address not found.) + end + + test "errors if argument 'hash' is missing", %{conn: conn} do + query = """ + query { + address { + fetched_coin_balance + } + } + """ + + variables = %{} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] == ~s(In argument "hash": Expected type "AddressHash!", found null.) + end + + test "errors if argument 'hash' is not a valid address hash", %{conn: conn} do + query = """ + query ($hash: AddressHash!) { + address(hash: $hash) { + fetched_coin_balance + } + } + """ + + variables = %{"hash" => "someInvalidHash"} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] =~ ~s(Argument "hash" has invalid value) + end + end + + describe "address transactions field" do + test "returns all expected transaction fields", %{conn: conn} do + address = insert(:address) + + transaction = insert(:transaction, from_address: address) + + query = """ + query ($hash: AddressHash!, $first: Int!) { + address(hash: $hash) { + transactions(first: $first) { + edges { + node { + hash + block_number + cumulative_gas_used + error + gas + gas_price + gas_used + index + input + nonce + r + s + status + v + value + from_address_hash + to_address_hash + created_contract_address_hash + } + } + } + } + } + """ + + variables = %{ + "hash" => to_string(address.hash), + "first" => 1 + } + + conn = post(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "address" => %{ + "transactions" => %{ + "edges" => [ + %{ + "node" => %{ + "hash" => to_string(transaction.hash), + "block_number" => transaction.block_number, + "cumulative_gas_used" => nil, + "error" => transaction.error, + "gas" => to_string(transaction.gas), + "gas_price" => to_string(transaction.gas_price.value), + "gas_used" => nil, + "index" => transaction.index, + "input" => to_string(transaction.input), + "nonce" => to_string(transaction.nonce), + "r" => to_string(transaction.r), + "s" => to_string(transaction.s), + "status" => nil, + "v" => to_string(transaction.v), + "value" => to_string(transaction.value.value), + "from_address_hash" => to_string(transaction.from_address_hash), + "to_address_hash" => to_string(transaction.to_address_hash), + "created_contract_address_hash" => nil + } + } + ] + } + } + } + } + end + + test "with address with zero transactions", %{conn: conn} do + address = insert(:address) + + query = """ + query ($hash: AddressHash!, $first: Int!) { + address(hash: $hash) { + transactions(first: $first) { + edges { + node { + hash + } + } + } + } + } + """ + + variables = %{ + "hash" => to_string(address.hash), + "first" => 1 + } + + conn = post(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "address" => %{ + "transactions" => %{ + "edges" => [] + } + } + } + } + end + + test "transactions are ordered by descending block and index", %{conn: conn} do + first_block = insert(:block) + second_block = insert(:block) + third_block = insert(:block) + + address = insert(:address) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(second_block) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(third_block) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(first_block) + + query = """ + query ($hash: AddressHash!, $first: Int!) { + address(hash: $hash) { + transactions(first: $first) { + edges { + node { + hash + block_number + index + } + } + } + } + } + """ + + variables = %{ + "hash" => to_string(address.hash), + "first" => 3 + } + + conn = post(conn, "/graphql", query: query, variables: variables) + + %{ + "data" => %{ + "address" => %{ + "transactions" => %{ + "edges" => transactions + } + } + } + } = json_response(conn, 200) + + block_number_and_index_order = + Enum.map(transactions, fn transaction -> + {transaction["node"]["block_number"], transaction["node"]["index"]} + end) + + assert block_number_and_index_order == Enum.sort(block_number_and_index_order, &(&1 >= &2)) + assert length(transactions) == 3 + assert Enum.all?(transactions, &(&1["node"]["block_number"] == third_block.number)) + end + + test "transactions are ordered by ascending block and index", %{conn: conn} do + first_block = insert(:block) + second_block = insert(:block) + third_block = insert(:block) + + address = insert(:address) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(second_block) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(third_block) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(first_block) + + query = """ + query ($hash: AddressHash!, $first: Int!) { + address(hash: $hash) { + transactions(first: $first, order: ASC) { + edges { + node { + hash + block_number + index + } + } + } + } + } + """ + + variables = %{ + "hash" => to_string(address.hash), + "first" => 3 + } + + conn = post(conn, "/graphql", query: query, variables: variables) + + %{ + "data" => %{ + "address" => %{ + "transactions" => %{ + "edges" => transactions + } + } + } + } = json_response(conn, 200) + + block_number_and_index_order = + Enum.map(transactions, fn transaction -> + {transaction["node"]["block_number"], transaction["node"]["index"]} + end) + + assert block_number_and_index_order == Enum.sort(block_number_and_index_order, &(&1 < &2)) + assert length(transactions) == 3 + assert Enum.all?(transactions, &(&1["node"]["block_number"] == first_block.number)) + end + + test "complexity correlates to 'first' or 'last' arguments", %{conn: conn} do + address = build(:address) + + query = """ + query ($hash: AddressHash!, $first: Int!) { + address(hash: $hash) { + transactions(first: $first) { + edges { + node { + hash + } + } + } + } + } + """ + + variables = %{ + "hash" => to_string(address.hash), + "first" => 67 + } + + conn = post(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error1, error2, error3]} = json_response(conn, 200) + assert error1["message"] =~ ~s(Field transactions is too complex) + assert error2["message"] =~ ~s(Field address is too complex) + assert error3["message"] =~ ~s(Operation is too complex) + end + + test "with 'last' and 'count' arguments", %{conn: conn} do + # "`last: N` must always be acompanied by either a `before:` argument to + # the query, or an explicit `count:` option to the `from_query` call. + # Otherwise it is impossible to derive the required offset." + # https://hexdocs.pm/absinthe_relay/Absinthe.Relay.Connection.html#from_query/4 + # + # This test ensures support of a 'count' argument. + + first_block = insert(:block) + second_block = insert(:block) + third_block = insert(:block) + + address = insert(:address) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(second_block) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(third_block) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(first_block) + + query = """ + query ($hash: AddressHash!, $last: Int!, $count: Int!) { + address(hash: $hash) { + transactions(last: $last, count: $count) { + edges { + node { + hash + block_number + } + } + } + } + } + """ + + variables = %{ + "hash" => to_string(address.hash), + "last" => 3, + "count" => 9 + } + + conn = post(conn, "/graphql", query: query, variables: variables) + + %{ + "data" => %{ + "address" => %{ + "transactions" => %{ + "edges" => transactions + } + } + } + } = json_response(conn, 200) + + assert length(transactions) == 3 + assert Enum.all?(transactions, &(&1["node"]["block_number"] == first_block.number)) + end + + test "pagination support with 'first' and 'after' arguments", %{conn: conn} do + first_block = insert(:block) + second_block = insert(:block) + third_block = insert(:block) + + address = insert(:address) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(second_block) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(third_block) + + 3 + |> insert_list(:transaction, from_address: address) + |> with_block(first_block) + + query1 = """ + query ($hash: AddressHash!, $first: Int!) { + address(hash: $hash) { + transactions(first: $first) { + page_info { + has_next_page + has_previous_page + } + edges { + node { + hash + block_number + } + cursor + } + } + } + } + """ + + variables1 = %{ + "hash" => to_string(address.hash), + "first" => 3 + } + + conn = post(conn, "/graphql", query: query1, variables: variables1) + + %{"data" => %{"address" => %{"transactions" => page1}}} = json_response(conn, 200) + + assert page1["page_info"] == %{"has_next_page" => true, "has_previous_page" => false} + assert Enum.all?(page1["edges"], &(&1["node"]["block_number"] == third_block.number)) + + last_cursor_page1 = + page1 + |> Map.get("edges") + |> List.last() + |> Map.get("cursor") + + query2 = """ + query ($hash: AddressHash!, $first: Int!, $after: String!) { + address(hash: $hash) { + transactions(first: $first, after: $after) { + page_info { + has_next_page + has_previous_page + } + edges { + node { + hash + block_number + } + cursor + } + } + } + } + """ + + variables2 = %{ + "hash" => to_string(address.hash), + "first" => 3, + "after" => last_cursor_page1 + } + + conn = post(conn, "/graphql", query: query2, variables: variables2) + + %{"data" => %{"address" => %{"transactions" => page2}}} = json_response(conn, 200) + + assert page2["page_info"] == %{"has_next_page" => true, "has_previous_page" => true} + assert Enum.all?(page2["edges"], &(&1["node"]["block_number"] == second_block.number)) + + last_cursor_page2 = + page2 + |> Map.get("edges") + |> List.last() + |> Map.get("cursor") + + variables3 = %{ + "hash" => to_string(address.hash), + "first" => 3, + "after" => last_cursor_page2 + } + + conn = post(conn, "/graphql", query: query2, variables: variables3) + + %{"data" => %{"address" => %{"transactions" => page3}}} = json_response(conn, 200) + + assert page3["page_info"] == %{"has_next_page" => false, "has_previous_page" => true} + assert Enum.all?(page3["edges"], &(&1["node"]["block_number"] == first_block.number)) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/addresses_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/addresses_test.exs new file mode 100644 index 0000000..f6145a4 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/schema/query/addresses_test.exs @@ -0,0 +1,185 @@ +defmodule BlockScoutWeb.Schema.Query.AddressesTest do + use BlockScoutWeb.ConnCase + + describe "addresses field" do + test "with valid argument 'hashes', returns all expected fields", %{conn: conn} do + address = insert(:address, fetched_coin_balance: 100) + + query = """ + query ($hashes: [AddressHash!]!) { + addresses(hashes: $hashes) { + hash + fetched_coin_balance + fetched_coin_balance_block_number + contract_code + } + } + """ + + variables = %{"hashes" => to_string(address.hash)} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "addresses" => [ + %{ + "hash" => to_string(address.hash), + "fetched_coin_balance" => to_string(address.fetched_coin_balance.value), + "fetched_coin_balance_block_number" => address.fetched_coin_balance_block_number, + "contract_code" => nil + } + ] + } + } + end + + test "with contract address, `contract_code` is serialized as expected", %{conn: conn} do + address = insert(:contract_address, fetched_coin_balance: 100) + + query = """ + query ($hashes: [AddressHash!]!) { + addresses(hashes: $hashes) { + contract_code + } + } + """ + + variables = %{"hashes" => to_string(address.hash)} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "addresses" => [ + %{ + "contract_code" => to_string(address.contract_code) + } + ] + } + } + end + + test "smart_contract returns all expected fields", %{conn: conn} do + address = insert(:address, fetched_coin_balance: 100) + smart_contract = insert(:smart_contract, address_hash: address.hash, contract_code_md5: "123") + + query = """ + query ($hashes: [AddressHash!]!) { + addresses(hashes: $hashes) { + fetched_coin_balance + smart_contract { + name + compiler_version + optimization + contract_source_code + abi + address_hash + } + } + } + """ + + variables = %{"hashes" => to_string(address.hash)} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "addresses" => [ + %{ + "fetched_coin_balance" => to_string(address.fetched_coin_balance.value), + "smart_contract" => %{ + "name" => smart_contract.name, + "compiler_version" => smart_contract.compiler_version, + "optimization" => smart_contract.optimization, + "contract_source_code" => smart_contract.contract_source_code, + "abi" => Jason.encode!(smart_contract.abi), + "address_hash" => to_string(address.hash) + } + } + ] + } + } + end + + test "errors for non-existent address hashes", %{conn: conn} do + address = build(:address) + + query = """ + query ($hashes: [AddressHash!]!) { + addresses(hashes: $hashes) { + fetched_coin_balance + } + } + """ + + variables = %{"hashes" => [to_string(address.hash)]} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] =~ ~s(Addresses not found.) + end + + test "errors if argument 'hashes' is missing", %{conn: conn} do + query = """ + query { + addresses { + fetched_coin_balance + } + } + """ + + variables = %{} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] == ~s(In argument "hashes": Expected type "[AddressHash!]!", found null.) + end + + test "errors if argument 'hashes' is not a list of address hashes", %{conn: conn} do + query = """ + query ($hashes: [AddressHash!]!) { + addresses(hashes: $hashes) { + fetched_coin_balance + } + } + """ + + variables = %{"hashes" => ["someInvalidHash"]} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] =~ ~s(Argument "hashes" has invalid value) + end + + test "correlates complexity to size of 'hashes' argument", %{conn: conn} do + # max of 50 addresses with four fields of complexity 1 can be fetched + # per query: + # 50 * 4 = 200, which is equal to a max complexity of 200 + hashes = 51 |> build_list(:address) |> Enum.map(&to_string(&1.hash)) + + query = """ + query ($hashes: [AddressHash!]!) { + addresses(hashes: $hashes) { + hash + fetched_coin_balance + fetched_coin_balance_block_number + contract_code + } + } + """ + + variables = %{"hashes" => hashes} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error1, error2]} = json_response(conn, 200) + assert error1["message"] =~ ~s(Field addresses is too complex) + assert error2["message"] =~ ~s(Operation is too complex) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/block_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/block_test.exs new file mode 100644 index 0000000..d601e3d --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/schema/query/block_test.exs @@ -0,0 +1,108 @@ +defmodule BlockScoutWeb.Schema.Query.BlockTest do + use BlockScoutWeb.ConnCase + + describe "block field" do + test "with valid argument 'number', returns all expected fields", %{conn: conn} do + block = insert(:block) + + query = """ + query ($number: Int!) { + block(number: $number) { + hash + consensus + difficulty + gas_limit + gas_used + nonce + number + size + timestamp + total_difficulty + miner_hash + parent_hash + parent_hash + } + } + """ + + variables = %{"number" => block.number} + + conn = post(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "block" => %{ + "hash" => to_string(block.hash), + "consensus" => block.consensus, + "difficulty" => to_string(block.difficulty), + "gas_limit" => to_string(block.gas_limit), + "gas_used" => to_string(block.gas_used), + "nonce" => to_string(block.nonce), + "number" => block.number, + "size" => block.size, + "timestamp" => DateTime.to_iso8601(block.timestamp), + "total_difficulty" => to_string(block.total_difficulty), + "miner_hash" => to_string(block.miner_hash), + "parent_hash" => to_string(block.parent_hash) + } + } + } + end + + test "errors for non-existent block number", %{conn: conn} do + block = insert(:block) + non_existent_block_number = block.number + 1 + + query = """ + query ($number: Int!) { + block(number: $number) { + number + } + } + """ + + variables = %{"number" => non_existent_block_number} + + conn = post(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] =~ ~s(Block number #{non_existent_block_number} was not found) + end + + test "errors if argument 'number' is missing", %{conn: conn} do + insert(:block) + + query = """ + { + block { + number + } + } + """ + + conn = get(conn, "/graphql", query: query) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] == ~s(In argument "number": Expected type "Int!", found null.) + end + + test "errors if argument 'number' is not an integer", %{conn: conn} do + insert(:block) + + query = """ + query ($number: Int!) { + block(number: $number) { + number + } + } + """ + + variables = %{"number" => "invalid"} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] =~ ~s(Argument "number" has invalid value) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/node_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/node_test.exs new file mode 100644 index 0000000..4638e2f --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/schema/query/node_test.exs @@ -0,0 +1,209 @@ +defmodule BlockScoutWeb.Schema.Query.NodeTest do + use BlockScoutWeb.ConnCase + + describe "node field" do + test "with valid argument 'id' for a transaction", %{conn: conn} do + transaction = insert(:transaction) + + query = """ + query($id: ID!) { + node(id: $id) { + ... on Transaction { + id + hash + } + } + } + """ + + id = Base.encode64("Transaction:#{transaction.hash}") + + variables = %{"id" => id} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "node" => %{ + "id" => id, + "hash" => to_string(transaction.hash) + } + } + } + end + + test "with 'id' for non-existent transaction", %{conn: conn} do + transaction = build(:transaction) + + query = """ + query($id: ID!) { + node(id: $id) { + ... on Transaction { + id + hash + } + } + } + """ + + id = Base.encode64("Transaction:#{transaction.hash}") + + variables = %{"id" => id} + + conn = get(conn, "/graphql", query: query, variables: variables) + + %{"errors" => [error]} = json_response(conn, 200) + + assert error["message"] == "Transaction not found." + end + + test "with valid argument 'id' for an internal transaction", %{conn: conn} do + transaction = insert(:transaction) |> with_block() + + internal_transaction = + insert(:internal_transaction, + transaction: transaction, + index: 0, + block_hash: transaction.block_hash, + block_index: 0 + ) + + query = """ + query($id: ID!) { + node(id: $id) { + ... on InternalTransaction { + id + transaction_hash + index + } + } + } + """ + + id = + %{transaction_hash: to_string(transaction.hash), index: internal_transaction.index} + |> Jason.encode!() + |> (fn unique_id -> "InternalTransaction:#{unique_id}" end).() + |> Base.encode64() + + variables = %{"id" => id} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "node" => %{ + "id" => id, + "transaction_hash" => to_string(transaction.hash), + "index" => internal_transaction.index + } + } + } + end + + test "with 'id' for non-existent internal transaction", %{conn: conn} do + transaction = insert(:transaction) |> with_block() + + internal_transaction = + build(:internal_transaction, + transaction: transaction, + index: 0, + block_hash: transaction.block_hash, + block_index: 0 + ) + + query = """ + query($id: ID!) { + node(id: $id) { + ... on InternalTransaction { + id + transaction_hash + index + } + } + } + """ + + id = + %{transaction_hash: to_string(transaction.hash), index: internal_transaction.index} + |> Jason.encode!() + |> (fn unique_id -> "InternalTransaction:#{unique_id}" end).() + |> Base.encode64() + + variables = %{"id" => id} + + conn = get(conn, "/graphql", query: query, variables: variables) + + %{"errors" => [error]} = json_response(conn, 200) + + assert error["message"] == "Internal transaction not found." + end + + test "with valid argument 'id' for a token_transfer", %{conn: conn} do + transaction = insert(:transaction) + token_transfer = insert(:token_transfer, transaction: transaction) + + query = """ + query($id: ID!) { + node(id: $id) { + ... on TokenTransfer { + id + transaction_hash + log_index + } + } + } + """ + + id = + %{transaction_hash: to_string(token_transfer.transaction_hash), log_index: token_transfer.log_index} + |> Jason.encode!() + |> (fn unique_id -> "TokenTransfer:#{unique_id}" end).() + |> Base.encode64() + + variables = %{"id" => id} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "node" => %{ + "id" => id, + "transaction_hash" => to_string(token_transfer.transaction_hash), + "log_index" => token_transfer.log_index + } + } + } + end + + test "with id for non-existent token transfer", %{conn: conn} do + transaction = build(:transaction) + + query = """ + query($id: ID!) { + node(id: $id) { + ... on TokenTransfer { + id + transaction_hash + log_index + } + } + } + """ + + id = + %{transaction_hash: to_string(transaction.hash), log_index: 0} + |> Jason.encode!() + |> (fn unique_id -> "TokenTransfer:#{unique_id}" end).() + |> Base.encode64() + + variables = %{"id" => id} + + conn = get(conn, "/graphql", query: query, variables: variables) + + %{"errors" => [error]} = json_response(conn, 200) + + assert error["message"] == "Token transfer not found." + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/token_transfers_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/token_transfers_test.exs new file mode 100644 index 0000000..1b02348 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/schema/query/token_transfers_test.exs @@ -0,0 +1,326 @@ +defmodule BlockScoutWeb.Schema.Query.TokenTransfersTest do + use BlockScoutWeb.ConnCase + + describe "token_transfers field" do + test "with valid argument, returns all expected fields", %{conn: conn} do + transaction = insert(:transaction) + token_transfer = insert(:token_transfer, transaction: transaction) + address_hash = to_string(token_transfer.token_contract_address_hash) + + query = """ + query ($token_contract_address_hash: AddressHash!, $first: Int!) { + token_transfers(token_contract_address_hash: $token_contract_address_hash, first: $first) { + edges { + node { + amount + block_number + log_index + token_id + from_address_hash + to_address_hash + token_contract_address_hash + transaction_hash + } + } + } + } + """ + + variables = %{ + "token_contract_address_hash" => address_hash, + "first" => 1 + } + + conn = post(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "token_transfers" => %{ + "edges" => [ + %{ + "node" => %{ + "amount" => to_string(token_transfer.amount), + "block_number" => token_transfer.block_number, + "log_index" => token_transfer.log_index, + "token_id" => token_transfer.token_id, + "from_address_hash" => to_string(token_transfer.from_address_hash), + "to_address_hash" => to_string(token_transfer.to_address_hash), + "token_contract_address_hash" => to_string(token_transfer.token_contract_address_hash), + "transaction_hash" => to_string(token_transfer.transaction_hash) + } + } + ] + } + } + } + end + + test "with token contract address with zero token transfers", %{conn: conn} do + address = insert(:contract_address) + + query = """ + query ($token_contract_address_hash: AddressHash!, $first: Int!) { + token_transfers(token_contract_address_hash: $token_contract_address_hash, first: $first) { + edges { + node { + amount + block_number + log_index + token_id + from_address_hash + to_address_hash + token_contract_address_hash + transaction_hash + } + } + } + } + """ + + variables = %{ + "token_contract_address_hash" => to_string(address.hash), + "first" => 10 + } + + conn = post(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "token_transfers" => %{ + "edges" => [] + } + } + } + end + + test "complexity correlates to first or last argument", %{conn: conn} do + address = insert(:contract_address) + + query1 = """ + query ($token_contract_address_hash: AddressHash!, $first: Int!) { + token_transfers(token_contract_address_hash: $token_contract_address_hash, first: $first) { + edges { + node { + amount + from_address_hash + to_address_hash + } + } + } + } + """ + + variables1 = %{ + "token_contract_address_hash" => to_string(address.hash), + "first" => 55 + } + + response1 = + conn + |> post("/graphql", query: query1, variables: variables1) + |> json_response(200) + + %{"errors" => [response1_error1, response1_error2]} = response1 + + assert response1_error1["message"] =~ ~s(Field token_transfers is too complex) + assert response1_error2["message"] =~ ~s(Operation is too complex) + + query2 = """ + query ($token_contract_address_hash: AddressHash!, $last: Int!) { + token_transfers(token_contract_address_hash: $token_contract_address_hash, last: $last) { + edges { + node { + amount + from_address_hash + to_address_hash + } + } + } + } + """ + + variables2 = %{ + "token_contract_address_hash" => to_string(address.hash), + "last" => 55 + } + + response2 = + conn + |> post("/graphql", query: query2, variables: variables2) + |> json_response(200) + + %{"errors" => [response2_error1, response2_error2]} = response2 + assert response2_error1["message"] =~ ~s(Field token_transfers is too complex) + assert response2_error2["message"] =~ ~s(Operation is too complex) + end + + test "with 'last' and 'count' arguments", %{conn: conn} do + # "`last: N` must always be acompanied by either a `before:` argument to + # the query, or an explicit `count:` option to the `from_query` call. + # Otherwise it is impossible to derive the required offset." + # https://hexdocs.pm/absinthe_relay/Absinthe.Relay.Connection.html#from_query/4 + # + # This test ensures support for a 'count' argument. + + address = insert(:contract_address) + + blocks = insert_list(2, :block) + + [transaction1, transaction2] = + for block <- blocks do + :transaction + |> insert() + |> with_block(block) + end + + token_transfer_attrs1 = %{ + block_number: transaction1.block_number, + transaction: transaction1, + token_contract_address: address + } + + token_transfer_attrs2 = %{ + block_number: transaction2.block_number, + transaction: transaction2, + token_contract_address: address + } + + insert(:token_transfer, token_transfer_attrs1) + insert(:token_transfer, token_transfer_attrs2) + + query = """ + query ($token_contract_address_hash: AddressHash!, $last: Int!, $count: Int) { + token_transfers(token_contract_address_hash: $token_contract_address_hash, last: $last, count: $count) { + edges { + node { + transaction_hash + } + } + } + } + """ + + variables = %{ + "token_contract_address_hash" => to_string(address.hash), + "last" => 1, + "count" => 2 + } + + [token_transfer] = + conn + |> post("/graphql", query: query, variables: variables) + |> json_response(200) + |> get_in(["data", "token_transfers", "edges"]) + + assert token_transfer["node"]["transaction_hash"] == to_string(transaction1.hash) + end + + test "pagination support with 'first' and 'after' arguments", %{conn: conn} do + address = insert(:contract_address) + + blocks = insert_list(3, :block) + + [transaction1, transaction2, transaction3] = + transactions = + for block <- blocks do + :transaction + |> insert() + |> with_block(block) + end + + for transaction <- transactions do + token_transfer_attrs = %{ + block_number: transaction.block_number, + transaction: transaction, + token_contract_address: address + } + + insert(:token_transfer, token_transfer_attrs) + end + + query1 = """ + query ($token_contract_address_hash: AddressHash!, $first: Int!) { + token_transfers(token_contract_address_hash: $token_contract_address_hash, first: $first) { + page_info { + has_next_page + has_previous_page + } + edges { + node { + transaction_hash + } + cursor + } + } + } + """ + + variables1 = %{ + "token_contract_address_hash" => to_string(address.hash), + "first" => 1 + } + + conn = post(conn, "/graphql", query: query1, variables: variables1) + + %{"data" => %{"token_transfers" => page1}} = json_response(conn, 200) + + assert page1["page_info"] == %{"has_next_page" => true, "has_previous_page" => false} + assert Enum.all?(page1["edges"], &(&1["node"]["transaction_hash"] == to_string(transaction3.hash))) + + last_cursor_page1 = + page1 + |> Map.get("edges") + |> List.last() + |> Map.get("cursor") + + query2 = """ + query ($token_contract_address_hash: AddressHash!, $first: Int!, $after: String!) { + token_transfers(token_contract_address_hash: $token_contract_address_hash, first: $first, after: $after) { + page_info { + has_next_page + has_previous_page + } + edges { + node { + transaction_hash + } + cursor + } + } + } + """ + + variables2 = %{ + "token_contract_address_hash" => to_string(address.hash), + "first" => 1, + "after" => last_cursor_page1 + } + + conn = post(conn, "/graphql", query: query2, variables: variables2) + + %{"data" => %{"token_transfers" => page2}} = json_response(conn, 200) + + assert page2["page_info"] == %{"has_next_page" => true, "has_previous_page" => true} + assert Enum.all?(page2["edges"], &(&1["node"]["transaction_hash"] == to_string(transaction2.hash))) + + last_cursor_page2 = + page2 + |> Map.get("edges") + |> List.last() + |> Map.get("cursor") + + variables3 = %{ + "token_contract_address_hash" => to_string(address.hash), + "first" => 1, + "after" => last_cursor_page2 + } + + conn = post(conn, "/graphql", query: query2, variables: variables3) + + %{"data" => %{"token_transfers" => page3}} = json_response(conn, 200) + + assert page3["page_info"] == %{"has_next_page" => false, "has_previous_page" => true} + assert Enum.all?(page3["edges"], &(&1["node"]["transaction_hash"] == to_string(transaction1.hash))) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/schema/query/transaction_test.exs b/apps/block_scout_web/test/block_scout_web/schema/query/transaction_test.exs new file mode 100644 index 0000000..8135ee5 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/schema/query/transaction_test.exs @@ -0,0 +1,552 @@ +defmodule BlockScoutWeb.Schema.Query.TransactionTest do + use BlockScoutWeb.ConnCase + + describe "transaction field" do + test "with valid argument 'hash', returns all expected fields", %{conn: conn} do + block = insert(:block) + + transaction = + :transaction + |> insert() + |> with_block(block, status: :ok) + + query = """ + query ($hash: FullHash!) { + transaction(hash: $hash) { + hash + block_number + cumulative_gas_used + error + gas + gas_price + gas_used + index + input + nonce + r + s + status + v + value + from_address_hash + to_address_hash + created_contract_address_hash + } + } + """ + + variables = %{"hash" => to_string(transaction.hash)} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "transaction" => %{ + "hash" => to_string(transaction.hash), + "block_number" => transaction.block_number, + "cumulative_gas_used" => to_string(transaction.cumulative_gas_used), + "error" => transaction.error, + "gas" => to_string(transaction.gas), + "gas_price" => to_string(transaction.gas_price.value), + "gas_used" => to_string(transaction.gas_used), + "index" => transaction.index, + "input" => to_string(transaction.input), + "nonce" => to_string(transaction.nonce), + "r" => to_string(transaction.r), + "s" => to_string(transaction.s), + "status" => transaction.status |> to_string() |> String.upcase(), + "v" => to_string(transaction.v), + "value" => to_string(transaction.value.value), + "from_address_hash" => to_string(transaction.from_address_hash), + "to_address_hash" => to_string(transaction.to_address_hash), + "created_contract_address_hash" => nil + } + } + } + end + + test "errors for non-existent transaction hash", %{conn: conn} do + transaction = build(:transaction) + + query = """ + query ($hash: FullHash!) { + transaction(hash: $hash) { + status + } + } + """ + + variables = %{"hash" => to_string(transaction.hash)} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] == "Transaction not found." + end + + test "errors if argument 'hash' is missing", %{conn: conn} do + query = """ + { + transaction { + status + } + } + """ + + conn = get(conn, "/graphql", query: query) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] == ~s(In argument "hash": Expected type "FullHash!", found null.) + end + + test "errors if argument 'hash' is not a 'FullHash'", %{conn: conn} do + query = """ + query ($hash: FullHash!) { + transaction(hash: $hash) { + status + } + } + """ + + variables = %{"hash" => "0x000"} + + conn = get(conn, "/graphql", query: query, variables: variables) + + assert %{"errors" => [error]} = json_response(conn, 200) + assert error["message"] =~ ~s(Argument "hash" has invalid value) + end + end + + describe "transaction internal_transactions field" do + test "returns all expected internal_transaction fields", %{conn: conn} do + address = insert(:address) + contract_address = insert(:contract_address) + + block = insert(:block) + + transaction = + :transaction + |> insert(from_address: address) + |> with_contract_creation(contract_address) + |> with_block(block) + + internal_transaction_attributes = %{ + transaction: transaction, + index: 0, + from_address: address, + call_type: :call, + block_hash: transaction.block_hash, + block_index: 0 + } + + internal_transaction = + :internal_transaction_create + |> insert(internal_transaction_attributes) + |> with_contract_creation(contract_address) + + query = """ + query ($hash: FullHash!, $first: Int!) { + transaction(hash: $hash) { + internal_transactions(first: $first) { + edges { + node { + call_type + created_contract_code + error + gas + gas_used + index + init + input + output + trace_address + type + value + block_number + transaction_index + created_contract_address_hash + from_address_hash + to_address_hash + transaction_hash + } + } + } + } + } + """ + + variables = %{ + "hash" => to_string(transaction.hash), + "first" => 1 + } + + conn = post(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "transaction" => %{ + "internal_transactions" => %{ + "edges" => [ + %{ + "node" => %{ + "call_type" => internal_transaction.call_type |> to_string() |> String.upcase(), + "created_contract_code" => to_string(internal_transaction.created_contract_code), + "error" => internal_transaction.error, + "gas" => to_string(internal_transaction.gas), + "gas_used" => to_string(internal_transaction.gas_used), + "index" => internal_transaction.index, + "init" => to_string(internal_transaction.init), + "input" => nil, + "output" => nil, + "trace_address" => Jason.encode!(internal_transaction.trace_address), + "type" => internal_transaction.type |> to_string() |> String.upcase(), + "value" => to_string(internal_transaction.value.value), + "block_number" => internal_transaction.block_number, + "transaction_index" => internal_transaction.transaction_index, + "created_contract_address_hash" => + to_string(internal_transaction.created_contract_address_hash), + "from_address_hash" => to_string(internal_transaction.from_address_hash), + "to_address_hash" => nil, + "transaction_hash" => to_string(internal_transaction.transaction_hash) + } + } + ] + } + } + } + } + end + + test "with transaction with zero internal transactions", %{conn: conn} do + address = insert(:address) + + block = insert(:block) + + transaction = + :transaction + |> insert(from_address: address) + |> with_block(block) + + query = """ + query ($hash: FullHash!, $first: Int!) { + transaction(hash: $hash) { + internal_transactions(first: $first) { + edges { + node { + index + transaction_hash + } + } + } + } + } + """ + + variables = %{ + "hash" => to_string(transaction.hash), + "first" => 1 + } + + conn = post(conn, "/graphql", query: query, variables: variables) + + assert json_response(conn, 200) == %{ + "data" => %{ + "transaction" => %{ + "internal_transactions" => %{ + "edges" => [] + } + } + } + } + end + + test "internal transactions are ordered by ascending index", %{conn: conn} do + transaction = insert(:transaction) |> with_block() + + insert(:internal_transaction, + transaction: transaction, + index: 2, + block_hash: transaction.block_hash, + block_index: 2 + ) + + insert(:internal_transaction, + transaction: transaction, + index: 0, + block_hash: transaction.block_hash, + block_index: 0 + ) + + insert(:internal_transaction, + transaction: transaction, + index: 1, + block_hash: transaction.block_hash, + block_index: 1 + ) + + query = """ + query ($hash: FullHash!, $first: Int!) { + transaction(hash: $hash) { + internal_transactions(first: $first) { + edges { + node { + index + transaction_hash + } + } + } + } + } + """ + + variables = %{ + "hash" => to_string(transaction.hash), + "first" => 3 + } + + response = + conn + |> post("/graphql", query: query, variables: variables) + |> json_response(200) + + internal_transactions = get_in(response, ["data", "transaction", "internal_transactions", "edges"]) + + index_order = Enum.map(internal_transactions, & &1["node"]["index"]) + + assert index_order == Enum.sort(index_order) + end + + test "complexity correlates to first or last argument", %{conn: conn} do + transaction = insert(:transaction) + + query1 = """ + query ($hash: FullHash!, $first: Int!) { + transaction(hash: $hash) { + internal_transactions(first: $first) { + edges { + node { + index + transaction_hash + } + } + } + } + } + """ + + variables1 = %{ + "hash" => to_string(transaction.hash), + "first" => 55 + } + + response1 = + conn + |> post("/graphql", query: query1, variables: variables1) + |> json_response(200) + + assert %{"errors" => [error1, error2, error3]} = response1 + assert error1["message"] =~ ~s(Field internal_transactions is too complex) + assert error2["message"] =~ ~s(Field transaction is too complex) + assert error3["message"] =~ ~s(Operation is too complex) + + query2 = """ + query ($hash: FullHash!, $last: Int!, $count: Int!) { + transaction(hash: $hash) { + internal_transactions(last: $last, count: $count) { + edges { + node { + index + transaction_hash + } + } + } + } + } + """ + + variables2 = %{ + "hash" => to_string(transaction.hash), + "last" => 55, + "count" => 100 + } + + response2 = + conn + |> post("/graphql", query: query2, variables: variables2) + |> json_response(200) + + assert %{"errors" => [error1, error2, error3]} = response2 + assert error1["message"] =~ ~s(Field internal_transactions is too complex) + assert error2["message"] =~ ~s(Field transaction is too complex) + assert error3["message"] =~ ~s(Operation is too complex) + end + + test "with 'last' and 'count' arguments", %{conn: conn} do + # "`last: N` must always be acompanied by either a `before:` argument to + # the query, or an explicit `count:` option to the `from_query` call. + # Otherwise it is impossible to derive the required offset." + # https://hexdocs.pm/absinthe_relay/Absinthe.Relay.Connection.html#from_query/4 + # + # This test ensures support for a 'count' argument. + + transaction = insert(:transaction) |> with_block() + + insert(:internal_transaction, + transaction: transaction, + index: 2, + block_hash: transaction.block_hash, + block_index: 2 + ) + + insert(:internal_transaction, + transaction: transaction, + index: 0, + block_hash: transaction.block_hash, + block_index: 0 + ) + + insert(:internal_transaction, + transaction: transaction, + index: 1, + block_hash: transaction.block_hash, + block_index: 1 + ) + + query = """ + query ($hash: FullHash!, $last: Int!, $count: Int!) { + transaction(hash: $hash) { + internal_transactions(last: $last, count: $count) { + edges { + node { + index + transaction_hash + } + } + } + } + } + """ + + variables = %{ + "hash" => to_string(transaction.hash), + "last" => 1, + "count" => 3 + } + + [internal_transaction] = + conn + |> post("/graphql", query: query, variables: variables) + |> json_response(200) + |> get_in(["data", "transaction", "internal_transactions", "edges"]) + + assert internal_transaction["node"]["index"] == 2 + end + + test "pagination support with 'first' and 'after' arguments", %{conn: conn} do + transaction = insert(:transaction) |> with_block() + + for index <- 0..5 do + insert(:internal_transaction_create, + transaction: transaction, + index: index, + block_hash: transaction.block_hash, + block_index: index + ) + end + + query1 = """ + query ($hash: FullHash!, $first: Int!) { + transaction(hash: $hash) { + internal_transactions(first: $first) { + page_info { + has_next_page + has_previous_page + } + edges { + node { + index + transaction_hash + } + cursor + } + } + } + } + """ + + variables1 = %{ + "hash" => to_string(transaction.hash), + "first" => 2 + } + + conn = post(conn, "/graphql", query: query1, variables: variables1) + + %{"data" => %{"transaction" => %{"internal_transactions" => page1}}} = json_response(conn, 200) + + assert page1["page_info"] == %{"has_next_page" => true, "has_previous_page" => false} + assert Enum.all?(page1["edges"], &(&1["node"]["index"] in 0..1)) + + last_cursor_page1 = + page1 + |> Map.get("edges") + |> List.last() + |> Map.get("cursor") + + query2 = """ + query ($hash: FullHash!, $first: Int!, $after: String!) { + transaction(hash: $hash) { + internal_transactions(first: $first, after: $after) { + page_info { + has_next_page + has_previous_page + } + edges { + node { + index + transaction_hash + } + cursor + } + } + } + } + """ + + variables2 = %{ + "hash" => to_string(transaction.hash), + "first" => 2, + "after" => last_cursor_page1 + } + + page2 = + conn + |> post("/graphql", query: query2, variables: variables2) + |> json_response(200) + |> get_in(["data", "transaction", "internal_transactions"]) + + assert page2["page_info"] == %{"has_next_page" => true, "has_previous_page" => true} + assert Enum.all?(page2["edges"], &(&1["node"]["index"] in 2..3)) + + last_cursor_page2 = + page2 + |> Map.get("edges") + |> List.last() + |> Map.get("cursor") + + variables3 = %{ + "hash" => to_string(transaction.hash), + "first" => 2, + "after" => last_cursor_page2 + } + + page3 = + conn + |> post("/graphql", query: query2, variables: variables3) + |> json_response(200) + |> get_in(["data", "transaction", "internal_transactions"]) + + assert page3["page_info"] == %{"has_next_page" => false, "has_previous_page" => true} + assert Enum.all?(page3["edges"], &(&1["node"]["index"] in 4..5)) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/schema/subscription/token_transfers_test.exs b/apps/block_scout_web/test/block_scout_web/schema/subscription/token_transfers_test.exs new file mode 100644 index 0000000..4383457 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/schema/subscription/token_transfers_test.exs @@ -0,0 +1,117 @@ +defmodule BlockScoutWeb.Schema.Subscription.TokenTransfersTest do + use BlockScoutWeb.SubscriptionCase + import Mox + + alias BlockScoutWeb.Notifier + + describe "token_transfers field" do + setup :set_mox_global + + setup do + configuration = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint) + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, pubsub_server: BlockScoutWeb.PubSub) + + :ok + + on_exit(fn -> + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, configuration) + end) + end + + test "with valid argument, returns all expected fields", %{socket: socket} do + transaction = insert(:transaction) + token_transfer = insert(:token_transfer, transaction: transaction) + address_hash = to_string(token_transfer.token_contract_address_hash) + + subscription = """ + subscription ($hash: AddressHash!) { + token_transfers(token_contract_address_hash: $hash) { + amount + from_address_hash + to_address_hash + token_contract_address_hash + transaction_hash + } + } + """ + + variables = %{"hash" => address_hash} + + ref = push_doc(socket, subscription, variables: variables) + + assert_reply(ref, :ok, %{subscriptionId: subscription_id}) + + Notifier.handle_event({:chain_event, :token_transfers, :realtime, [token_transfer]}) + + expected = %{ + result: %{ + data: %{ + "token_transfers" => [ + %{ + "amount" => to_string(token_transfer.amount), + "from_address_hash" => to_string(token_transfer.from_address_hash), + "to_address_hash" => to_string(token_transfer.to_address_hash), + "token_contract_address_hash" => to_string(token_transfer.token_contract_address_hash), + "transaction_hash" => to_string(token_transfer.transaction_hash) + } + ] + } + }, + subscriptionId: subscription_id + } + + assert_push("subscription:data", push) + assert push == expected + end + + test "ignores irrelevant tokens", %{socket: socket} do + transaction = insert(:transaction) + [token_transfer1, token_transfer2] = insert_list(2, :token_transfer, transaction: transaction) + address_hash1 = to_string(token_transfer1.token_contract_address_hash) + + subscription = """ + subscription ($hash: AddressHash!) { + token_transfers(token_contract_address_hash: $hash) { + amount + token_contract_address_hash + } + } + """ + + variables = %{"hash" => address_hash1} + + ref = push_doc(socket, subscription, variables: variables) + + assert_reply(ref, :ok, %{subscriptionId: _subscription_id}) + + Notifier.handle_event({:chain_event, :token_transfers, :realtime, [token_transfer2]}) + + refute_push("subscription:data", _push) + end + + test "ignores non-realtime updates", %{socket: socket} do + transaction = insert(:transaction) + token_transfer = insert(:token_transfer, transaction: transaction) + address_hash = to_string(token_transfer.token_contract_address_hash) + + subscription = """ + subscription ($hash: AddressHash!) { + token_transfers(token_contract_address_hash: $hash) { + amount + token_contract_address_hash + } + } + """ + + variables = %{"hash" => address_hash} + + ref = push_doc(socket, subscription, variables: variables) + + assert_reply(ref, :ok, %{subscriptionId: _subscription_id}) + + Notifier.handle_event({:chain_event, :token_transfers, :catchup, [token_transfer]}) + + refute_push("subscription:data", _push) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/social_media_test.exs b/apps/block_scout_web/test/block_scout_web/social_media_test.exs new file mode 100644 index 0000000..8a102a6 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/social_media_test.exs @@ -0,0 +1,25 @@ +defmodule BlockScoutWeb.SocialMediaTest do + use Explorer.DataCase + + alias BlockScoutWeb.SocialMedia + + test "it filters out unsupported services" do + Application.put_env( + :block_scout_web, + BlockScoutWeb.SocialMedia, + twitter: "MyTwitterProfile", + myspace: "MyAwesomeProfile" + ) + + links = SocialMedia.links() + assert Keyword.has_key?(links, :twitter) + refute Keyword.has_key?(links, :myspace) + end + + test "it prepends the service url" do + Application.put_env(:block_scout_web, BlockScoutWeb.SocialMedia, twitter: "MyTwitterProfile") + + links = SocialMedia.links() + assert links[:twitter] == "https://www.twitter.com/MyTwitterProfile" + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/abi_encoded_value_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/abi_encoded_value_view_test.exs new file mode 100644 index 0000000..2953bb8 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/abi_encoded_value_view_test.exs @@ -0,0 +1,127 @@ +defmodule BlockScoutWeb.ABIEncodedValueViewTest do + use BlockScoutWeb.ConnCase, async: true + + alias BlockScoutWeb.ABIEncodedValueView + + defp value_html(type, value) do + type + |> ABIEncodedValueView.value_html(value) + |> case do + :error -> + raise "failed to generate html" + + other -> + other + end + |> Phoenix.HTML.Safe.to_iodata() + |> IO.iodata_to_binary() + end + + defp copy_text(type, value) do + type + |> ABIEncodedValueView.copy_text(value) + |> case do + :error -> + raise "failed to generate copy text" + + other -> + other + end + |> Phoenix.HTML.Safe.to_iodata() + |> IO.iodata_to_binary() + end + + describe "value_html/2" do + test "it formats addresses as links" do + address = "0x0000000000000000000000000000000000000000" + address_bytes = address |> String.trim_leading("0x") |> Base.decode16!() + + expected = ~s(#{address}) + + assert value_html("address", address_bytes) == expected + end + + test "it formats lists with newlines and spaces" do + expected = + String.trim(""" + [ + 1, + 2, + 3, + 4 + ] + """) + + assert value_html("uint[]", [1, 2, 3, 4]) == expected + end + + test "it formats nested lists with nested depth" do + expected = + String.trim(""" + [ + [ + 1, + 2 + ], + [ + 3, + 4 + ] + ] + """) + + assert value_html("uint[][]", [[1, 2], [3, 4]]) == expected + end + + test "it formats lists of addresses as a list of links" do + address = "0x0000000000000000000000000000000000000000" + address_link = ~s(#{address}) + + expected = + String.trim(""" + [ + #{address_link}, + #{address_link}, + #{address_link}, + #{address_link} + ] + """) + + address_bytes = "0x0000000000000000000000000000000000000000" |> String.trim_leading("0x") |> Base.decode16!() + + assert value_html("address[4]", [address_bytes, address_bytes, address_bytes, address_bytes]) == expected + end + + test "it renders :dynamic values as bytes" do + assert value_html("uint", {:dynamic, <<1>>}) == "0x01" + end + + test "it renders :tuple values as string" do + assert value_html("(uint256)", {123}) == "(123)" + end + end + + describe "copy_text/2" do + test "it skips link formatting of addresses" do + address = "0x0000000000000000000000000000000000000000" + address_bytes = address |> String.trim_leading("0x") |> Base.decode16!() + + assert copy_text("address", address_bytes) == address + end + + test "it skips the formatting when copying lists" do + assert copy_text("uint[4]", [1, 2, 3, 4]) == "[1, 2, 3, 4]" + end + + test "it copies bytes as their hex representation" do + hex = "0xffffff" + bytes = hex |> String.trim_leading("0x") |> Base.decode16!(case: :lower) + + assert copy_text("bytes", bytes) == hex + end + + test "it copies :dynamic values as bytes" do + assert copy_text("uint", {:dynamic, <<1>>}) == "0x01" + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/address_coin_balance_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/address_coin_balance_view_test.exs new file mode 100644 index 0000000..bc07e1c --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/address_coin_balance_view_test.exs @@ -0,0 +1,62 @@ +defmodule BlockScoutWeb.AddressCoinBalanceViewTest do + use BlockScoutWeb.ConnCase, async: true + + alias BlockScoutWeb.AddressCoinBalanceView + alias Explorer.Chain.Wei + + describe "format/1" do + test "format the wei value in ether" do + wei = Wei.from(Decimal.new(1_340_000_000), :gwei) + + assert AddressCoinBalanceView.format(wei) == "1.34 ETH" + end + + test "format negative values" do + wei = Wei.from(Decimal.new(-1_340_000_000), :gwei) + + assert AddressCoinBalanceView.format(wei) == "-1.34 ETH" + end + end + + describe "delta_arrow/1" do + test "return up pointing arrow for positive value" do + value = Decimal.new(123) + + assert AddressCoinBalanceView.delta_arrow(value) == "▲" + end + + test "return down pointing arrow for negative value" do + value = Decimal.new(-123) + + assert AddressCoinBalanceView.delta_arrow(value) == "▼" + end + end + + describe "delta_sign/1" do + test "return Positive for positive value" do + value = Decimal.new(123) + + assert AddressCoinBalanceView.delta_sign(value) == "Positive" + end + + test "return Negative for negative value" do + value = Decimal.new(-123) + + assert AddressCoinBalanceView.delta_sign(value) == "Negative" + end + end + + describe "format_delta/1" do + test "format positive values" do + value = Decimal.new(1_340_000_000_000_000_000) + + assert AddressCoinBalanceView.format_delta(value) == "1.34 ETH" + end + + test "format negative values" do + value = Decimal.new(-1_340_000_000_000_000_000) + + assert AddressCoinBalanceView.format_delta(value) == "1.34 ETH" + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/address_contract_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/address_contract_view_test.exs new file mode 100644 index 0000000..189c740 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/address_contract_view_test.exs @@ -0,0 +1,79 @@ +defmodule BlockScoutWeb.AddressContractViewTest do + use BlockScoutWeb.ConnCase, async: true + + alias BlockScoutWeb.AddressContractView + + doctest BlockScoutWeb.AddressContractView + + describe "format_optimization_text/1" do + test "returns \"true\" for the boolean true" do + assert AddressContractView.format_optimization_text(true) == "true" + end + + test "returns \"false\" for the boolean false" do + assert AddressContractView.format_optimization_text(false) == "false" + end + end + + describe "contract_lines_with_index/1" do + test "returns a list of tuples containing two strings each" do + code = """ + pragma solidity >=0.4.22 <0.6.0; + + struct Proposal { + uint voteCount; + } + + address chairperson; + mapping(address => Voter) voters; + Proposal[] proposals; + + constructor(uint8 _numProposals) public { + chairperson = msg.sender; + voters[chairperson].weight = 1; + proposals.length = _numProposals; + } + """ + + result = AddressContractView.contract_lines_with_index(code) + + assert result == [ + {"pragma solidity >=0.4.22 <0.6.0;", " 1"}, + {"", " 2"}, + {"struct Proposal {", " 3"}, + {" uint voteCount;", " 4"}, + {"}", " 5"}, + {"", " 6"}, + {"address chairperson;", " 7"}, + {"mapping(address => Voter) voters;", " 8"}, + {"Proposal[] proposals;", " 9"}, + {"", "10"}, + {"constructor(uint8 _numProposals) public {", "11"}, + {" chairperson = msg.sender;", "12"}, + {" voters[chairperson].weight = 1;", "13"}, + {" proposals.length = _numProposals;", "14"}, + {"}", "15"}, + {"", "16"} + ] + end + + test "returns a list of tuples and the second element always has n chars with x lines" do + chars = 3 + lines = 100 + result = AddressContractView.contract_lines_with_index(Enum.join(1..lines, "\n")) + assert Enum.all?(result, fn {_, number} -> String.length(number) == chars end) + end + + test "returns a list of tuples and the first element is just a line from the original string" do + result = AddressContractView.contract_lines_with_index("a\nb\nc\nd\ne") + + assert Enum.map(result, fn {line, _number} -> line end) == [ + "a", + "b", + "c", + "d", + "e" + ] + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/address_decompiled_contract_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/address_decompiled_contract_view_test.exs new file mode 100644 index 0000000..abac26b --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/address_decompiled_contract_view_test.exs @@ -0,0 +1,110 @@ +defmodule BlockScoutWeb.AddressDecompiledContractViewTest do + use Explorer.DataCase + + alias BlockScoutWeb.AddressDecompiledContractView + + describe "highlight_decompiled_code/1" do + test "generate correct html code" do + code = """ + # + # eveem.org 6 Feb 2019 + # Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875 + # + # Let's make the world open source + #  + # + # I failed with these: + # - unknowne77c646d(?) + # - transferFromWithData(address _from, address _to, uint256 _value, bytes _data) + # All the rest is below. + # + + + # Storage definitions and getters + + def storage: + allowance is uint256 => uint256 # mask(256, 0) at storage #2 + stor4 is uint256 => uint8 # mask(8, 0) at storage #4 + + def allowance(address _owner, address _spender) payable:  + require (calldata.size - 4) >= 64 + return allowance[sha3(((320 - 1) and (320 - 1) and _owner), 1), ((320 - 1) and _spender and (320 - 1))] + + + # + # Regular functions - see Tutorial for understanding quirks of the code + # + + + # folder failed in this function - may be terribly long, sorry + def unknownc47d033b(?) payable:  + if (calldata.size - 4) < 32: + revert + else: + if not (320 - 1) or not cd[4]: + revert + else: + mem[0] = (320 - 1) and (320 - 1) and cd[4] + mem[32] = 4 + mem[96] = bool(stor4[((320 - 1) and (320 - 1) and cd[4])]) + return bool(stor4[((320 - 1) and (320 - 1) and cd[4])]) + + def _fallback() payable: # default function + revert + """ + + result = AddressDecompiledContractView.highlight_decompiled_code(code) + + assert result == + " #\n # eveem.org 6 Feb 2019\n # Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875\n #\n # Let's make the world open source\n # \n #\n # I failed with these:\n # - unknowne77c646d(?)\n # - transferFromWithData(address _from, address _to, uint256 _value, bytes _data)\n # All the rest is below.\n #\n\n\n # Storage definitions and getters\n\n def storage:\n allowance is uint256 => uint256 # mask(256, 0) at storage #2\n stor4 is uint256 => uint8 # mask(8, 0) at storage #4\n\n def allowance(address _owner, address _spender) payable: \n require (calldata.size - 4) >= 64\n return allowance[sha3(((320 - 1) and (320 - 1) and _owner), 1), ((320 - 1) and _spender and (320 - 1))]\n\n\n #\n # Regular functions - see Tutorial for understanding quirks of the code\n #\n\n\n # folder failed in this function - may be terribly long, sorry\n def unknownc47d033b(?) payable: \n if (calldata.size - 4) < 32:\n revert\n else:\n if not (320 - 1) or not cd[4]:\n revert\n else:\n mem[0] = (320 - 1) and (320 - 1) and cd[4]\n mem[32] = 4\n mem[96] = bool(stor4[((320 - 1) and (320 - 1) and cd[4])])\n return bool(stor4[((320 - 1) and (320 - 1) and cd[4])])\n\n def _fallback() payable: # default function\n revert\n\n\n" + end + + test "adds style span to every line" do + code = """ + # + # eveem.org 6 Feb 2019 + # Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875 + # + # Let's make the world open source + #  + """ + + assert AddressDecompiledContractView.highlight_decompiled_code(code) == + " #\n # eveem.org 6 Feb 2019\n # Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875\n #\n # Let's make the world open source\n # \n\n\n" + end + + test "does not remove bold text" do + code = """ + # + # Eveem.org 26 Apr 2019 + # Decompiled source of 0x06012c8cf97bead5deae237070f9587f8e7a266d + # + # Let's make the world open source + #  + + const name = 'CryptoKitties' + const symbol = 'CK' + const GEN0_STARTING_PRICE = 10^16 + const GEN0_AUCTION_DURATION = (24 * 3600) + const GEN0_CREATION_LIMIT = 45000 + const PROMO_CREATION_LIMIT = 5000 + + """ + + assert AddressDecompiledContractView.highlight_decompiled_code(code) == + "#\n# Eveem.org 26 Apr 2019\n# Decompiled source of 0x06012c8cf97bead5deae237070f9587f8e7a266d\n#\n# Let's make the world open source\n# \n\nconst name = 'CryptoKitties'\nconst symbol = 'CK'\nconst GEN0_STARTING_PRICE = 10^16\nconst GEN0_AUCTION_DURATION = (24 * 3600)\nconst GEN0_CREATION_LIMIT = 45000\nconst PROMO_CREATION_LIMIT = 5000\n\n\n\n" + end + end + + describe "last_decompiled_contract_version/1" do + test "returns last version" do + contract2 = insert(:decompiled_smart_contract, decompiler_version: "v2") + contract1 = insert(:decompiled_smart_contract, decompiler_version: "v1") + contract3 = insert(:decompiled_smart_contract, decompiler_version: "v3") + + result = AddressDecompiledContractView.last_decompiled_contract_version([contract2, contract1, contract3]) + + assert result == contract3 + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/address_token_balance_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/address_token_balance_view_test.exs new file mode 100644 index 0000000..1ac75c9 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/address_token_balance_view_test.exs @@ -0,0 +1,182 @@ +defmodule BlockScoutWeb.AddressTokenBalanceViewTest do + use BlockScoutWeb.ConnCase, async: true + + alias BlockScoutWeb.AddressTokenBalanceView + alias Explorer.Chain + + describe "tokens_count_title/1" do + test "returns the title pluralized" do + token_balances = [ + build(:token_balance), + build(:token_balance) + ] + + assert AddressTokenBalanceView.tokens_count_title(token_balances) == "2 tokens" + end + end + + describe "filter_by_type/2" do + test "filter tokens by the given type" do + token_balance_a = build(:token_balance, token: build(:token, type: "ERC-20")) + token_balance_b = build(:token_balance, token: build(:token, type: "ERC-721")) + + token_balances = [{token_balance_a, token_balance_a.token}, {token_balance_b, token_balance_b.token}] + + assert AddressTokenBalanceView.filter_by_type(token_balances, "ERC-20") == [ + {token_balance_a, token_balance_a.token} + ] + end + end + + describe "sort_by_name/1" do + test "sorts the given tokens by its name" do + token_balance_a = build(:token_balance, token: build(:token, name: "token name")) + token_balance_b = build(:token_balance, token: build(:token, name: "token")) + token_balance_c = build(:token_balance, token: build(:token, name: "atoken")) + + token_balances = [ + token_balance_a, + token_balance_b, + token_balance_c + ] + + expected = [token_balance_c, token_balance_b, token_balance_a] + + assert AddressTokenBalanceView.sort_by_name(token_balances) == expected + end + + test "considers nil values in the bottom of the list" do + token_balance_a = build(:token_balance, token: build(:token, name: nil)) + token_balance_b = build(:token_balance, token: build(:token, name: "token name")) + token_balance_c = build(:token_balance, token: build(:token, name: "token")) + + token_balances = [ + token_balance_a, + token_balance_b, + token_balance_c + ] + + expected = [token_balance_c, token_balance_b, token_balance_a] + + assert AddressTokenBalanceView.sort_by_name(token_balances) == expected + end + + test "considers capitalization" do + token_balance_a = build(:token_balance, token: build(:token, name: "Token")) + token_balance_b = build(:token_balance, token: build(:token, name: "atoken")) + + token_balances = [token_balance_a, token_balance_b] + expected = [token_balance_b, token_balance_a] + + assert AddressTokenBalanceView.sort_by_name(token_balances) == expected + end + end + + describe "sort_by_usd_value_and_name/1" do + test "sorts the given tokens by its name and usd_value" do + token_balance_a = + build(:token_balance, + token: build(:token, name: "token name", decimals: Decimal.new(18)) |> Map.put(:usd_value, Decimal.new(2)), + value: Decimal.new(100_500) + ) + + token_balance_b = + build(:token_balance, + token: + build(:token, name: "token", decimals: Decimal.new(18)) |> Map.put(:usd_value, Decimal.from_float(3.45)), + value: Decimal.new(100_500) + ) + + token_balance_c = + build(:token_balance, + token: build(:token, name: nil, decimals: Decimal.new(18)) |> Map.put(:usd_value, Decimal.new(2)), + value: Decimal.new(100_500) + ) + + token_balance_d = + build(:token_balance, + token: build(:token, name: "Atoken", decimals: Decimal.new(18)) |> Map.put(:usd_value, Decimal.new(1)), + value: Decimal.new(100_500) + ) + + token_balance_e = + build(:token_balance, + token: build(:token, name: "atoken", decimals: Decimal.new(18)) |> Map.put(:usd_value, nil), + value: Decimal.new(100_500) + ) + + token_balance_f = + build(:token_balance, + token: build(:token, name: "Btoken", decimals: Decimal.new(18)) |> Map.put(:usd_value, nil), + value: Decimal.new(100_500) + ) + + token_balance_g = + build(:token_balance, + token: build(:token, name: "Btoken", decimals: Decimal.new(18)) |> Map.put(:usd_value, Decimal.new(1)), + value: Decimal.new(100_500) + ) + + token_balances = [ + {token_balance_a, token_balance_a.token}, + {token_balance_b, token_balance_b.token}, + {token_balance_c, token_balance_c.token}, + {token_balance_d, token_balance_d.token}, + {token_balance_e, token_balance_e.token}, + {token_balance_f, token_balance_f.token}, + {token_balance_g, token_balance_g.token} + ] + + expected = [ + {token_balance_b, token_balance_b.token}, + {token_balance_a, token_balance_a.token}, + {token_balance_c, token_balance_c.token}, + {token_balance_d, token_balance_d.token}, + {token_balance_g, token_balance_g.token}, + {token_balance_e, token_balance_e.token}, + {token_balance_f, token_balance_f.token} + ] + + assert AddressTokenBalanceView.sort_by_usd_value_and_name(token_balances) == expected + end + end + + describe "balance_in_usd/1" do + test "return balance in usd" do + token = + :token + |> build(decimals: Decimal.new(0)) + |> Map.put(:usd_value, Decimal.new(3)) + + token_balance = build(:token_balance, value: Decimal.new(10), token: token) + + result = Chain.balance_in_usd(token_balance) + + assert Decimal.compare(result, 30) == :eq + end + + test "return nil if usd_value is not present" do + token = + :token + |> build(decimals: Decimal.new(0)) + |> Map.put(:usd_value, nil) + + token_balance = build(:token_balance, value: 10, token: token) + + assert Chain.balance_in_usd(token_balance) == nil + end + + test "consider decimals when computing value" do + token = + :token + |> build(decimals: Decimal.new(2)) + |> Map.put(:usd_value, Decimal.new(3)) + + token_balance = build(:token_balance, value: Decimal.new(10), token: token) + + result = Chain.balance_in_usd(token_balance) + + assert Decimal.compare(result, Decimal.from_float(0.3)) == :eq + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/address_transaction_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/address_transaction_view_test.exs new file mode 100644 index 0000000..666f06b --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/address_transaction_view_test.exs @@ -0,0 +1,5 @@ +defmodule BlockScoutWeb.AddressTransactionViewTest do + use BlockScoutWeb.ConnCase, async: true + + doctest BlockScoutWeb.AddressTransactionView +end diff --git a/apps/block_scout_web/test/block_scout_web/views/address_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/address_view_test.exs new file mode 100644 index 0000000..c8e2393 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/address_view_test.exs @@ -0,0 +1,393 @@ +defmodule BlockScoutWeb.AddressViewTest do + use BlockScoutWeb.ConnCase, async: true + + alias Explorer.Chain.{Address, Data, Hash, Transaction} + alias BlockScoutWeb.{AddressView, Endpoint} + + describe "address_partial_selector/4" do + test "for a pending transaction contract creation to address" do + transaction = insert(:transaction, to_address: nil, created_contract_address_hash: nil) + assert AddressView.address_partial_selector(transaction, :to, nil) == "Contract Address Pending" + end + + test "for a pending internal transaction contract creation to address" do + transaction = insert(:transaction, to_address: nil) |> with_block() + + internal_transaction = + insert(:internal_transaction, + index: 1, + transaction: transaction, + to_address: nil, + created_contract_address_hash: nil, + block_hash: transaction.block_hash, + block_index: 1 + ) + + assert "Contract Address Pending" == AddressView.address_partial_selector(internal_transaction, :to, nil) + end + + test "will truncate address" do + transaction = %Transaction{to_address: to_address} = insert(:transaction) + + assert [ + view_module: AddressView, + partial: "_link.html", + address: ^to_address, + contract: false, + truncate: true, + use_custom_tooltip: false + ] = AddressView.address_partial_selector(transaction, :to, nil, true) + end + + test "for a non-contract to address not on address page" do + transaction = %Transaction{to_address: to_address} = insert(:transaction) + + assert [ + view_module: AddressView, + partial: "_link.html", + address: ^to_address, + contract: false, + truncate: false, + use_custom_tooltip: false + ] = AddressView.address_partial_selector(transaction, :to, nil) + end + + test "for a non-contract to address non matching address page" do + transaction = %Transaction{to_address: to_address} = insert(:transaction) + + assert [ + view_module: AddressView, + partial: "_link.html", + address: ^to_address, + contract: false, + truncate: false, + use_custom_tooltip: false + ] = AddressView.address_partial_selector(transaction, :to, nil) + end + + test "for a non-contract to address matching address page" do + transaction = %Transaction{to_address: to_address} = insert(:transaction) + + assert [ + view_module: AddressView, + partial: "_responsive_hash.html", + address: ^to_address, + contract: false, + truncate: false, + use_custom_tooltip: false + ] = AddressView.address_partial_selector(transaction, :to, transaction.to_address) + end + + test "for a contract to address non matching address page" do + contract_address = insert(:contract_address) + transaction = insert(:transaction, to_address: nil, created_contract_address: contract_address) + + assert [ + view_module: AddressView, + partial: "_link.html", + address: ^contract_address, + contract: true, + truncate: false, + use_custom_tooltip: false + ] = AddressView.address_partial_selector(transaction, :to, transaction.to_address) + end + + test "for a contract to address matching address page" do + contract_address = insert(:contract_address) + transaction = insert(:transaction, to_address: nil, created_contract_address: contract_address) + + assert [ + view_module: AddressView, + partial: "_responsive_hash.html", + address: ^contract_address, + contract: true, + truncate: false, + use_custom_tooltip: false + ] = AddressView.address_partial_selector(transaction, :to, contract_address) + end + + test "for a non-contract from address not on address page" do + transaction = %Transaction{to_address: to_address} = insert(:transaction) + + assert [ + view_module: AddressView, + partial: "_link.html", + address: ^to_address, + contract: false, + truncate: false, + use_custom_tooltip: false + ] = AddressView.address_partial_selector(transaction, :to, nil) + end + + test "for a non-contract from address matching address page" do + transaction = %Transaction{from_address: from_address} = insert(:transaction) + + assert [ + view_module: AddressView, + partial: "_responsive_hash.html", + address: ^from_address, + contract: false, + truncate: false, + use_custom_tooltip: false + ] = AddressView.address_partial_selector(transaction, :from, transaction.from_address) + end + end + + describe "balance_block_number/1" do + test "gives empty string with no fetched balance block number present" do + assert AddressView.balance_block_number(%Address{}) == "" + end + + test "gives block number when fetched balance block number is non-nil" do + assert AddressView.balance_block_number(%Address{fetched_coin_balance_block_number: 1_000_000}) == "1000000" + end + end + + describe "balance_percentage_enabled/1" do + test "with non_zero market cap" do + Application.put_env(:block_scout_web, :show_percentage, true) + + assert AddressView.balance_percentage_enabled?(100_500) == true + end + + test "with zero market cap" do + Application.put_env(:block_scout_web, :show_percentage, true) + + assert AddressView.balance_percentage_enabled?(0) == false + end + + test "with switched off show_percentage" do + Application.put_env(:block_scout_web, :show_percentage, false) + + assert AddressView.balance_percentage_enabled?(100_501) == false + end + end + + test "balance_percentage/1" do + Application.put_env(:explorer, :supply, Explorer.Chain.Supply.ProofOfAuthority) + address = insert(:address, fetched_coin_balance: 2_524_608_000_000_000_000_000_000) + + assert "1.0000% Market Cap" = AddressView.balance_percentage(address) + end + + test "balance_percentage with nil total_supply" do + address = insert(:address, fetched_coin_balance: 2_524_608_000_000_000_000_000_000) + + assert "" = AddressView.balance_percentage(address, nil) + end + + describe "contract?/1" do + test "with a smart contract" do + {:ok, code} = Data.cast("0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef") + address = insert(:address, contract_code: code) + assert AddressView.contract?(address) + end + + test "with an account" do + address = insert(:address, contract_code: nil) + refute AddressView.contract?(address) + end + + test "with nil address" do + assert AddressView.contract?(nil) + end + end + + describe "hash/1" do + test "gives a string version of an address's hash" do + address = %Address{ + hash: %Hash{ + byte_count: 20, + bytes: <<139, 243, 141, 71, 100, 146, 144, 100, 242, 212, 211, 165, 101, 32, 167, 106, 179, 223, 65, 91>> + } + } + + assert AddressView.hash(address) == "0x8bf38d4764929064f2d4d3a56520a76ab3df415b" + end + end + + describe "primary_name/1" do + test "gives an address's primary name when present" do + address = insert(:address) + + address_name = insert(:address_name, address: address, primary: true, name: "POA Foundation Wallet") + insert(:address_name, address: address, name: "POA Wallet") + + preloaded_address = Explorer.Repo.preload(address, :names) + + assert AddressView.primary_name(preloaded_address) == address_name.name + end + + test "returns any when no primary available" do + address_name = insert(:address_name, name: "POA Wallet") + preloaded_address = Explorer.Repo.preload(address_name.address, :names) + + assert AddressView.primary_name(preloaded_address) == address_name.name + end + end + + describe "qr_code/1" do + test "it returns an encoded value" do + address = build(:address) + assert {:ok, _} = Base.decode64(AddressView.qr_code(address.hash)) + end + end + + describe "smart_contract_verified?/1" do + test "returns true when smart contract is verified" do + smart_contract = insert(:smart_contract, contract_code_md5: "123") + address = insert(:address, smart_contract: smart_contract) + + assert AddressView.smart_contract_verified?(address) + end + + test "returns false when smart contract is not verified" do + address = insert(:address, smart_contract: nil) + + refute AddressView.smart_contract_verified?(address) + end + end + + describe "smart_contract_with_read_only_functions?/1" do + test "returns true when abi has read only functions" do + smart_contract = + insert( + :smart_contract, + abi: [ + %{ + "constant" => true, + "inputs" => [], + "name" => "get", + "outputs" => [%{"name" => "", "type" => "uint256"}], + "payable" => false, + "stateMutability" => "view", + "type" => "function" + } + ], + contract_code_md5: "123" + ) + + address = insert(:address, smart_contract: smart_contract) + + assert AddressView.smart_contract_with_read_only_functions?(address) + end + + test "returns false when there is no read only functions" do + smart_contract = + insert( + :smart_contract, + abi: [ + %{ + "constant" => false, + "inputs" => [%{"name" => "x", "type" => "uint256"}], + "name" => "set", + "outputs" => [], + "payable" => false, + "stateMutability" => "nonpayable", + "type" => "function" + } + ], + contract_code_md5: "123" + ) + + address = insert(:address, smart_contract: smart_contract) + + refute AddressView.smart_contract_with_read_only_functions?(address) + end + + test "returns false when smart contract is not verified" do + address = insert(:address, smart_contract: nil) + + refute AddressView.smart_contract_with_read_only_functions?(address) + end + end + + describe "token_title/1" do + test "returns the 6 first and 6 last chars of address hash when token has no name" do + token = insert(:token, name: nil) + + hash = to_string(token.contract_address_hash) + expected_hash = String.slice(hash, 0, 8) <> "-" <> String.slice(hash, -6, 6) + assert expected_hash == AddressView.token_title(token) + end + + test "returns name(symbol) when token has name" do + token = insert(:token, name: "super token money", symbol: "ST$") + + assert AddressView.token_title(token) == "super token money (ST$)" + end + end + + describe "current_tab_name/1" do + test "generates the correct tab name for the token path" do + path = address_token_path(Endpoint, :index, "0x4ddr3s") + + assert AddressView.current_tab_name(path) == "Tokens" + end + + test "generates the correct tab name for the transactions path" do + path = address_transaction_path(Endpoint, :index, "0x4ddr3s") + + assert AddressView.current_tab_name(path) == "Transactions" + end + + test "generates the correct tab name for the internal transactions path" do + path = address_internal_transaction_path(Endpoint, :index, "0x4ddr3s") + + assert AddressView.current_tab_name(path) == "Internal Transactions" + end + + test "generates the correct tab name for the contracts path" do + path = address_contract_path(Endpoint, :index, "0x4ddr3s") + + assert AddressView.current_tab_name(path) == "Code" + end + + test "generates the correct tab name for the read_contract path" do + path = address_read_contract_path(Endpoint, :index, "0x4ddr3s") + + assert AddressView.current_tab_name(path) == "Read Contract" + end + + test "generates the correct tab name for the coin_balances path" do + path = address_coin_balance_path(Endpoint, :index, "0x4ddr3s") + + assert AddressView.current_tab_name(path) == "Coin Balance History" + end + + test "generates the correct tab name for the validations path" do + path = address_validation_path(Endpoint, :index, "0x4ddr3s") + + assert AddressView.current_tab_name(path) == "Blocks Validated" + end + end + + describe "short_hash/1" do + test "returns a shortened hash of 6 hex characters" do + address = insert(:address) + assert "0x" <> short_hash = AddressView.short_hash(address) + assert String.length(short_hash) == 6 + end + end + + describe "address_page_title/1" do + test "uses the Smart Contract name when the contract is verified" do + smart_contract = build(:smart_contract, name: "POA") + address = build(:address, smart_contract: smart_contract) + + assert AddressView.address_page_title(address) == "POA (#{address})" + end + + test "uses the string 'Contract' when it's a contract" do + address = build(:contract_address, smart_contract: nil) + + assert AddressView.address_page_title(address) == "Contract #{address}" + end + + test "uses the address hash when it is not a contract" do + address = build(:address, smart_contract: nil) + + assert AddressView.address_page_title(address) == "#{address}" + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/api_docs_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/api_docs_view_test.exs new file mode 100644 index 0000000..cc1c9f1 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/api_docs_view_test.exs @@ -0,0 +1,117 @@ +defmodule BlockScoutWeb.ApiDocsViewTest do + use BlockScoutWeb.ConnCase, async: false + + alias BlockScoutWeb.APIDocsView + + describe "api_url/1" do + setup do + original = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint) + + on_exit(fn -> Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, original) end) + + :ok + end + + test "adds slash before path" do + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, + url: [scheme: "https", host: "blockscout.com", port: 9999, api_path: "/chain/dog"] + ) + + assert APIDocsView.api_url() == "https://blockscout.com/chain/dog/api" + end + + test "does not add slash to empty path" do + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, + url: [scheme: "https", host: "blockscout.com", port: 9999, api_path: ""] + ) + + assert APIDocsView.api_url() == "https://blockscout.com/api" + end + + test "localhost return with port" do + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, + url: [scheme: "http", host: "localhost"], + http: [port: 9999] + ) + + assert APIDocsView.api_url() == "http://localhost:9999/api" + end + end + + describe "blockscout_url/2" do + setup do + original = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint) + + on_exit(fn -> Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, original) end) + + :ok + end + + test "set_path = true returns url with path" do + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, + url: [scheme: "https", host: "blockscout.com", api_path: "/eth/mainnet", path: "/eth/mainnet"] + ) + + assert APIDocsView.blockscout_url(true, true) == "https://blockscout.com/eth/mainnet" + end + + test "set_path = false returns url w/out path" do + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, + url: [scheme: "https", host: "blockscout.com", api_path: "/eth/mainnet", path: "/eth/mainnet"] + ) + + assert APIDocsView.blockscout_url(false) == "https://blockscout.com" + end + + test "set_path = true is_api returns url with api_path" do + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, + url: [scheme: "https", host: "blockscout.com", api_path: "/eth/mainnet", path: "/"] + ) + + assert APIDocsView.blockscout_url(true, true) == "https://blockscout.com/eth/mainnet" + end + + test "set_path = true is_api returns url with path" do + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, + url: [scheme: "https", host: "blockscout.com", api_path: "/eth/mainnet", path: "/eth/mainnet2"] + ) + + assert APIDocsView.blockscout_url(true, false) == "https://blockscout.com/eth/mainnet2" + end + end + + describe "eth_rpc_api_url/1" do + setup do + original = Application.get_env(:block_scout_web, BlockScoutWeb.Endpoint) + + on_exit(fn -> Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, original) end) + + :ok + end + + test "adds slash before path" do + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, + url: [scheme: "https", host: "blockscout.com", port: 9999, api_path: "/chain/dog"] + ) + + assert APIDocsView.eth_rpc_api_url() == "https://blockscout.com/chain/dog/api/eth-rpc" + end + + test "does not add slash to empty path" do + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, + url: [scheme: "https", host: "blockscout.com", port: 9999, path: ""] + ) + + assert APIDocsView.eth_rpc_api_url() == "https://blockscout.com/api/eth-rpc" + end + + test "localhost return with port" do + Application.put_env(:block_scout_web, BlockScoutWeb.Endpoint, + url: [scheme: "http", host: "localhost"], + http: [port: 9999] + ) + + assert APIDocsView.eth_rpc_api_url() == "http://localhost:9999/api/eth-rpc" + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/block_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/block_view_test.exs new file mode 100644 index 0000000..eea5baf --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/block_view_test.exs @@ -0,0 +1,98 @@ +defmodule BlockScoutWeb.BlockViewTest do + use BlockScoutWeb.ConnCase, async: true + + alias BlockScoutWeb.BlockView + alias Explorer.Repo + + describe "average_gas_price/1" do + test "returns an average of the gas prices for a block's transactions with the unit value" do + block = insert(:block) + + Enum.each(1..10, fn index -> + :transaction + |> insert(gas_price: 10_000_000_000 * index) + |> with_block(block) + end) + + assert "55 Gwei" == BlockView.average_gas_price(Repo.preload(block, [:transactions])) + end + end + + describe "block_type/1" do + test "returns Block" do + block = insert(:block, nephews: []) + + assert BlockView.block_type(block) == "Block" + end + + test "returns Reorg" do + reorg = insert(:block, consensus: false, nephews: []) + + assert BlockView.block_type(reorg) == "Reorg" + end + + test "returns Uncle" do + uncle = insert(:block, consensus: false) + insert(:block_second_degree_relation, uncle_hash: uncle.hash) + preloaded = Repo.preload(uncle, :nephews) + + assert BlockView.block_type(preloaded) == "Uncle" + end + end + + describe "formatted_timestamp/1" do + test "returns a formatted timestamp string for a block" do + block = insert(:block) + + assert Timex.format!(block.timestamp, "%b-%d-%Y %H:%M:%S %p %Z", :strftime) == + BlockView.formatted_timestamp(block) + end + end + + describe "show_reward?/1" do + test "returns false when list of rewards is empty" do + assert BlockView.show_reward?([]) == false + end + + test "returns true when list of rewards is not empty" do + block = insert(:block) + validator = insert(:reward, address_hash: block.miner_hash, block_hash: block.hash, address_type: :validator) + + assert BlockView.show_reward?([validator]) == true + end + end + + describe "combined_rewards_value/1" do + test "returns all the reward values summed up and formatted into a String" do + block = insert(:block) + + insert( + :reward, + address_hash: block.miner_hash, + block_hash: block.hash, + address_type: :validator, + reward: Decimal.new(1_000_000_000_000_000_000) + ) + + insert( + :reward, + address_hash: block.miner_hash, + block_hash: block.hash, + address_type: :emission_funds, + reward: Decimal.new(1_000_000_000_000_000_000) + ) + + insert( + :reward, + address_hash: block.miner_hash, + block_hash: block.hash, + address_type: :uncle, + reward: Decimal.new(1_000_042_000_000_000_000) + ) + + block = Repo.preload(block, :rewards) + + assert BlockView.combined_rewards_value(block) == "3.000042 ETH" + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/currency_helpers_test.exs b/apps/block_scout_web/test/block_scout_web/views/currency_helpers_test.exs new file mode 100644 index 0000000..172a06a --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/currency_helpers_test.exs @@ -0,0 +1,84 @@ +defmodule BlockScoutWeb.CurrencyHelpersTest do + use ExUnit.Case + + alias BlockScoutWeb.CurrencyHelpers + + doctest BlockScoutWeb.CurrencyHelpers, import: true + + describe "format_according_to_decimals/1" do + test "formats the amount as value considering the given decimals" do + amount = Decimal.new(205_000_000_000_000) + decimals = Decimal.new(12) + + assert CurrencyHelpers.format_according_to_decimals(amount, decimals) == "205" + end + + test "considers the decimal places according to the given decimals" do + amount = Decimal.new(205_000) + decimals = Decimal.new(12) + + assert CurrencyHelpers.format_according_to_decimals(amount, decimals) == "0.000000205" + end + + test "does not consider right zeros in decimal places" do + amount = Decimal.new(90_000_000) + decimals = Decimal.new(6) + + assert CurrencyHelpers.format_according_to_decimals(amount, decimals) == "90" + end + + test "returns the full number when there is no right zeros in decimal places" do + amount = Decimal.new(9_324_876) + decimals = Decimal.new(6) + + assert CurrencyHelpers.format_according_to_decimals(amount, decimals) == "9.324876" + end + + test "formats the value considering thousands separators" do + amount = Decimal.new(1_000_450) + decimals = Decimal.new(2) + + assert CurrencyHelpers.format_according_to_decimals(amount, decimals) == "10,004.5" + end + + test "supports value as integer" do + amount = 1_000_450 + decimals = Decimal.new(2) + + assert CurrencyHelpers.format_according_to_decimals(amount, decimals) == "10,004.5" + end + + test "considers 0 when decimals is nil" do + amount = 1_000_450 + decimals = nil + + assert CurrencyHelpers.format_according_to_decimals(amount, decimals) == "1,000,450" + end + end + + describe "format_integer_to_currency/1" do + test "formats the integer value to a currency format" do + assert CurrencyHelpers.format_integer_to_currency(9000) == "9,000" + end + end + + describe "divide_decimals/2" do + test "divide by the given decimal amount" do + result = CurrencyHelpers.divide_decimals(Decimal.new(1000), Decimal.new(3)) + expected_result = Decimal.new(1) + assert Decimal.compare(result, expected_result) == :eq + end + + test "work when number of decimals is bigger than the number's digits" do + result = CurrencyHelpers.divide_decimals(Decimal.new(1000), Decimal.new(5)) + expected_result = Decimal.from_float(0.01) + assert Decimal.compare(result, expected_result) == :eq + end + + test "return the same number when number of decimals is 0" do + result = CurrencyHelpers.divide_decimals(Decimal.new(1000), Decimal.new(0)) + expected_result = Decimal.new(1000) + assert Decimal.compare(result, expected_result) == :eq + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/error_helpers_test.exs b/apps/block_scout_web/test/block_scout_web/views/error_helpers_test.exs new file mode 100644 index 0000000..ffa3899 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/error_helpers_test.exs @@ -0,0 +1,42 @@ +defmodule BlockScoutWeb.ErrorHelpersTest do + use BlockScoutWeb.ConnCase, async: true + import Phoenix.HTML.Tag, only: [content_tag: 3] + + alias BlockScoutWeb.ErrorHelpers + + @changeset %{ + errors: [ + contract_code: {"has already been taken", []} + ] + } + + describe "error_tag tests" do + test "error_tag/2 renders spans with default options" do + assert ErrorHelpers.error_tag(@changeset, :contract_code) == [ + content_tag(:span, "has already been taken", class: "has-error") + ] + end + + test "error_tag/3 overrides default options" do + assert ErrorHelpers.error_tag(@changeset, :contract_code, class: "something-else") == [ + content_tag(:span, "has already been taken", class: "something-else") + ] + end + + test "error_tag/3 merges given options with default ones" do + assert ErrorHelpers.error_tag(@changeset, :contract_code, data_hidden: true) == [ + content_tag(:span, "has already been taken", class: "has-error", data_hidden: true) + ] + end + end + + describe "translate_error/1 tests" do + test "returns errors" do + assert ErrorHelpers.translate_error({"test", []}) == "test" + end + + test "returns errors with count" do + assert ErrorHelpers.translate_error({"%{count} test", [count: 1]}) == "1 test" + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/error_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/error_view_test.exs new file mode 100644 index 0000000..6babf7c --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/error_view_test.exs @@ -0,0 +1,22 @@ +defmodule BlockScoutWeb.ErrorViewTest do + use BlockScoutWeb.ConnCase, async: true + + # Bring render/3 and render_to_string/3 for testing custom views + import Phoenix.View + + test "renders 404.html" do + assert render_to_string(BlockScoutWeb.ErrorView, "404.html", []) == "Page not found" + end + + test "renders 422.html" do + assert render_to_string(BlockScoutWeb.ErrorView, "422.html", []) == "Unprocessable entity" + end + + test "render 500.html" do + assert render_to_string(BlockScoutWeb.ErrorView, "500.html", []) == "Internal server error" + end + + test "render any other" do + assert render_to_string(BlockScoutWeb.ErrorView, "505.html", []) == "Internal server error" + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/internal_transaction_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/internal_transaction_view_test.exs new file mode 100644 index 0000000..987f89f --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/internal_transaction_view_test.exs @@ -0,0 +1,40 @@ +defmodule BlockScoutWeb.InternalTransactionViewTest do + use BlockScoutWeb.ConnCase, async: true + + alias BlockScoutWeb.InternalTransactionView + alias Explorer.Chain.InternalTransaction + + doctest BlockScoutWeb.InternalTransactionView + + describe "type/1" do + test "returns the correct string when the type is :call and call type is :call" do + internal_transaction = %InternalTransaction{type: :call, call_type: :call} + + assert InternalTransactionView.type(internal_transaction) == "Call" + end + + test "returns the correct string when the type is :call and call type is :delegate_call" do + internal_transaction = %InternalTransaction{type: :call, call_type: :delegatecall} + + assert InternalTransactionView.type(internal_transaction) == "Delegate Call" + end + + test "returns the correct string when the type is :create" do + internal_transaction = %InternalTransaction{type: :create} + + assert InternalTransactionView.type(internal_transaction) == "Create" + end + + test "returns the correct string when the type is :selfdestruct" do + internal_transaction = %InternalTransaction{type: :selfdestruct} + + assert InternalTransactionView.type(internal_transaction) == "Self-Destruct" + end + + test "returns the correct string when the type is :reward" do + internal_transaction = %InternalTransaction{type: :reward} + + assert InternalTransactionView.type(internal_transaction) == "Reward" + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/layout_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/layout_view_test.exs new file mode 100644 index 0000000..b194934 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/layout_view_test.exs @@ -0,0 +1,203 @@ +defmodule BlockScoutWeb.LayoutViewTest do + use BlockScoutWeb.ConnCase + + alias BlockScoutWeb.LayoutView + + test "configured_social_media_services/0" do + assert length(LayoutView.configured_social_media_services()) > 0 + end + + setup do + on_exit(fn -> + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, []) + end) + end + + describe "logo/0" do + test "use the environment logo when it's configured" do + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, logo: "custom/logo.png") + + assert LayoutView.logo() == "custom/logo.png" + end + + test "logo is nil when there is no env configured for it" do + assert LayoutView.logo() == nil + end + end + + describe "subnetwork_title/0" do + test "use the environment subnetwork title when it's configured" do + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, subnetwork: "Subnetwork Test") + + assert LayoutView.subnetwork_title() == "Subnetwork Test" + end + + test "use the default subnetwork title when there is no env configured for it" do + assert LayoutView.subnetwork_title() == "Sokol" + end + end + + describe "network_title/0" do + test "use the environment network title when it's configured" do + Application.put_env(:block_scout_web, BlockScoutWeb.Chain, network: "Custom Network") + + assert LayoutView.network_title() == "Custom Network" + end + + test "use the default network title when there is no env configured for it" do + assert LayoutView.network_title() == "POA" + end + end + + describe "release_link/1" do + test "set empty string if no blockscout version configured" do + Application.put_env(:block_scout_web, :blockscout_version, nil) + + assert LayoutView.release_link(nil) == "" + end + + test "set empty string if blockscout version is empty string" do + Application.put_env(:block_scout_web, :blockscout_version, "") + + assert LayoutView.release_link("") == "" + end + + test "use the default value when there is no release_link env configured for it" do + Application.put_env(:block_scout_web, :release_link, nil) + + assert LayoutView.release_link("v1.3.4-beta") == + {:safe, + ~s(v1.3.4-beta)} + end + + test "use the default value when empty release_link env configured for it" do + Application.put_env(:block_scout_web, :release_link, "") + + assert LayoutView.release_link("v1.3.4-beta") == + {:safe, + ~s(v1.3.4-beta)} + end + + test "use the environment release link when it's configured" do + Application.put_env( + :block_scout_web, + :release_link, + "https://github.com/poanetwork/blockscout/releases/tag/v1.3.4-beta" + ) + + assert LayoutView.release_link("v1.3.4-beta") == + {:safe, + ~s(v1.3.4-beta)} + end + end + + @supported_chains_pattern ~s([ { "title": "RSK", "url": "https://blockscout.com/rsk/mainnet", "other?": true }, { "title": "Sokol", "url": "https://blockscout.com/poa/sokol", "test_net?": true }, { "title": "POA", "url": "https://blockscout.com/poa/core" }, { "title": "LUKSO L14", "url": "https://blockscout.com/lukso/l14", "test_net?": true, "hide_in_dropdown?": true } ]) + + describe "other_networks/0" do + test "get networks list based on env variables" do + Application.put_env(:block_scout_web, :other_networks, @supported_chains_pattern) + + assert LayoutView.other_networks() == [ + %{ + title: "POA", + url: "https://blockscout.com/poa/core" + }, + %{ + title: "RSK", + url: "https://blockscout.com/rsk/mainnet", + other?: true + }, + %{ + title: "LUKSO L14", + url: "https://blockscout.com/lukso/l14", + test_net?: true, + hide_in_dropdown?: true + } + ] + end + + test "get empty networks list if SUPPORTED_CHAINS is not parsed" do + Application.put_env(:block_scout_web, :other_networks, "not a valid json") + + assert LayoutView.other_networks() == [] + end + end + + describe "main_nets/1" do + test "get all main networks list based on env variables" do + Application.put_env(:block_scout_web, :other_networks, @supported_chains_pattern) + + assert LayoutView.main_nets(LayoutView.other_networks()) == [ + %{ + title: "POA", + url: "https://blockscout.com/poa/core" + }, + %{ + title: "RSK", + url: "https://blockscout.com/rsk/mainnet", + other?: true + } + ] + end + end + + describe "test_nets/1" do + test "get all networks list based on env variables" do + Application.put_env(:block_scout_web, :other_networks, @supported_chains_pattern) + + assert LayoutView.test_nets(LayoutView.other_networks()) == [ + %{ + title: "LUKSO L14", + url: "https://blockscout.com/lukso/l14", + test_net?: true, + hide_in_dropdown?: true + } + ] + end + end + + describe "dropdown_nets/0" do + test "get all dropdown networks list based on env variables" do + Application.put_env(:block_scout_web, :other_networks, @supported_chains_pattern) + + assert LayoutView.dropdown_nets() == [ + %{ + title: "POA", + url: "https://blockscout.com/poa/core" + }, + %{ + title: "RSK", + url: "https://blockscout.com/rsk/mainnet", + other?: true + } + ] + end + end + + describe "dropdown_head_main_nets/0" do + test "get dropdown all main networks except those of 'other' type list based on env variables" do + Application.put_env(:block_scout_web, :other_networks, @supported_chains_pattern) + + assert LayoutView.dropdown_head_main_nets() == [ + %{ + title: "POA", + url: "https://blockscout.com/poa/core" + } + ] + end + end + + describe "dropdown_other_nets/0" do + test "get dropdown networks of 'other' type list based on env variables" do + Application.put_env(:block_scout_web, :other_networks, @supported_chains_pattern) + + assert LayoutView.dropdown_other_nets() == [ + %{ + title: "RSK", + url: "https://blockscout.com/rsk/mainnet", + other?: true + } + ] + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/render_helpers_test.exs b/apps/block_scout_web/test/block_scout_web/views/render_helpers_test.exs new file mode 100644 index 0000000..5cc0d1f --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/render_helpers_test.exs @@ -0,0 +1,22 @@ +defmodule BlockScoutWeb.RenderHelpersTest do + use BlockScoutWeb.ConnCase, async: true + + alias BlockScoutWeb.{BlockView, RenderHelpers} + + describe "render_partial/1" do + test "renders text" do + assert "test" == RenderHelpers.render_partial("test") + end + + test "renders the proper partial when view_module, partial and args are given" do + block = build(:block) + + assert {:safe, _} = + RenderHelpers.render_partial( + view_module: BlockView, + partial: "_link.html", + block: block + ) + end + end +end diff --git a/apps/block_scout_web/test/block_scout_web/views/search_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/search_view_test.exs new file mode 100644 index 0000000..8bc64ad --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/views/search_view_test.exs @@ -0,0 +1,44 @@ +defmodule BlockScoutWeb.SearchViewTest do + use ExUnit.Case + alias BlockScoutWeb.SearchView + + test "highlight_search_result/2 returns search result if query doesn't match" do + query = "test" + search_result = "qwerty" + res = SearchView.highlight_search_result(search_result, query) + IO.inspect(res) + + assert res == {:safe, search_result} + end + + test "highlight_search_result/2 returns safe HTML of unsafe search result if query doesn't match" do + query = "test" + search_result = "qwe1'\">

    _rx>&`ToQjX`;C0VSj{ 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 GGxoo2g_>Rf literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/rinkeby-block-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/rinkeby-block-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..66e5f1940bff4c4903553b36e40e1acc41166511 GIT binary patch literal 11333 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/rinkeby-page-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/rinkeby-page-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..8857aa685af4a02563946dba1aef9d8b0446a449 GIT binary patch literal 13184 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/rinkeby-page-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/rinkeby-page-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8abedadcdbb0cc1586e903ef4f7eac209f21fc5f GIT binary patch literal 28743 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/rinkeby-tx-not-found.png b/apps/block_scout_web/assets/static/images/errors-img/rinkeby-tx-not-found.png new file mode 100644 index 0000000000000000000000000000000000000000..63194c65458e669a1fd7b778d61b16178d13e108 GIT binary patch literal 10356 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 literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/rinkeby-tx-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/rinkeby-tx-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3b19876ae0c49630d33efc901c0c6bf6697513b6 GIT binary patch literal 21581 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 R1D{`B(a|!{tWvj$_&0TcE3g0n literal 0 HcmV?d00001 diff --git a/apps/block_scout_web/assets/static/images/errors-img/rinnkeby-block-not-found@2x.png b/apps/block_scout_web/assets/static/images/errors-img/rinnkeby-block-not-found@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c3cf19b3278fbd9cc5ed4d112d6de6c7b040909d GIT binary patch literal 23316 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