diff --git a/.github/workflows/ci-default.yml b/.github/workflows/ci-default.yml index 00deed3ce..13f7f907f 100644 --- a/.github/workflows/ci-default.yml +++ b/.github/workflows/ci-default.yml @@ -120,7 +120,7 @@ jobs: # Temporarily include this separately. Eventually this should all be part of lint - name: Lint SQL run: | - poetry run sqlfluff lint --dialect bigquery ./dbt/models + poetry run sqlfluff lint --dialect bigquery ./warehouse/dbt/models if: always() - name: Test diff --git a/apps/docs/.eslintrc.json b/apps/docs/.eslintrc.json new file mode 100644 index 000000000..f38d372a6 --- /dev/null +++ b/apps/docs/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "root": false, + "extends": ["../../.eslintrc.js"], + "parserOptions": { + "project": ["./apps/docs/tsconfig.json"] + } +} diff --git a/docs/.gitignore b/apps/docs/.gitignore similarity index 100% rename from docs/.gitignore rename to apps/docs/.gitignore diff --git a/docs/README.md b/apps/docs/README.md similarity index 100% rename from docs/README.md rename to apps/docs/README.md diff --git a/docs/babel.config.js b/apps/docs/babel.config.js similarity index 100% rename from docs/babel.config.js rename to apps/docs/babel.config.js diff --git a/docs/blog/2023-11-07-open-source-open-data-open-infra.mdx b/apps/docs/blog/2023-11-07-open-source-open-data-open-infra.mdx similarity index 100% rename from docs/blog/2023-11-07-open-source-open-data-open-infra.mdx rename to apps/docs/blog/2023-11-07-open-source-open-data-open-infra.mdx diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/before-after.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/before-after.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/before-after.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/before-after.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/devs-heatmap.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/devs-heatmap.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/devs-heatmap.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/devs-heatmap.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-all.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-all.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-all.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-all.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-rpgf3.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-rpgf3.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-rpgf3.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-rpgf3.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-zoom.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-zoom.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-zoom.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/fees-zoom.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/hvaus.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/hvaus.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/hvaus.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/hvaus.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/index.md b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/index.md similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/index.md rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/index.md diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-heatmap.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-heatmap.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-heatmap.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-heatmap.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf1-3.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf1-3.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf1-3.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf1-3.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf2-3.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf2-3.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf2-3.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf2-3.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf3.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf3.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf3.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/mads-rpgf3.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/network-graph-big.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/network-graph-big.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/network-graph-big.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/network-graph-big.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/network-graph.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/network-graph.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/network-graph.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/network-graph.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/users-heatmap.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/users-heatmap.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/users-heatmap.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/users-heatmap.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/users-scatter.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/users-scatter.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/users-scatter.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/users-scatter.png diff --git a/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/venn-diagram.png b/apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/venn-diagram.png similarity index 100% rename from docs/blog/2023-11-20-retropgf3-ecosystem-analysis/venn-diagram.png rename to apps/docs/blog/2023-11-20-retropgf3-ecosystem-analysis/venn-diagram.png diff --git a/docs/blog/2023-12-14-levels-of-the-game.mdx b/apps/docs/blog/2023-12-14-levels-of-the-game.mdx similarity index 100% rename from docs/blog/2023-12-14-levels-of-the-game.mdx rename to apps/docs/blog/2023-12-14-levels-of-the-game.mdx diff --git a/docs/blog/2023-12-15-pln-ecosystem-analysis/datashader.png b/apps/docs/blog/2023-12-15-pln-ecosystem-analysis/datashader.png similarity index 100% rename from docs/blog/2023-12-15-pln-ecosystem-analysis/datashader.png rename to apps/docs/blog/2023-12-15-pln-ecosystem-analysis/datashader.png diff --git a/docs/blog/2023-12-15-pln-ecosystem-analysis/heatmap-pln-ff.png b/apps/docs/blog/2023-12-15-pln-ecosystem-analysis/heatmap-pln-ff.png similarity index 100% rename from docs/blog/2023-12-15-pln-ecosystem-analysis/heatmap-pln-ff.png rename to apps/docs/blog/2023-12-15-pln-ecosystem-analysis/heatmap-pln-ff.png diff --git a/docs/blog/2023-12-15-pln-ecosystem-analysis/heatmap-pln.png b/apps/docs/blog/2023-12-15-pln-ecosystem-analysis/heatmap-pln.png similarity index 100% rename from docs/blog/2023-12-15-pln-ecosystem-analysis/heatmap-pln.png rename to apps/docs/blog/2023-12-15-pln-ecosystem-analysis/heatmap-pln.png diff --git a/docs/blog/2023-12-15-pln-ecosystem-analysis/index.mdx b/apps/docs/blog/2023-12-15-pln-ecosystem-analysis/index.mdx similarity index 100% rename from docs/blog/2023-12-15-pln-ecosystem-analysis/index.mdx rename to apps/docs/blog/2023-12-15-pln-ecosystem-analysis/index.mdx diff --git a/docs/blog/2023-12-15-pln-ecosystem-analysis/leaderboard.png b/apps/docs/blog/2023-12-15-pln-ecosystem-analysis/leaderboard.png similarity index 100% rename from docs/blog/2023-12-15-pln-ecosystem-analysis/leaderboard.png rename to apps/docs/blog/2023-12-15-pln-ecosystem-analysis/leaderboard.png diff --git a/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-ff.png b/apps/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-ff.png similarity index 100% rename from docs/blog/2023-12-15-pln-ecosystem-analysis/mads-ff.png rename to apps/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-ff.png diff --git a/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln-ff.png b/apps/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln-ff.png similarity index 100% rename from docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln-ff.png rename to apps/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln-ff.png diff --git a/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln.png b/apps/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln.png similarity index 100% rename from docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln.png rename to apps/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln.png diff --git a/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln2.png b/apps/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln2.png similarity index 100% rename from docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln2.png rename to apps/docs/blog/2023-12-15-pln-ecosystem-analysis/mads-pln2.png diff --git a/docs/blog/2023-12-15-pln-ecosystem-analysis/network-graph.png b/apps/docs/blog/2023-12-15-pln-ecosystem-analysis/network-graph.png similarity index 100% rename from docs/blog/2023-12-15-pln-ecosystem-analysis/network-graph.png rename to apps/docs/blog/2023-12-15-pln-ecosystem-analysis/network-graph.png diff --git a/docs/blog/2023-12-15-pln-ecosystem-analysis/venn.png b/apps/docs/blog/2023-12-15-pln-ecosystem-analysis/venn.png similarity index 100% rename from docs/blog/2023-12-15-pln-ecosystem-analysis/venn.png rename to apps/docs/blog/2023-12-15-pln-ecosystem-analysis/venn.png diff --git a/docs/blog/2024-01-03-fund-your-dependencies/a16z.png b/apps/docs/blog/2024-01-03-fund-your-dependencies/a16z.png similarity index 100% rename from docs/blog/2024-01-03-fund-your-dependencies/a16z.png rename to apps/docs/blog/2024-01-03-fund-your-dependencies/a16z.png diff --git a/docs/blog/2024-01-03-fund-your-dependencies/index.mdx b/apps/docs/blog/2024-01-03-fund-your-dependencies/index.mdx similarity index 100% rename from docs/blog/2024-01-03-fund-your-dependencies/index.mdx rename to apps/docs/blog/2024-01-03-fund-your-dependencies/index.mdx diff --git a/docs/blog/2024-01-03-fund-your-dependencies/mads.png b/apps/docs/blog/2024-01-03-fund-your-dependencies/mads.png similarity index 100% rename from docs/blog/2024-01-03-fund-your-dependencies/mads.png rename to apps/docs/blog/2024-01-03-fund-your-dependencies/mads.png diff --git a/docs/blog/2024-01-03-fund-your-dependencies/maus.png b/apps/docs/blog/2024-01-03-fund-your-dependencies/maus.png similarity index 100% rename from docs/blog/2024-01-03-fund-your-dependencies/maus.png rename to apps/docs/blog/2024-01-03-fund-your-dependencies/maus.png diff --git a/docs/blog/2024-01-03-fund-your-dependencies/sankey.png b/apps/docs/blog/2024-01-03-fund-your-dependencies/sankey.png similarity index 100% rename from docs/blog/2024-01-03-fund-your-dependencies/sankey.png rename to apps/docs/blog/2024-01-03-fund-your-dependencies/sankey.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/01 - coinmarketcap.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/01 - coinmarketcap.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/01 - coinmarketcap.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/01 - coinmarketcap.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/02 - rpgf3_allocation.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/02 - rpgf3_allocation.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/02 - rpgf3_allocation.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/02 - rpgf3_allocation.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/03 - rpgf3_allocation_bottom.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/03 - rpgf3_allocation_bottom.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/03 - rpgf3_allocation_bottom.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/03 - rpgf3_allocation_bottom.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/04 - rpgf2_allocation.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/04 - rpgf2_allocation.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/04 - rpgf2_allocation.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/04 - rpgf2_allocation.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/05 - oso_allocation.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/05 - oso_allocation.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/05 - oso_allocation.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/05 - oso_allocation.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/06a - github_allocation.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/06a - github_allocation.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/06a - github_allocation.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/06a - github_allocation.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/06b - github_tree.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/06b - github_tree.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/06b - github_tree.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/06b - github_tree.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/07 - npm_allocation.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/07 - npm_allocation.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/07 - npm_allocation.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/07 - npm_allocation.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/08a - onchain_allocation.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/08a - onchain_allocation.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/08a - onchain_allocation.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/08a - onchain_allocation.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/08b - onochain_tree.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/08b - onochain_tree.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/08b - onochain_tree.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/08b - onochain_tree.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/09 - media_allocation.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/09 - media_allocation.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/09 - media_allocation.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/09 - media_allocation.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/10 - dune_allocation.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/10 - dune_allocation.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/10 - dune_allocation.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/10 - dune_allocation.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/11 - individuals_allocation.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/11 - individuals_allocation.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/11 - individuals_allocation.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/11 - individuals_allocation.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/12a - other_projects_tree.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/12a - other_projects_tree.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/12a - other_projects_tree.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/12a - other_projects_tree.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/12b - other_projects_allocation.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/12b - other_projects_allocation.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/12b - other_projects_allocation.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/12b - other_projects_allocation.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/13 - teamsize_dist.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/13 - teamsize_dist.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/13 - teamsize_dist.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/13 - teamsize_dist.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/14 - impact_vectors.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/14 - impact_vectors.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/14 - impact_vectors.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/14 - impact_vectors.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/15 - vector_ranks.png b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/15 - vector_ranks.png similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/15 - vector_ranks.png rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/15 - vector_ranks.png diff --git a/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/index.mdx b/apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/index.mdx similarity index 100% rename from docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/index.mdx rename to apps/docs/blog/2024-01-19-what-builders-can-learn-from-retropgf3/index.mdx diff --git a/docs/blog/2024-01-26-octant-epoch-02-ecosystem-analysis.mdx b/apps/docs/blog/2024-01-26-octant-epoch-02-ecosystem-analysis.mdx similarity index 100% rename from docs/blog/2024-01-26-octant-epoch-02-ecosystem-analysis.mdx rename to apps/docs/blog/2024-01-26-octant-epoch-02-ecosystem-analysis.mdx diff --git a/docs/blog/2024-01-31-arb-ecosystem-analysis/area-arbitrum-all.png b/apps/docs/blog/2024-01-31-arb-ecosystem-analysis/area-arbitrum-all.png similarity index 100% rename from docs/blog/2024-01-31-arb-ecosystem-analysis/area-arbitrum-all.png rename to apps/docs/blog/2024-01-31-arb-ecosystem-analysis/area-arbitrum-all.png diff --git a/docs/blog/2024-01-31-arb-ecosystem-analysis/area-arbitrum-onchain.png b/apps/docs/blog/2024-01-31-arb-ecosystem-analysis/area-arbitrum-onchain.png similarity index 100% rename from docs/blog/2024-01-31-arb-ecosystem-analysis/area-arbitrum-onchain.png rename to apps/docs/blog/2024-01-31-arb-ecosystem-analysis/area-arbitrum-onchain.png diff --git a/docs/blog/2024-01-31-arb-ecosystem-analysis/coverage-sankey.png b/apps/docs/blog/2024-01-31-arb-ecosystem-analysis/coverage-sankey.png similarity index 100% rename from docs/blog/2024-01-31-arb-ecosystem-analysis/coverage-sankey.png rename to apps/docs/blog/2024-01-31-arb-ecosystem-analysis/coverage-sankey.png diff --git a/docs/blog/2024-01-31-arb-ecosystem-analysis/developer-delta-distribution.png b/apps/docs/blog/2024-01-31-arb-ecosystem-analysis/developer-delta-distribution.png similarity index 100% rename from docs/blog/2024-01-31-arb-ecosystem-analysis/developer-delta-distribution.png rename to apps/docs/blog/2024-01-31-arb-ecosystem-analysis/developer-delta-distribution.png diff --git a/docs/blog/2024-01-31-arb-ecosystem-analysis/heatmap-arb-stip.png b/apps/docs/blog/2024-01-31-arb-ecosystem-analysis/heatmap-arb-stip.png similarity index 100% rename from docs/blog/2024-01-31-arb-ecosystem-analysis/heatmap-arb-stip.png rename to apps/docs/blog/2024-01-31-arb-ecosystem-analysis/heatmap-arb-stip.png diff --git a/docs/blog/2024-01-31-arb-ecosystem-analysis/heatmap-arb.png b/apps/docs/blog/2024-01-31-arb-ecosystem-analysis/heatmap-arb.png similarity index 100% rename from docs/blog/2024-01-31-arb-ecosystem-analysis/heatmap-arb.png rename to apps/docs/blog/2024-01-31-arb-ecosystem-analysis/heatmap-arb.png diff --git a/docs/blog/2024-01-31-arb-ecosystem-analysis/index.mdx b/apps/docs/blog/2024-01-31-arb-ecosystem-analysis/index.mdx similarity index 100% rename from docs/blog/2024-01-31-arb-ecosystem-analysis/index.mdx rename to apps/docs/blog/2024-01-31-arb-ecosystem-analysis/index.mdx diff --git a/docs/blog/2024-01-31-arb-ecosystem-analysis/venn.png b/apps/docs/blog/2024-01-31-arb-ecosystem-analysis/venn.png similarity index 100% rename from docs/blog/2024-01-31-arb-ecosystem-analysis/venn.png rename to apps/docs/blog/2024-01-31-arb-ecosystem-analysis/venn.png diff --git a/docs/blog/2024-02-15-impact-data-scientists.mdx b/apps/docs/blog/2024-02-15-impact-data-scientists.mdx similarity index 100% rename from docs/blog/2024-02-15-impact-data-scientists.mdx rename to apps/docs/blog/2024-02-15-impact-data-scientists.mdx diff --git a/docs/blog/2024-02-21-gitcoin-grants-impact/01-rounds.png b/apps/docs/blog/2024-02-21-gitcoin-grants-impact/01-rounds.png similarity index 100% rename from docs/blog/2024-02-21-gitcoin-grants-impact/01-rounds.png rename to apps/docs/blog/2024-02-21-gitcoin-grants-impact/01-rounds.png diff --git a/docs/blog/2024-02-21-gitcoin-grants-impact/02-gr1-4.png b/apps/docs/blog/2024-02-21-gitcoin-grants-impact/02-gr1-4.png similarity index 100% rename from docs/blog/2024-02-21-gitcoin-grants-impact/02-gr1-4.png rename to apps/docs/blog/2024-02-21-gitcoin-grants-impact/02-gr1-4.png diff --git a/docs/blog/2024-02-21-gitcoin-grants-impact/03-gr5-9.png b/apps/docs/blog/2024-02-21-gitcoin-grants-impact/03-gr5-9.png similarity index 100% rename from docs/blog/2024-02-21-gitcoin-grants-impact/03-gr5-9.png rename to apps/docs/blog/2024-02-21-gitcoin-grants-impact/03-gr5-9.png diff --git a/docs/blog/2024-02-21-gitcoin-grants-impact/04-gr10-15.png b/apps/docs/blog/2024-02-21-gitcoin-grants-impact/04-gr10-15.png similarity index 100% rename from docs/blog/2024-02-21-gitcoin-grants-impact/04-gr10-15.png rename to apps/docs/blog/2024-02-21-gitcoin-grants-impact/04-gr10-15.png diff --git a/docs/blog/2024-02-21-gitcoin-grants-impact/05-alpha-gr19.png b/apps/docs/blog/2024-02-21-gitcoin-grants-impact/05-alpha-gr19.png similarity index 100% rename from docs/blog/2024-02-21-gitcoin-grants-impact/05-alpha-gr19.png rename to apps/docs/blog/2024-02-21-gitcoin-grants-impact/05-alpha-gr19.png diff --git a/docs/blog/2024-02-21-gitcoin-grants-impact/06-contribs.png b/apps/docs/blog/2024-02-21-gitcoin-grants-impact/06-contribs.png similarity index 100% rename from docs/blog/2024-02-21-gitcoin-grants-impact/06-contribs.png rename to apps/docs/blog/2024-02-21-gitcoin-grants-impact/06-contribs.png diff --git a/docs/blog/2024-02-21-gitcoin-grants-impact/07-developer-growth.png b/apps/docs/blog/2024-02-21-gitcoin-grants-impact/07-developer-growth.png similarity index 100% rename from docs/blog/2024-02-21-gitcoin-grants-impact/07-developer-growth.png rename to apps/docs/blog/2024-02-21-gitcoin-grants-impact/07-developer-growth.png diff --git a/docs/blog/2024-02-21-gitcoin-grants-impact/08-main-exhibit.png b/apps/docs/blog/2024-02-21-gitcoin-grants-impact/08-main-exhibit.png similarity index 100% rename from docs/blog/2024-02-21-gitcoin-grants-impact/08-main-exhibit.png rename to apps/docs/blog/2024-02-21-gitcoin-grants-impact/08-main-exhibit.png diff --git a/docs/blog/2024-02-21-gitcoin-grants-impact/index.mdx b/apps/docs/blog/2024-02-21-gitcoin-grants-impact/index.mdx similarity index 100% rename from docs/blog/2024-02-21-gitcoin-grants-impact/index.mdx rename to apps/docs/blog/2024-02-21-gitcoin-grants-impact/index.mdx diff --git a/docs/blog/2024-02-28-arbitrum-impact-pools/blockspace_pool.png b/apps/docs/blog/2024-02-28-arbitrum-impact-pools/blockspace_pool.png similarity index 100% rename from docs/blog/2024-02-28-arbitrum-impact-pools/blockspace_pool.png rename to apps/docs/blog/2024-02-28-arbitrum-impact-pools/blockspace_pool.png diff --git a/docs/blog/2024-02-28-arbitrum-impact-pools/blockspace_scatter.png b/apps/docs/blog/2024-02-28-arbitrum-impact-pools/blockspace_scatter.png similarity index 100% rename from docs/blog/2024-02-28-arbitrum-impact-pools/blockspace_scatter.png rename to apps/docs/blog/2024-02-28-arbitrum-impact-pools/blockspace_scatter.png diff --git a/docs/blog/2024-02-28-arbitrum-impact-pools/dev_growth_pool.png b/apps/docs/blog/2024-02-28-arbitrum-impact-pools/dev_growth_pool.png similarity index 100% rename from docs/blog/2024-02-28-arbitrum-impact-pools/dev_growth_pool.png rename to apps/docs/blog/2024-02-28-arbitrum-impact-pools/dev_growth_pool.png diff --git a/docs/blog/2024-02-28-arbitrum-impact-pools/dev_growth_scatter.png b/apps/docs/blog/2024-02-28-arbitrum-impact-pools/dev_growth_scatter.png similarity index 100% rename from docs/blog/2024-02-28-arbitrum-impact-pools/dev_growth_scatter.png rename to apps/docs/blog/2024-02-28-arbitrum-impact-pools/dev_growth_scatter.png diff --git a/docs/blog/2024-02-28-arbitrum-impact-pools/index.mdx b/apps/docs/blog/2024-02-28-arbitrum-impact-pools/index.mdx similarity index 100% rename from docs/blog/2024-02-28-arbitrum-impact-pools/index.mdx rename to apps/docs/blog/2024-02-28-arbitrum-impact-pools/index.mdx diff --git a/docs/blog/2024-02-28-arbitrum-impact-pools/momentum_pool.png b/apps/docs/blog/2024-02-28-arbitrum-impact-pools/momentum_pool.png similarity index 100% rename from docs/blog/2024-02-28-arbitrum-impact-pools/momentum_pool.png rename to apps/docs/blog/2024-02-28-arbitrum-impact-pools/momentum_pool.png diff --git a/docs/blog/2024-02-28-arbitrum-impact-pools/momentum_scatter.png b/apps/docs/blog/2024-02-28-arbitrum-impact-pools/momentum_scatter.png similarity index 100% rename from docs/blog/2024-02-28-arbitrum-impact-pools/momentum_scatter.png rename to apps/docs/blog/2024-02-28-arbitrum-impact-pools/momentum_scatter.png diff --git a/docs/blog/2024-02-28-arbitrum-impact-pools/user_growth_pool.png b/apps/docs/blog/2024-02-28-arbitrum-impact-pools/user_growth_pool.png similarity index 100% rename from docs/blog/2024-02-28-arbitrum-impact-pools/user_growth_pool.png rename to apps/docs/blog/2024-02-28-arbitrum-impact-pools/user_growth_pool.png diff --git a/docs/blog/2024-02-28-arbitrum-impact-pools/user_growth_scatter.png b/apps/docs/blog/2024-02-28-arbitrum-impact-pools/user_growth_scatter.png similarity index 100% rename from docs/blog/2024-02-28-arbitrum-impact-pools/user_growth_scatter.png rename to apps/docs/blog/2024-02-28-arbitrum-impact-pools/user_growth_scatter.png diff --git a/docs/blog/authors.yml b/apps/docs/blog/authors.yml similarity index 100% rename from docs/blog/authors.yml rename to apps/docs/blog/authors.yml diff --git a/docs/docs/about.md b/apps/docs/docs/about.md similarity index 100% rename from docs/docs/about.md rename to apps/docs/docs/about.md diff --git a/docs/docs/contribute/_category_.json b/apps/docs/docs/contribute/_category_.json similarity index 100% rename from docs/docs/contribute/_category_.json rename to apps/docs/docs/contribute/_category_.json diff --git a/docs/docs/contribute/connect-data.md b/apps/docs/docs/contribute/connect-data.md similarity index 100% rename from docs/docs/contribute/connect-data.md rename to apps/docs/docs/contribute/connect-data.md diff --git a/docs/docs/contribute/data-scientist-meme.png b/apps/docs/docs/contribute/data-scientist-meme.png similarity index 100% rename from docs/docs/contribute/data-scientist-meme.png rename to apps/docs/docs/contribute/data-scientist-meme.png diff --git a/docs/docs/contribute/funding-data.md b/apps/docs/docs/contribute/funding-data.md similarity index 100% rename from docs/docs/contribute/funding-data.md rename to apps/docs/docs/contribute/funding-data.md diff --git a/docs/docs/contribute/impact-models.md b/apps/docs/docs/contribute/impact-models.md similarity index 100% rename from docs/docs/contribute/impact-models.md rename to apps/docs/docs/contribute/impact-models.md diff --git a/docs/docs/contribute/index.mdx b/apps/docs/docs/contribute/index.mdx similarity index 100% rename from docs/docs/contribute/index.mdx rename to apps/docs/docs/contribute/index.mdx diff --git a/docs/docs/contribute/project-data.md b/apps/docs/docs/contribute/project-data.md similarity index 100% rename from docs/docs/contribute/project-data.md rename to apps/docs/docs/contribute/project-data.md diff --git a/docs/docs/contribute/share-insights.md b/apps/docs/docs/contribute/share-insights.md similarity index 100% rename from docs/docs/contribute/share-insights.md rename to apps/docs/docs/contribute/share-insights.md diff --git a/docs/docs/getting-started/_category_.json b/apps/docs/docs/getting-started/_category_.json similarity index 100% rename from docs/docs/getting-started/_category_.json rename to apps/docs/docs/getting-started/_category_.json diff --git a/docs/docs/getting-started/api-access.md b/apps/docs/docs/getting-started/api-access.md similarity index 100% rename from docs/docs/getting-started/api-access.md rename to apps/docs/docs/getting-started/api-access.md diff --git a/docs/docs/getting-started/first-queries.md b/apps/docs/docs/getting-started/first-queries.md similarity index 100% rename from docs/docs/getting-started/first-queries.md rename to apps/docs/docs/getting-started/first-queries.md diff --git a/docs/docs/getting-started/gcp_admin.png b/apps/docs/docs/getting-started/gcp_admin.png similarity index 100% rename from docs/docs/getting-started/gcp_admin.png rename to apps/docs/docs/getting-started/gcp_admin.png diff --git a/docs/docs/getting-started/gcp_billing.png b/apps/docs/docs/getting-started/gcp_billing.png similarity index 100% rename from docs/docs/getting-started/gcp_billing.png rename to apps/docs/docs/getting-started/gcp_billing.png diff --git a/docs/docs/getting-started/gcp_console.png b/apps/docs/docs/getting-started/gcp_console.png similarity index 100% rename from docs/docs/getting-started/gcp_console.png rename to apps/docs/docs/getting-started/gcp_console.png diff --git a/docs/docs/getting-started/gcp_query.png b/apps/docs/docs/getting-started/gcp_query.png similarity index 100% rename from docs/docs/getting-started/gcp_query.png rename to apps/docs/docs/getting-started/gcp_query.png diff --git a/docs/docs/getting-started/gcp_signup.png b/apps/docs/docs/getting-started/gcp_signup.png similarity index 100% rename from docs/docs/getting-started/gcp_signup.png rename to apps/docs/docs/getting-started/gcp_signup.png diff --git a/docs/docs/getting-started/gcp_welcome.png b/apps/docs/docs/getting-started/gcp_welcome.png similarity index 100% rename from docs/docs/getting-started/gcp_welcome.png rename to apps/docs/docs/getting-started/gcp_welcome.png diff --git a/docs/docs/getting-started/index.mdx b/apps/docs/docs/getting-started/index.mdx similarity index 100% rename from docs/docs/getting-started/index.mdx rename to apps/docs/docs/getting-started/index.mdx diff --git a/docs/docs/how-oso-works/_category_.json b/apps/docs/docs/how-oso-works/_category_.json similarity index 100% rename from docs/docs/how-oso-works/_category_.json rename to apps/docs/docs/how-oso-works/_category_.json diff --git a/docs/docs/how-oso-works/architecture.md b/apps/docs/docs/how-oso-works/architecture.md similarity index 100% rename from docs/docs/how-oso-works/architecture.md rename to apps/docs/docs/how-oso-works/architecture.md diff --git a/docs/docs/how-oso-works/dashboards.md b/apps/docs/docs/how-oso-works/dashboards.md similarity index 100% rename from docs/docs/how-oso-works/dashboards.md rename to apps/docs/docs/how-oso-works/dashboards.md diff --git a/docs/docs/how-oso-works/index.md b/apps/docs/docs/how-oso-works/index.md similarity index 100% rename from docs/docs/how-oso-works/index.md rename to apps/docs/docs/how-oso-works/index.md diff --git a/docs/docs/how-oso-works/ops/_category_.json b/apps/docs/docs/how-oso-works/ops/_category_.json similarity index 100% rename from docs/docs/how-oso-works/ops/_category_.json rename to apps/docs/docs/how-oso-works/ops/_category_.json diff --git a/docs/docs/how-oso-works/ops/gcloud.md b/apps/docs/docs/how-oso-works/ops/gcloud.md similarity index 100% rename from docs/docs/how-oso-works/ops/gcloud.md rename to apps/docs/docs/how-oso-works/ops/gcloud.md diff --git a/docs/docs/how-oso-works/ops/hasura.md b/apps/docs/docs/how-oso-works/ops/hasura.md similarity index 100% rename from docs/docs/how-oso-works/ops/hasura.md rename to apps/docs/docs/how-oso-works/ops/hasura.md diff --git a/docs/docs/how-oso-works/ops/overview.md b/apps/docs/docs/how-oso-works/ops/overview.md similarity index 100% rename from docs/docs/how-oso-works/ops/overview.md rename to apps/docs/docs/how-oso-works/ops/overview.md diff --git a/docs/docs/how-oso-works/ops/supabase.md b/apps/docs/docs/how-oso-works/ops/supabase.md similarity index 100% rename from docs/docs/how-oso-works/ops/supabase.md rename to apps/docs/docs/how-oso-works/ops/supabase.md diff --git a/docs/docs/how-oso-works/resources/_category_.json b/apps/docs/docs/how-oso-works/resources/_category_.json similarity index 100% rename from docs/docs/how-oso-works/resources/_category_.json rename to apps/docs/docs/how-oso-works/resources/_category_.json diff --git a/docs/docs/how-oso-works/resources/bounties.md b/apps/docs/docs/how-oso-works/resources/bounties.md similarity index 100% rename from docs/docs/how-oso-works/resources/bounties.md rename to apps/docs/docs/how-oso-works/resources/bounties.md diff --git a/docs/docs/how-oso-works/resources/ecosystems/_category_.json b/apps/docs/docs/how-oso-works/resources/ecosystems/_category_.json similarity index 100% rename from docs/docs/how-oso-works/resources/ecosystems/_category_.json rename to apps/docs/docs/how-oso-works/resources/ecosystems/_category_.json diff --git a/docs/docs/how-oso-works/resources/ecosystems/arbitrum.md b/apps/docs/docs/how-oso-works/resources/ecosystems/arbitrum.md similarity index 100% rename from docs/docs/how-oso-works/resources/ecosystems/arbitrum.md rename to apps/docs/docs/how-oso-works/resources/ecosystems/arbitrum.md diff --git a/docs/docs/how-oso-works/resources/ecosystems/optimism.md b/apps/docs/docs/how-oso-works/resources/ecosystems/optimism.md similarity index 100% rename from docs/docs/how-oso-works/resources/ecosystems/optimism.md rename to apps/docs/docs/how-oso-works/resources/ecosystems/optimism.md diff --git a/docs/docs/how-oso-works/resources/ecosystems/pln.md b/apps/docs/docs/how-oso-works/resources/ecosystems/pln.md similarity index 100% rename from docs/docs/how-oso-works/resources/ecosystems/pln.md rename to apps/docs/docs/how-oso-works/resources/ecosystems/pln.md diff --git a/docs/docs/how-oso-works/resources/impact-vector-spec.md b/apps/docs/docs/how-oso-works/resources/impact-vector-spec.md similarity index 100% rename from docs/docs/how-oso-works/resources/impact-vector-spec.md rename to apps/docs/docs/how-oso-works/resources/impact-vector-spec.md diff --git a/docs/docs/how-oso-works/resources/metrics/_category_.json b/apps/docs/docs/how-oso-works/resources/metrics/_category_.json similarity index 100% rename from docs/docs/how-oso-works/resources/metrics/_category_.json rename to apps/docs/docs/how-oso-works/resources/metrics/_category_.json diff --git a/docs/docs/how-oso-works/resources/metrics/dependencies.md b/apps/docs/docs/how-oso-works/resources/metrics/dependencies.md similarity index 100% rename from docs/docs/how-oso-works/resources/metrics/dependencies.md rename to apps/docs/docs/how-oso-works/resources/metrics/dependencies.md diff --git a/docs/docs/how-oso-works/resources/metrics/github_activity.md b/apps/docs/docs/how-oso-works/resources/metrics/github_activity.md similarity index 100% rename from docs/docs/how-oso-works/resources/metrics/github_activity.md rename to apps/docs/docs/how-oso-works/resources/metrics/github_activity.md diff --git a/docs/docs/how-oso-works/resources/metrics/github_contributors.md b/apps/docs/docs/how-oso-works/resources/metrics/github_contributors.md similarity index 100% rename from docs/docs/how-oso-works/resources/metrics/github_contributors.md rename to apps/docs/docs/how-oso-works/resources/metrics/github_contributors.md diff --git a/docs/docs/how-oso-works/resources/metrics/index.mdx b/apps/docs/docs/how-oso-works/resources/metrics/index.mdx similarity index 100% rename from docs/docs/how-oso-works/resources/metrics/index.mdx rename to apps/docs/docs/how-oso-works/resources/metrics/index.mdx diff --git a/docs/docs/how-oso-works/resources/metrics/npm_activity.md b/apps/docs/docs/how-oso-works/resources/metrics/npm_activity.md similarity index 100% rename from docs/docs/how-oso-works/resources/metrics/npm_activity.md rename to apps/docs/docs/how-oso-works/resources/metrics/npm_activity.md diff --git a/docs/docs/how-oso-works/resources/metrics/onchain_activity.md b/apps/docs/docs/how-oso-works/resources/metrics/onchain_activity.md similarity index 100% rename from docs/docs/how-oso-works/resources/metrics/onchain_activity.md rename to apps/docs/docs/how-oso-works/resources/metrics/onchain_activity.md diff --git a/docs/docs/how-oso-works/resources/metrics/onchain_users.md b/apps/docs/docs/how-oso-works/resources/metrics/onchain_users.md similarity index 100% rename from docs/docs/how-oso-works/resources/metrics/onchain_users.md rename to apps/docs/docs/how-oso-works/resources/metrics/onchain_users.md diff --git a/docs/docs/how-oso-works/schema/_category_.json b/apps/docs/docs/how-oso-works/schema/_category_.json similarity index 100% rename from docs/docs/how-oso-works/schema/_category_.json rename to apps/docs/docs/how-oso-works/schema/_category_.json diff --git a/docs/docs/how-oso-works/schema/artifact.md b/apps/docs/docs/how-oso-works/schema/artifact.md similarity index 100% rename from docs/docs/how-oso-works/schema/artifact.md rename to apps/docs/docs/how-oso-works/schema/artifact.md diff --git a/docs/docs/how-oso-works/schema/collection.md b/apps/docs/docs/how-oso-works/schema/collection.md similarity index 100% rename from docs/docs/how-oso-works/schema/collection.md rename to apps/docs/docs/how-oso-works/schema/collection.md diff --git a/docs/docs/how-oso-works/schema/event.md b/apps/docs/docs/how-oso-works/schema/event.md similarity index 100% rename from docs/docs/how-oso-works/schema/event.md rename to apps/docs/docs/how-oso-works/schema/event.md diff --git a/docs/docs/how-oso-works/schema/project.md b/apps/docs/docs/how-oso-works/schema/project.md similarity index 100% rename from docs/docs/how-oso-works/schema/project.md rename to apps/docs/docs/how-oso-works/schema/project.md diff --git a/docs/docs/how-oso-works/schema/schema-updates.md b/apps/docs/docs/how-oso-works/schema/schema-updates.md similarity index 100% rename from docs/docs/how-oso-works/schema/schema-updates.md rename to apps/docs/docs/how-oso-works/schema/schema-updates.md diff --git a/docs/docs/integrate/_category_.json b/apps/docs/docs/integrate/_category_.json similarity index 100% rename from docs/docs/integrate/_category_.json rename to apps/docs/docs/integrate/_category_.json diff --git a/docs/docs/integrate/data-science.md b/apps/docs/docs/integrate/data-science.md similarity index 100% rename from docs/docs/integrate/data-science.md rename to apps/docs/docs/integrate/data-science.md diff --git a/docs/docs/integrate/download-data.md b/apps/docs/docs/integrate/download-data.md similarity index 100% rename from docs/docs/integrate/download-data.md rename to apps/docs/docs/integrate/download-data.md diff --git a/docs/docs/integrate/embed.md b/apps/docs/docs/integrate/embed.md similarity index 100% rename from docs/docs/integrate/embed.md rename to apps/docs/docs/integrate/embed.md diff --git a/docs/docs/integrate/gcp_add_key.png b/apps/docs/docs/integrate/gcp_add_key.png similarity index 100% rename from docs/docs/integrate/gcp_add_key.png rename to apps/docs/docs/integrate/gcp_add_key.png diff --git a/docs/docs/integrate/gcp_apis.png b/apps/docs/docs/integrate/gcp_apis.png similarity index 100% rename from docs/docs/integrate/gcp_apis.png rename to apps/docs/docs/integrate/gcp_apis.png diff --git a/docs/docs/integrate/gcp_credentials.png b/apps/docs/docs/integrate/gcp_credentials.png similarity index 100% rename from docs/docs/integrate/gcp_credentials.png rename to apps/docs/docs/integrate/gcp_credentials.png diff --git a/docs/docs/integrate/gcp_credentials_keys.png b/apps/docs/docs/integrate/gcp_credentials_keys.png similarity index 100% rename from docs/docs/integrate/gcp_credentials_keys.png rename to apps/docs/docs/integrate/gcp_credentials_keys.png diff --git a/docs/docs/integrate/gcp_playground.png b/apps/docs/docs/integrate/gcp_playground.png similarity index 100% rename from docs/docs/integrate/gcp_playground.png rename to apps/docs/docs/integrate/gcp_playground.png diff --git a/docs/docs/integrate/gcp_service_account.png b/apps/docs/docs/integrate/gcp_service_account.png similarity index 100% rename from docs/docs/integrate/gcp_service_account.png rename to apps/docs/docs/integrate/gcp_service_account.png diff --git a/docs/docs/integrate/graphql-explorer.png b/apps/docs/docs/integrate/graphql-explorer.png similarity index 100% rename from docs/docs/integrate/graphql-explorer.png rename to apps/docs/docs/integrate/graphql-explorer.png diff --git a/docs/docs/integrate/histogram.png b/apps/docs/docs/integrate/histogram.png similarity index 100% rename from docs/docs/integrate/histogram.png rename to apps/docs/docs/integrate/histogram.png diff --git a/docs/docs/integrate/index.md b/apps/docs/docs/integrate/index.md similarity index 100% rename from docs/docs/integrate/index.md rename to apps/docs/docs/integrate/index.md diff --git a/docs/docs/integrate/oss-directory.md b/apps/docs/docs/integrate/oss-directory.md similarity index 100% rename from docs/docs/integrate/oss-directory.md rename to apps/docs/docs/integrate/oss-directory.md diff --git a/docs/docs/integrate/stars_vs_forks.png b/apps/docs/docs/integrate/stars_vs_forks.png similarity index 100% rename from docs/docs/integrate/stars_vs_forks.png rename to apps/docs/docs/integrate/stars_vs_forks.png diff --git a/docs/docusaurus.config.ts b/apps/docs/docusaurus.config.ts similarity index 100% rename from docs/docusaurus.config.ts rename to apps/docs/docusaurus.config.ts diff --git a/docs/package.json b/apps/docs/package.json similarity index 83% rename from docs/package.json rename to apps/docs/package.json index aff2ffbf0..cbe0c2a39 100644 --- a/docs/package.json +++ b/apps/docs/package.json @@ -14,8 +14,8 @@ "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", "lint": "pnpm lint:eslint && pnpm lint:prettier", - "lint:eslint": "eslint --ignore-path ../.gitignore --ignore-path .gitignore --max-warnings 0 .", - "lint:prettier": "prettier --ignore-path ../.gitignore --ignore-path .gitignore --log-level warn --check **/*.{js,jsx,ts,tsx,sol,md,json}" + "lint:eslint": "eslint --ignore-path ../../.gitignore --ignore-path .gitignore --max-warnings 0 .", + "lint:prettier": "prettier --ignore-path ../../.gitignore --ignore-path .gitignore --log-level warn --check **/*.{js,jsx,ts,tsx,sol,md,json}" }, "dependencies": { "@docusaurus/core": "3.1.0", diff --git a/docs/sidebars.ts b/apps/docs/sidebars.ts similarity index 100% rename from docs/sidebars.ts rename to apps/docs/sidebars.ts diff --git a/docs/src/components/HomepageFeatures/index.tsx b/apps/docs/src/components/HomepageFeatures/index.tsx similarity index 100% rename from docs/src/components/HomepageFeatures/index.tsx rename to apps/docs/src/components/HomepageFeatures/index.tsx diff --git a/docs/src/components/HomepageFeatures/styles.module.css b/apps/docs/src/components/HomepageFeatures/styles.module.css similarity index 100% rename from docs/src/components/HomepageFeatures/styles.module.css rename to apps/docs/src/components/HomepageFeatures/styles.module.css diff --git a/docs/src/css/custom.css b/apps/docs/src/css/custom.css similarity index 100% rename from docs/src/css/custom.css rename to apps/docs/src/css/custom.css diff --git a/docs/src/pages/index.mdx b/apps/docs/src/pages/index.mdx similarity index 100% rename from docs/src/pages/index.mdx rename to apps/docs/src/pages/index.mdx diff --git a/docs/src/pages/index.module.css b/apps/docs/src/pages/index.module.css similarity index 100% rename from docs/src/pages/index.module.css rename to apps/docs/src/pages/index.module.css diff --git a/docs/static/.nojekyll b/apps/docs/static/.nojekyll similarity index 100% rename from docs/static/.nojekyll rename to apps/docs/static/.nojekyll diff --git a/docs/static/img/docusaurus-social-card.jpg b/apps/docs/static/img/docusaurus-social-card.jpg similarity index 100% rename from docs/static/img/docusaurus-social-card.jpg rename to apps/docs/static/img/docusaurus-social-card.jpg diff --git a/docs/static/img/docusaurus.png b/apps/docs/static/img/docusaurus.png similarity index 100% rename from docs/static/img/docusaurus.png rename to apps/docs/static/img/docusaurus.png diff --git a/docs/static/img/favicon.ico b/apps/docs/static/img/favicon.ico similarity index 100% rename from docs/static/img/favicon.ico rename to apps/docs/static/img/favicon.ico diff --git a/docs/static/img/logo.svg b/apps/docs/static/img/logo.svg similarity index 100% rename from docs/static/img/logo.svg rename to apps/docs/static/img/logo.svg diff --git a/docs/static/img/oso-emblem-black.svg b/apps/docs/static/img/oso-emblem-black.svg similarity index 100% rename from docs/static/img/oso-emblem-black.svg rename to apps/docs/static/img/oso-emblem-black.svg diff --git a/docs/static/img/oso-primary-black.png b/apps/docs/static/img/oso-primary-black.png similarity index 100% rename from docs/static/img/oso-primary-black.png rename to apps/docs/static/img/oso-primary-black.png diff --git a/docs/static/img/undraw_docusaurus_mountain.svg b/apps/docs/static/img/undraw_docusaurus_mountain.svg similarity index 100% rename from docs/static/img/undraw_docusaurus_mountain.svg rename to apps/docs/static/img/undraw_docusaurus_mountain.svg diff --git a/docs/static/img/undraw_docusaurus_react.svg b/apps/docs/static/img/undraw_docusaurus_react.svg similarity index 100% rename from docs/static/img/undraw_docusaurus_react.svg rename to apps/docs/static/img/undraw_docusaurus_react.svg diff --git a/docs/static/img/undraw_docusaurus_tree.svg b/apps/docs/static/img/undraw_docusaurus_tree.svg similarity index 100% rename from docs/static/img/undraw_docusaurus_tree.svg rename to apps/docs/static/img/undraw_docusaurus_tree.svg diff --git a/docs/tsconfig.json b/apps/docs/tsconfig.json similarity index 100% rename from docs/tsconfig.json rename to apps/docs/tsconfig.json diff --git a/frontend/.env.local.example b/apps/frontend/.env.local.example similarity index 100% rename from frontend/.env.local.example rename to apps/frontend/.env.local.example diff --git a/frontend/.eslintrc.json b/apps/frontend/.eslintrc.json similarity index 76% rename from frontend/.eslintrc.json rename to apps/frontend/.eslintrc.json index e53874297..5ef4a3dce 100644 --- a/frontend/.eslintrc.json +++ b/apps/frontend/.eslintrc.json @@ -1,9 +1,9 @@ { "root": false, - "extends": ["../.eslintrc.js", "next/core-web-vitals"], + "extends": ["../../.eslintrc.js", "next/core-web-vitals"], "parser": "@typescript-eslint/parser", "parserOptions": { - "project": ["./frontend/tsconfig.json"] + "project": ["./apps/frontend/tsconfig.json"] }, "rules": { "react-hooks/exhaustive-deps": "off", diff --git a/frontend/.gitignore b/apps/frontend/.gitignore similarity index 100% rename from frontend/.gitignore rename to apps/frontend/.gitignore diff --git a/frontend/README.md b/apps/frontend/README.md similarity index 100% rename from frontend/README.md rename to apps/frontend/README.md diff --git a/frontend/app/[...catchall]/page.tsx b/apps/frontend/app/[...catchall]/page.tsx similarity index 100% rename from frontend/app/[...catchall]/page.tsx rename to apps/frontend/app/[...catchall]/page.tsx diff --git a/frontend/app/api/artifacts/route.ts b/apps/frontend/app/api/artifacts/route.ts similarity index 100% rename from frontend/app/api/artifacts/route.ts rename to apps/frontend/app/api/artifacts/route.ts diff --git a/frontend/app/api/auth/route.ts b/apps/frontend/app/api/auth/route.ts similarity index 100% rename from frontend/app/api/auth/route.ts rename to apps/frontend/app/api/auth/route.ts diff --git a/frontend/app/api/projects/route.ts b/apps/frontend/app/api/projects/route.ts similarity index 100% rename from frontend/app/api/projects/route.ts rename to apps/frontend/app/api/projects/route.ts diff --git a/frontend/app/artifact/[namespace]/[type]/[...name]/page.tsx b/apps/frontend/app/artifact/[namespace]/[type]/[...name]/page.tsx similarity index 100% rename from frontend/app/artifact/[namespace]/[type]/[...name]/page.tsx rename to apps/frontend/app/artifact/[namespace]/[type]/[...name]/page.tsx diff --git a/frontend/app/globals.css b/apps/frontend/app/globals.css similarity index 100% rename from frontend/app/globals.css rename to apps/frontend/app/globals.css diff --git a/frontend/app/layout.tsx b/apps/frontend/app/layout.tsx similarity index 100% rename from frontend/app/layout.tsx rename to apps/frontend/app/layout.tsx diff --git a/frontend/app/not-found.tsx b/apps/frontend/app/not-found.tsx similarity index 100% rename from frontend/app/not-found.tsx rename to apps/frontend/app/not-found.tsx diff --git a/frontend/app/page.tsx b/apps/frontend/app/page.tsx similarity index 100% rename from frontend/app/page.tsx rename to apps/frontend/app/page.tsx diff --git a/frontend/app/plasmic-host/page.tsx b/apps/frontend/app/plasmic-host/page.tsx similarity index 100% rename from frontend/app/plasmic-host/page.tsx rename to apps/frontend/app/plasmic-host/page.tsx diff --git a/frontend/app/project/[...slug]/page.tsx b/apps/frontend/app/project/[...slug]/page.tsx similarity index 100% rename from frontend/app/project/[...slug]/page.tsx rename to apps/frontend/app/project/[...slug]/page.tsx diff --git a/frontend/codegen.ts b/apps/frontend/codegen.ts similarity index 100% rename from frontend/codegen.ts rename to apps/frontend/codegen.ts diff --git a/frontend/components/dataprovider/apollo-wrapper.tsx b/apps/frontend/components/dataprovider/apollo-wrapper.tsx similarity index 100% rename from frontend/components/dataprovider/apollo-wrapper.tsx rename to apps/frontend/components/dataprovider/apollo-wrapper.tsx diff --git a/frontend/components/dataprovider/auth-router.tsx b/apps/frontend/components/dataprovider/auth-router.tsx similarity index 100% rename from frontend/components/dataprovider/auth-router.tsx rename to apps/frontend/components/dataprovider/auth-router.tsx diff --git a/frontend/components/dataprovider/event-data-provider.tsx b/apps/frontend/components/dataprovider/event-data-provider.tsx similarity index 100% rename from frontend/components/dataprovider/event-data-provider.tsx rename to apps/frontend/components/dataprovider/event-data-provider.tsx diff --git a/frontend/components/dataprovider/provider-view.tsx b/apps/frontend/components/dataprovider/provider-view.tsx similarity index 100% rename from frontend/components/dataprovider/provider-view.tsx rename to apps/frontend/components/dataprovider/provider-view.tsx diff --git a/frontend/components/dataprovider/supabase-query.tsx b/apps/frontend/components/dataprovider/supabase-query.tsx similarity index 100% rename from frontend/components/dataprovider/supabase-query.tsx rename to apps/frontend/components/dataprovider/supabase-query.tsx diff --git a/frontend/components/dataprovider/table-data-provider.tsx b/apps/frontend/components/dataprovider/table-data-provider.tsx similarity index 100% rename from frontend/components/dataprovider/table-data-provider.tsx rename to apps/frontend/components/dataprovider/table-data-provider.tsx diff --git a/frontend/components/forms/form-elements.tsx b/apps/frontend/components/forms/form-elements.tsx similarity index 100% rename from frontend/components/forms/form-elements.tsx rename to apps/frontend/components/forms/form-elements.tsx diff --git a/frontend/components/forms/visualization-context.tsx b/apps/frontend/components/forms/visualization-context.tsx similarity index 100% rename from frontend/components/forms/visualization-context.tsx rename to apps/frontend/components/forms/visualization-context.tsx diff --git a/frontend/components/project-browser/css/browser.module.css b/apps/frontend/components/project-browser/css/browser.module.css similarity index 100% rename from frontend/components/project-browser/css/browser.module.css rename to apps/frontend/components/project-browser/css/browser.module.css diff --git a/frontend/components/project-browser/css/data-table.module.css b/apps/frontend/components/project-browser/css/data-table.module.css similarity index 100% rename from frontend/components/project-browser/css/data-table.module.css rename to apps/frontend/components/project-browser/css/data-table.module.css diff --git a/frontend/components/project-browser/data-table/expandable.tsx b/apps/frontend/components/project-browser/data-table/expandable.tsx similarity index 100% rename from frontend/components/project-browser/data-table/expandable.tsx rename to apps/frontend/components/project-browser/data-table/expandable.tsx diff --git a/frontend/components/project-browser/data-table/field-default.tsx b/apps/frontend/components/project-browser/data-table/field-default.tsx similarity index 100% rename from frontend/components/project-browser/data-table/field-default.tsx rename to apps/frontend/components/project-browser/data-table/field-default.tsx diff --git a/frontend/components/project-browser/data-table/field-growth.tsx b/apps/frontend/components/project-browser/data-table/field-growth.tsx similarity index 100% rename from frontend/components/project-browser/data-table/field-growth.tsx rename to apps/frontend/components/project-browser/data-table/field-growth.tsx diff --git a/frontend/components/project-browser/data-table/field-label.tsx b/apps/frontend/components/project-browser/data-table/field-label.tsx similarity index 100% rename from frontend/components/project-browser/data-table/field-label.tsx rename to apps/frontend/components/project-browser/data-table/field-label.tsx diff --git a/frontend/components/project-browser/data-table/field-project.tsx b/apps/frontend/components/project-browser/data-table/field-project.tsx similarity index 100% rename from frontend/components/project-browser/data-table/field-project.tsx rename to apps/frontend/components/project-browser/data-table/field-project.tsx diff --git a/frontend/components/project-browser/data-table/field-status.tsx b/apps/frontend/components/project-browser/data-table/field-status.tsx similarity index 100% rename from frontend/components/project-browser/data-table/field-status.tsx rename to apps/frontend/components/project-browser/data-table/field-status.tsx diff --git a/frontend/components/project-browser/data-table/generic-data-table.tsx b/apps/frontend/components/project-browser/data-table/generic-data-table.tsx similarity index 100% rename from frontend/components/project-browser/data-table/generic-data-table.tsx rename to apps/frontend/components/project-browser/data-table/generic-data-table.tsx diff --git a/frontend/components/project-browser/expanded-project-data-table.tsx b/apps/frontend/components/project-browser/expanded-project-data-table.tsx similarity index 100% rename from frontend/components/project-browser/expanded-project-data-table.tsx rename to apps/frontend/components/project-browser/expanded-project-data-table.tsx diff --git a/frontend/components/project-browser/project-browser.tsx b/apps/frontend/components/project-browser/project-browser.tsx similarity index 100% rename from frontend/components/project-browser/project-browser.tsx rename to apps/frontend/components/project-browser/project-browser.tsx diff --git a/frontend/components/project-browser/project-client-provider.tsx b/apps/frontend/components/project-browser/project-client-provider.tsx similarity index 100% rename from frontend/components/project-browser/project-client-provider.tsx rename to apps/frontend/components/project-browser/project-client-provider.tsx diff --git a/frontend/components/project-browser/project-contexts.tsx b/apps/frontend/components/project-browser/project-contexts.tsx similarity index 100% rename from frontend/components/project-browser/project-contexts.tsx rename to apps/frontend/components/project-browser/project-contexts.tsx diff --git a/frontend/components/project-browser/project-data-table.tsx b/apps/frontend/components/project-browser/project-data-table.tsx similarity index 100% rename from frontend/components/project-browser/project-data-table.tsx rename to apps/frontend/components/project-browser/project-data-table.tsx diff --git a/frontend/components/widgets/algolia.tsx b/apps/frontend/components/widgets/algolia.tsx similarity index 100% rename from frontend/components/widgets/algolia.tsx rename to apps/frontend/components/widgets/algolia.tsx diff --git a/frontend/components/widgets/auth-actions.tsx b/apps/frontend/components/widgets/auth-actions.tsx similarity index 100% rename from frontend/components/widgets/auth-actions.tsx rename to apps/frontend/components/widgets/auth-actions.tsx diff --git a/frontend/components/widgets/auth-form.tsx b/apps/frontend/components/widgets/auth-form.tsx similarity index 100% rename from frontend/components/widgets/auth-form.tsx rename to apps/frontend/components/widgets/auth-form.tsx diff --git a/frontend/components/widgets/feedback-farm.tsx b/apps/frontend/components/widgets/feedback-farm.tsx similarity index 100% rename from frontend/components/widgets/feedback-farm.tsx rename to apps/frontend/components/widgets/feedback-farm.tsx diff --git a/frontend/components/widgets/supabase-write.tsx b/apps/frontend/components/widgets/supabase-write.tsx similarity index 100% rename from frontend/components/widgets/supabase-write.tsx rename to apps/frontend/components/widgets/supabase-write.tsx diff --git a/frontend/components/widgets/tremor.tsx b/apps/frontend/components/widgets/tremor.tsx similarity index 100% rename from frontend/components/widgets/tremor.tsx rename to apps/frontend/components/widgets/tremor.tsx diff --git a/frontend/lib/clients/apollo.ts b/apps/frontend/lib/clients/apollo.ts similarity index 100% rename from frontend/lib/clients/apollo.ts rename to apps/frontend/lib/clients/apollo.ts diff --git a/frontend/lib/clients/supabase.ts b/apps/frontend/lib/clients/supabase.ts similarity index 100% rename from frontend/lib/clients/supabase.ts rename to apps/frontend/lib/clients/supabase.ts diff --git a/frontend/lib/common.ts b/apps/frontend/lib/common.ts similarity index 100% rename from frontend/lib/common.ts rename to apps/frontend/lib/common.ts diff --git a/frontend/lib/config.ts b/apps/frontend/lib/config.ts similarity index 100% rename from frontend/lib/config.ts rename to apps/frontend/lib/config.ts diff --git a/frontend/lib/data-table.ts b/apps/frontend/lib/data-table.ts similarity index 100% rename from frontend/lib/data-table.ts rename to apps/frontend/lib/data-table.ts diff --git a/frontend/lib/graphql/cached-queries.ts b/apps/frontend/lib/graphql/cached-queries.ts similarity index 100% rename from frontend/lib/graphql/cached-queries.ts rename to apps/frontend/lib/graphql/cached-queries.ts diff --git a/frontend/lib/graphql/queries.ts b/apps/frontend/lib/graphql/queries.ts similarity index 100% rename from frontend/lib/graphql/queries.ts rename to apps/frontend/lib/graphql/queries.ts diff --git a/frontend/lib/logger.ts b/apps/frontend/lib/logger.ts similarity index 100% rename from frontend/lib/logger.ts rename to apps/frontend/lib/logger.ts diff --git a/frontend/lib/mocks/github-chart.html b/apps/frontend/lib/mocks/github-chart.html similarity index 100% rename from frontend/lib/mocks/github-chart.html rename to apps/frontend/lib/mocks/github-chart.html diff --git a/frontend/lib/mocks/npmChart.html b/apps/frontend/lib/mocks/npmChart.html similarity index 100% rename from frontend/lib/mocks/npmChart.html rename to apps/frontend/lib/mocks/npmChart.html diff --git a/frontend/lib/mocks/sankey-chart.html b/apps/frontend/lib/mocks/sankey-chart.html similarity index 100% rename from frontend/lib/mocks/sankey-chart.html rename to apps/frontend/lib/mocks/sankey-chart.html diff --git a/frontend/lib/parsing.ts b/apps/frontend/lib/parsing.ts similarity index 100% rename from frontend/lib/parsing.ts rename to apps/frontend/lib/parsing.ts diff --git a/frontend/lib/paths.ts b/apps/frontend/lib/paths.ts similarity index 100% rename from frontend/lib/paths.ts rename to apps/frontend/lib/paths.ts diff --git a/frontend/lib/projects.ts b/apps/frontend/lib/projects.ts similarity index 100% rename from frontend/lib/projects.ts rename to apps/frontend/lib/projects.ts diff --git a/frontend/lib/types/db.ts b/apps/frontend/lib/types/db.ts similarity index 100% rename from frontend/lib/types/db.ts rename to apps/frontend/lib/types/db.ts diff --git a/frontend/lib/types/errors.ts b/apps/frontend/lib/types/errors.ts similarity index 100% rename from frontend/lib/types/errors.ts rename to apps/frontend/lib/types/errors.ts diff --git a/frontend/lib/types/plasmic.ts b/apps/frontend/lib/types/plasmic.ts similarity index 100% rename from frontend/lib/types/plasmic.ts rename to apps/frontend/lib/types/plasmic.ts diff --git a/frontend/lib/types/supabase.ts b/apps/frontend/lib/types/supabase.ts similarity index 100% rename from frontend/lib/types/supabase.ts rename to apps/frontend/lib/types/supabase.ts diff --git a/frontend/next.config.js b/apps/frontend/next.config.js similarity index 100% rename from frontend/next.config.js rename to apps/frontend/next.config.js diff --git a/frontend/package.json b/apps/frontend/package.json similarity index 92% rename from frontend/package.json rename to apps/frontend/package.json index 7aaf8ee24..1b250b893 100644 --- a/frontend/package.json +++ b/apps/frontend/package.json @@ -11,8 +11,8 @@ "graphql:compile": "graphql-codegen", "graphql:watch": "graphql-codegen -w", "lint": "tsc --noEmit && next lint && pnpm lint:eslint && pnpm lint:prettier", - "lint:eslint": "eslint --ignore-path ../.gitignore --ignore-path .gitignore --max-warnings 0 .", - "lint:prettier": "prettier --ignore-path ../.gitignore --ignore-path .gitignore --log-level warn --check **/*.{js,jsx,ts,tsx,sol,md,json}", + "lint:eslint": "eslint --ignore-path ../../.gitignore --ignore-path .gitignore --max-warnings 0 .", + "lint:prettier": "prettier --ignore-path ../../.gitignore --ignore-path .gitignore --log-level warn --check **/*.{js,jsx,ts,tsx,sol,md,json}", "start": "next start", "supabase:gentypes": "supabase gen types typescript --project-id 'dfjsvzdlhmtnsqbfmcpm' --schema public > lib/types/supabase.ts", "test": "jest --ci --passWithNoTests", diff --git a/frontend/plasmic-init-client.tsx b/apps/frontend/plasmic-init-client.tsx similarity index 100% rename from frontend/plasmic-init-client.tsx rename to apps/frontend/plasmic-init-client.tsx diff --git a/frontend/plasmic-init.ts b/apps/frontend/plasmic-init.ts similarity index 100% rename from frontend/plasmic-init.ts rename to apps/frontend/plasmic-init.ts diff --git a/frontend/postcss.config.js b/apps/frontend/postcss.config.js similarity index 100% rename from frontend/postcss.config.js rename to apps/frontend/postcss.config.js diff --git a/frontend/public/img/oso-emblem-black.svg b/apps/frontend/public/img/oso-emblem-black.svg similarity index 100% rename from frontend/public/img/oso-emblem-black.svg rename to apps/frontend/public/img/oso-emblem-black.svg diff --git a/frontend/supabase/.gitignore b/apps/frontend/supabase/.gitignore similarity index 100% rename from frontend/supabase/.gitignore rename to apps/frontend/supabase/.gitignore diff --git a/frontend/supabase/config.toml b/apps/frontend/supabase/config.toml similarity index 100% rename from frontend/supabase/config.toml rename to apps/frontend/supabase/config.toml diff --git a/frontend/supabase/migrations/20240125160035_user_management.sql b/apps/frontend/supabase/migrations/20240125160035_user_management.sql similarity index 100% rename from frontend/supabase/migrations/20240125160035_user_management.sql rename to apps/frontend/supabase/migrations/20240125160035_user_management.sql diff --git a/frontend/supabase/migrations/20240209235914_hasura_claim.sql b/apps/frontend/supabase/migrations/20240209235914_hasura_claim.sql similarity index 100% rename from frontend/supabase/migrations/20240209235914_hasura_claim.sql rename to apps/frontend/supabase/migrations/20240209235914_hasura_claim.sql diff --git a/frontend/supabase/migrations/20240214045719_api_keys.sql b/apps/frontend/supabase/migrations/20240214045719_api_keys.sql similarity index 100% rename from frontend/supabase/migrations/20240214045719_api_keys.sql rename to apps/frontend/supabase/migrations/20240214045719_api_keys.sql diff --git a/frontend/supabase/migrations/20240214050450_data_collective.sql b/apps/frontend/supabase/migrations/20240214050450_data_collective.sql similarity index 100% rename from frontend/supabase/migrations/20240214050450_data_collective.sql rename to apps/frontend/supabase/migrations/20240214050450_data_collective.sql diff --git a/frontend/supabase/seed.sql b/apps/frontend/supabase/seed.sql similarity index 100% rename from frontend/supabase/seed.sql rename to apps/frontend/supabase/seed.sql diff --git a/frontend/tailwind.config.js b/apps/frontend/tailwind.config.js similarity index 100% rename from frontend/tailwind.config.js rename to apps/frontend/tailwind.config.js diff --git a/frontend/tsconfig.json b/apps/frontend/tsconfig.json similarity index 100% rename from frontend/tsconfig.json rename to apps/frontend/tsconfig.json diff --git a/hasura/.env.example b/apps/hasura/.env.example similarity index 100% rename from hasura/.env.example rename to apps/hasura/.env.example diff --git a/apps/hasura/.eslintrc.json b/apps/hasura/.eslintrc.json new file mode 100644 index 000000000..3fe5b13eb --- /dev/null +++ b/apps/hasura/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "extends": ["../../.eslintrc.js"], + "root": false, + "parserOptions": { + "project": ["./apps/hasura/tsconfig.json"] + } +} diff --git a/hasura/README.md b/apps/hasura/README.md similarity index 100% rename from hasura/README.md rename to apps/hasura/README.md diff --git a/hasura/config.yaml b/apps/hasura/config.yaml similarity index 100% rename from hasura/config.yaml rename to apps/hasura/config.yaml diff --git a/hasura/metadata/actions.graphql b/apps/hasura/metadata/actions.graphql similarity index 100% rename from hasura/metadata/actions.graphql rename to apps/hasura/metadata/actions.graphql diff --git a/hasura/metadata/actions.yaml b/apps/hasura/metadata/actions.yaml similarity index 100% rename from hasura/metadata/actions.yaml rename to apps/hasura/metadata/actions.yaml diff --git a/hasura/metadata/allow_list.yaml b/apps/hasura/metadata/allow_list.yaml similarity index 100% rename from hasura/metadata/allow_list.yaml rename to apps/hasura/metadata/allow_list.yaml diff --git a/hasura/metadata/api_limits.yaml b/apps/hasura/metadata/api_limits.yaml similarity index 100% rename from hasura/metadata/api_limits.yaml rename to apps/hasura/metadata/api_limits.yaml diff --git a/hasura/metadata/cron_triggers.yaml b/apps/hasura/metadata/cron_triggers.yaml similarity index 100% rename from hasura/metadata/cron_triggers.yaml rename to apps/hasura/metadata/cron_triggers.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/artifacts.yaml b/apps/hasura/metadata/databases/cloudsql/tables/artifacts.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/artifacts.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/artifacts.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/artifacts_by_project.yaml b/apps/hasura/metadata/databases/cloudsql/tables/artifacts_by_project.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/artifacts_by_project.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/artifacts_by_project.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/collections.yaml b/apps/hasura/metadata/databases/cloudsql/tables/collections.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/collections.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/collections.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/event_types.yaml b/apps/hasura/metadata/databases/cloudsql/tables/event_types.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/event_types.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/event_types.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_daily_from_artifact.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_daily_from_artifact.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_daily_from_artifact.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_daily_from_artifact.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_daily_from_collection.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_daily_from_collection.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_daily_from_collection.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_daily_from_collection.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_daily_from_project.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_daily_from_project.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_daily_from_project.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_daily_from_project.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_daily_to_artifact.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_daily_to_artifact.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_daily_to_artifact.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_daily_to_artifact.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_daily_to_collection.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_daily_to_collection.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_daily_to_collection.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_daily_to_collection.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_daily_to_project.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_daily_to_project.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_daily_to_project.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_daily_to_project.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_monthly_from_artifact.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_monthly_from_artifact.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_monthly_from_artifact.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_monthly_from_artifact.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_monthly_from_collection.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_monthly_from_collection.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_monthly_from_collection.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_monthly_from_collection.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_monthly_from_project.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_monthly_from_project.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_monthly_from_project.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_monthly_from_project.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_monthly_to_artifact.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_monthly_to_artifact.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_monthly_to_artifact.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_monthly_to_artifact.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_monthly_to_collection.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_monthly_to_collection.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_monthly_to_collection.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_monthly_to_collection.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_monthly_to_project.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_monthly_to_project.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_monthly_to_project.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_monthly_to_project.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_weekly_from_artifact.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_weekly_from_artifact.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_weekly_from_artifact.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_weekly_from_artifact.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_weekly_from_collection.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_weekly_from_collection.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_weekly_from_collection.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_weekly_from_collection.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_weekly_from_project.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_weekly_from_project.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_weekly_from_project.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_weekly_from_project.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_weekly_to_artifact.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_weekly_to_artifact.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_weekly_to_artifact.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_weekly_to_artifact.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_weekly_to_collection.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_weekly_to_collection.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_weekly_to_collection.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_weekly_to_collection.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/events_weekly_to_project.yaml b/apps/hasura/metadata/databases/cloudsql/tables/events_weekly_to_project.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/events_weekly_to_project.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/events_weekly_to_project.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/first_contribution_to_project.yaml b/apps/hasura/metadata/databases/cloudsql/tables/first_contribution_to_project.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/first_contribution_to_project.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/first_contribution_to_project.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/github_metrics_by_collection.yaml b/apps/hasura/metadata/databases/cloudsql/tables/github_metrics_by_collection.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/github_metrics_by_collection.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/github_metrics_by_collection.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/github_metrics_by_project.yaml b/apps/hasura/metadata/databases/cloudsql/tables/github_metrics_by_project.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/github_metrics_by_project.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/github_metrics_by_project.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/last_contribution_to_project.yaml b/apps/hasura/metadata/databases/cloudsql/tables/last_contribution_to_project.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/last_contribution_to_project.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/last_contribution_to_project.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/onchain_metrics_by_collection_arbitrum.yaml b/apps/hasura/metadata/databases/cloudsql/tables/onchain_metrics_by_collection_arbitrum.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/onchain_metrics_by_collection_arbitrum.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/onchain_metrics_by_collection_arbitrum.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/onchain_metrics_by_project_arbitrum.yaml b/apps/hasura/metadata/databases/cloudsql/tables/onchain_metrics_by_project_arbitrum.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/onchain_metrics_by_project_arbitrum.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/onchain_metrics_by_project_arbitrum.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/projects.yaml b/apps/hasura/metadata/databases/cloudsql/tables/projects.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/projects.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/projects.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/projects_by_collection.yaml b/apps/hasura/metadata/databases/cloudsql/tables/projects_by_collection.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/projects_by_collection.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/projects_by_collection.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/tables.yaml b/apps/hasura/metadata/databases/cloudsql/tables/tables.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/tables.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/tables.yaml diff --git a/hasura/metadata/databases/cloudsql/tables/users_monthly_to_project.yaml b/apps/hasura/metadata/databases/cloudsql/tables/users_monthly_to_project.yaml similarity index 100% rename from hasura/metadata/databases/cloudsql/tables/users_monthly_to_project.yaml rename to apps/hasura/metadata/databases/cloudsql/tables/users_monthly_to_project.yaml diff --git a/hasura/metadata/databases/databases.yaml b/apps/hasura/metadata/databases/databases.yaml similarity index 100% rename from hasura/metadata/databases/databases.yaml rename to apps/hasura/metadata/databases/databases.yaml diff --git a/hasura/metadata/query_collections.yaml b/apps/hasura/metadata/query_collections.yaml similarity index 100% rename from hasura/metadata/query_collections.yaml rename to apps/hasura/metadata/query_collections.yaml diff --git a/hasura/metadata/remote_schemas.yaml b/apps/hasura/metadata/remote_schemas.yaml similarity index 100% rename from hasura/metadata/remote_schemas.yaml rename to apps/hasura/metadata/remote_schemas.yaml diff --git a/hasura/metadata/version.yaml b/apps/hasura/metadata/version.yaml similarity index 100% rename from hasura/metadata/version.yaml rename to apps/hasura/metadata/version.yaml diff --git a/hasura/package.json b/apps/hasura/package.json similarity index 84% rename from hasura/package.json rename to apps/hasura/package.json index db6742494..003972a9e 100644 --- a/hasura/package.json +++ b/apps/hasura/package.json @@ -19,8 +19,8 @@ "scripts": { "build": "tsc && pnpm metadata:genTables", "lint": "tsc --noEmit && pnpm lint:eslint && pnpm lint:prettier", - "lint:eslint": "eslint --ignore-path ../.gitignore --max-warnings 0 .", - "lint:prettier": "prettier --ignore-path ../.gitignore --log-level warn --check **/*.{js,jsx,ts,tsx,sol,md,json}", + "lint:eslint": "eslint --ignore-path ../../.gitignore --max-warnings 0 .", + "lint:prettier": "prettier --ignore-path ../../.gitignore --log-level warn --check **/*.{js,jsx,ts,tsx,sol,md,json}", "metadata:genTables": "node --loader ts-node/esm src/genTables.ts", "metadata:pull": "hasura metadata export", "metadata:reload": "hasura metadata reload", diff --git a/hasura/src/genTables.ts b/apps/hasura/src/genTables.ts similarity index 96% rename from hasura/src/genTables.ts rename to apps/hasura/src/genTables.ts index e564d57aa..131ab7c47 100644 --- a/hasura/src/genTables.ts +++ b/apps/hasura/src/genTables.ts @@ -8,7 +8,10 @@ const __dirname = path.dirname(__filename); // YAML file extension const EXTENSION = ".yaml"; // Recursively scan this directory for database tables -const modelDir = path.resolve(__dirname, "../../dbt/models/marts/"); +const modelDir = path.resolve( + __dirname, + "../../../warehouse/dbt/models/marts/", +); // Where to store all table configs const tablesDir = path.resolve( __dirname, diff --git a/hasura/tsconfig.json b/apps/hasura/tsconfig.json similarity index 100% rename from hasura/tsconfig.json rename to apps/hasura/tsconfig.json diff --git a/cloudquery/README.md b/cloudquery/README.md deleted file mode 100644 index 69f397a29..000000000 --- a/cloudquery/README.md +++ /dev/null @@ -1 +0,0 @@ -Cloudquery plugins and other libraries diff --git a/dbt_project.yml b/dbt_project.yml index e181ccb18..38c1bed9e 100644 --- a/dbt_project.yml +++ b/dbt_project.yml @@ -8,12 +8,12 @@ profile: 'opensource_observer' # These configurations specify where dbt should look for different types of files. # The `model-paths` config, for example, states that models in this project can be # found in the "models/" directory. You probably won't need to change these! -model-paths: ["dbt/models"] -analysis-paths: ["dbt/analyses"] -test-paths: ["dbt/tests"] -seed-paths: ["dbt/seeds"] -macro-paths: ["dbt/macros"] -snapshot-paths: ["dbt/snapshots"] +model-paths: ["warehouse/dbt/models"] +analysis-paths: ["warehouse/dbt/analyses"] +test-paths: ["warehouse/dbt/tests"] +seed-paths: ["warehouse/dbt/seeds"] +macro-paths: ["warehouse/dbt/macros"] +snapshot-paths: ["warehouse/dbt/snapshots"] clean-targets: # directories to be removed by `dbt clean` - "target" diff --git a/docs/.eslintrc.json b/docs/.eslintrc.json deleted file mode 100644 index e8f64ca5f..000000000 --- a/docs/.eslintrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "root": false, - "extends": ["../.eslintrc.js"], - "parserOptions": { - "project": ["./docs/tsconfig.json"] - } -} diff --git a/hasura/.eslintrc.json b/hasura/.eslintrc.json deleted file mode 100644 index cb33b1256..000000000 --- a/hasura/.eslintrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": ["../.eslintrc.js"], - "root": false, - "parserOptions": { - "project": ["./hasura/tsconfig.json"] - } -} diff --git a/indexer/.env.example b/indexer/.env.example deleted file mode 100644 index cdaeb8973..000000000 --- a/indexer/.env.example +++ /dev/null @@ -1,20 +0,0 @@ -#.env - -## Database -DB_HOST=localhost -DB_PORT=5432 -DB_USER=postgres -DB_PASSWORD=password -DB_DATABASE=tsdb - -## APIs -X_GITHUB_GRAPHQL_API= -X_GITHUB_TOKEN= -DUNE_API_KEY= - -## TRIAGE -ALCHEMY_KEY= -ALCHEMY_KEY_OP= -ETHERSCAN_KEY= -ETHERSCAN_KEY_OP= -OPENAI_API_KEY= \ No newline at end of file diff --git a/indexer/.eslintrc.json b/indexer/.eslintrc.json deleted file mode 100644 index c055e5efa..000000000 --- a/indexer/.eslintrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": ["../.eslintrc.js"], - "root": false, - "parserOptions": { - "project": ["./indexer/tsconfig.json"] - } -} diff --git a/indexer/.gitignore b/indexer/.gitignore deleted file mode 100644 index ff5458271..000000000 --- a/indexer/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -# ignore local storage artifacts -.env -__pycache__/ -.DS_Store -.ipynb_checkpoints/ -*.log -*.ipynb -dist - -.envrc -.direnv -shell.nix - -# local data files -crypto_ecosystems.json -protocol_mapping.json -_notebooks/ \ No newline at end of file diff --git a/indexer/README.md b/indexer/README.md deleted file mode 100644 index 12c1eb8e6..000000000 --- a/indexer/README.md +++ /dev/null @@ -1,137 +0,0 @@ -# Indexer - -## Database - -### Setup - -Install [TimescaleDB](https://docs.timescale.com/self-hosted/latest/install/) locally for development. - -_It is recommended you test any code changes against a development database first. Please do not develop against the production database._ - -Copy `.env.example` to `.env` and populate the `DATABASE_URL`. - -### psql - -For convenience, we've added a script to quickly fire up `psql` with the database configured in your `.env` - -```bash -bash ./utilities/database/psql.sh -``` - -### Refreshing the materialized views - -Sometimes a materialized view will be missing data -(for example if we insert [older historical data](https://docs.timescale.com/use-timescale/latest/continuous-aggregates/troubleshooting/#continuous-aggregate-doesnt-refresh-with-newly-inserted-historical-data)) - -To refresh a TimescaleDB continuous aggregate: - -```sql -CALL refresh_continuous_aggregate('events_daily_to_project', '2021-05-01', '2021-06-01'); -``` - -See [Timescale docs](https://docs.timescale.com/use-timescale/latest/continuous-aggregates/refresh-policies/#manually-refresh-a-continuous-aggregate) for more details. - -Not all materialized views are supported by Timescale (for example `FirstContributionToProject`). -These views are _not_ continuously updated and must always be manually refreshed by running: - -```sql -REFRESH MATERIALIZED VIEW _first_contribution_to_project; -``` - -See the [Postgres docs](https://www.postgresql.org/docs/current/rules-materializedviews.html) for more details. - -## TypeScript / JavaScript - -### Setup - -Install your npm dependencies - -```bash -pnpm install -``` - -### After pulling latest changes - -After running `git pull`, make sure to run any missing migrations to make sure your local database schema matches the codebase. - -```bash -pnpm migration:run -``` - -For more details on how to collaborate within a team with Prisma, see [here](https://www.prisma.io/docs/guides/migrate/developing-with-prisma-migrate/team-development) - -### Run data fetchers - -To import projects and collections from [oss-directory](https://github.com/hypercerts-org/oss-directory), run the following - -```bash -pnpm start importOssDirectory -``` - -This will populate the database with Projects, Collections, Artifacts, and start fetching event data associated with these artifacts - -TODO: fill out the rest - -### Updating the database schema - -All of our ORM entities are stored in `./src/db/orm-entities.ts`. -After you've made your edits, run the following to automatically generate the migration. -If you want to start with an empty migration file, use `pnpm migration:create`. - -```bash -pnpm migration:generate src/db/migration/[NAME] -# OR pnpm migration:create src/db/migration/[NAME] -``` - -To run the migration, use `pnpm migration:run`. - -## Python - -### Setup - -1. Set environment variables in a `.env` file. You'll need API access credentials for Alchemy, Etherscan, Github, and Supabase. - -2. Install the requirements in the `requirements.txt` file. - -`$ pip install -r requirements.txt` - -### Adding projects - -Projects must be stored in a JSON with the following fields: - -- name -- description -- github_org - -A database of projects can then be initialized with the following command in `src/database.py`: - -`insert_projects_and_wallets_from_json("data/projects.json")` - -### Fetching Github events for a project - -Once the database of projects is created, you can trigger the script to gather Github events with the following command in `src/database.py`: - -`insert_all_events()` - -Don't forget to review constant settings for: - -``` -START, END = '2021-01-01T00:00:00Z', '2023-04-30T00:00:00Z' -QUERIES = ["merged PR", "issue", "created PR"] -``` - -Note: there is currently no detection of duplicate entries in the database, so be careful modifying these settings. - -### Fetching financial transactions linked to a project's Ethereum address - -The script uses Zerion to download all transaction data for a wallet address. - -`$ python src/zerion_scraper.py` - -It will store all of the CSV files in a local directory: - -`STORAGE_DIR = "data/temp"` - -Finally, these can be added to the events database through the following command in `src/database.py`: - -`insert_zerion_transactions()` diff --git a/indexer/assets/os-observer.png b/indexer/assets/os-observer.png deleted file mode 100644 index bf650d6cb..000000000 Binary files a/indexer/assets/os-observer.png and /dev/null differ diff --git a/indexer/assets/style.css b/indexer/assets/style.css deleted file mode 100644 index f48fdde25..000000000 --- a/indexer/assets/style.css +++ /dev/null @@ -1,11 +0,0 @@ -.DateInput, .DateInput_1 { - border-radius: 4px 0 0 4px; - width: 40%; -} - -.DateInput_input, .DateInput_input_1 { - Height: 34px; - Width: 100%; - border-radius: inherit; - font-size: inherit; -} \ No newline at end of file diff --git a/indexer/database/.env.example b/indexer/database/.env.example deleted file mode 100644 index 9aac12705..000000000 --- a/indexer/database/.env.example +++ /dev/null @@ -1,8 +0,0 @@ -#.env - -## Database -DB_HOST= -DB_PORT= -DB_NAME= -DB_USER= -DB_PASSWORD= \ No newline at end of file diff --git a/indexer/database/db_connect.py b/indexer/database/db_connect.py deleted file mode 100644 index 74d039cc7..000000000 --- a/indexer/database/db_connect.py +++ /dev/null @@ -1,99 +0,0 @@ -import csv -import os -import psycopg2 -from dotenv import load_dotenv - - -def connect_to_database(): - load_dotenv() - db_host = os.getenv("DB_HOST") - db_port = os.getenv("DB_PORT") - db_name = os.getenv("DB_NAME") - db_user = os.getenv("DB_USER") - db_password = os.getenv("DB_PASSWORD") - connection_string = f"postgres://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}" - - try: - connection = psycopg2.connect(connection_string) - return connection - except psycopg2.Error as e: - print("Error connecting to the database:", e) - return None - - -def execute_query(connection, query, params=None, col_names=False): - try: - with connection.cursor() as cursor: - cursor.execute(query, params) - if col_names: - results = cursor.fetchall() - column_names = [desc[0] for desc in cursor.description] - results = [column_names] + results - else: - results = cursor.fetchall() - return results - except psycopg2.Error as e: - print("Error executing query:", e) - return None - - -def close_connection(connection): - if connection: - connection.close() - - -def read_query_from_file(filename): - path = os.path.join(os.path.dirname(os.path.abspath(__file__)), filename) - with open(path, 'r') as file: - query = file.read() - return query - - -def dump_results_to_csv(results, filename): - local_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), f"../_notebooks/tsdb/{filename}.csv") - with open(local_path, 'w') as file: - writer = csv.writer(file) - writer.writerows(results) - - -def query_events_and_dump_to_csv(query_file, collection_slug, postfix, start_date='2016-01-01'): - db_connection = connect_to_database() - - try: - query = read_query_from_file(query_file) - params = (start_date, collection_slug) - result = execute_query(db_connection, query, params=params, col_names=True) - - if result: - filename = f"{collection_slug}_{postfix}" - dump_results_to_csv(result, filename) - - finally: - close_connection(db_connection) - -def sandbox(): - - db_connection = connect_to_database() - query = """ - SELECT * FROM projects - """ - result = execute_query(db_connection, query) - print(result) - - -def main(): - # query_events_and_dump_to_csv( - # "get_monthly_commits_by_collection_after_first_star.sql", - # #"optimism", - # "gitcoin-allo", - # "filtered_commits" - # ) - query_events_and_dump_to_csv( - "get_filtered_monthly_contributors_by_collection.sql", - "optimism", - "filtered_contributors" - ) - -if __name__ == "__main__": - main() - #sandbox() diff --git a/indexer/database/get_artifacts_by_project.sql b/indexer/database/get_artifacts_by_project.sql deleted file mode 100644 index f852c027d..000000000 --- a/indexer/database/get_artifacts_by_project.sql +++ /dev/null @@ -1,13 +0,0 @@ -SELECT - project."id" AS project_id, - project."name" AS project_name, - project."slug" AS project_slug, - artifact."type" AS artifact_type, - artifact."namespace" AS artifact_namespace, - artifact."name" AS artifact_name -FROM - project -LEFT JOIN - project_artifacts_artifact ON project."id" = project_artifacts_artifact."projectId" -LEFT JOIN - artifact ON project_artifacts_artifact."artifactId" = artifact."id"; \ No newline at end of file diff --git a/indexer/database/get_collection_event_stats.sql b/indexer/database/get_collection_event_stats.sql deleted file mode 100644 index 469ddb3b2..000000000 --- a/indexer/database/get_collection_event_stats.sql +++ /dev/null @@ -1,41 +0,0 @@ -SELECT - p."slug" AS project_slug, - p."name" AS project_name, - e."type" AS event_type, - COUNT(e."id") AS total_events, - COUNT(DISTINCT e."fromId") AS total_contributors, - MIN(e."time") AS first_event_time, - MAX(e."time") AS last_event_time -FROM - project p -LEFT JOIN - project_artifacts_artifact ON p."id" = project_artifacts_artifact."projectId" -LEFT JOIN - artifact ON project_artifacts_artifact."artifactId" = artifact."id" -LEFT JOIN - event e ON artifact."id" = e."toId" -LEFT JOIN - collection_projects_project cpp ON p."id" = cpp."projectId" -LEFT JOIN - collection c ON cpp."collectionId" = c."id" -WHERE - e."type" IN ( - 'PULL_REQUEST_CREATED', - 'PULL_REQUEST_MERGED', - 'COMMIT_CODE', - 'ISSUE_CLOSED', - 'PULL_REQUEST_CLOSED', - 'PULL_REQUEST_APPROVED', - 'ISSUE_CREATED', - 'STARRED', - 'FORKED' - ) - AND e."time" IS NOT NULL - --AND e."time" >= '2019-01-01' - AND c."slug" = 'ffdw-grants' -GROUP BY - p."slug", - p."name", - e."type" -ORDER BY - project_name; diff --git a/indexer/database/get_commits_by_collection.sql b/indexer/database/get_commits_by_collection.sql deleted file mode 100644 index 14cf14007..000000000 --- a/indexer/database/get_commits_by_collection.sql +++ /dev/null @@ -1,23 +0,0 @@ -SELECT - p."id" AS project_id, - p."slug" as project_slug, - p."name" AS project_name, - e."type" AS event_type, - a."name" AS artifact_name, - e."fromId" AS contributor_id, - e."time" AS event_time -FROM - project p -LEFT JOIN - project_artifacts_artifact pa ON p."id" = pa."projectId" -LEFT JOIN - artifact a ON pa."artifactId" = a."id" -LEFT JOIN - event e ON a."id" = e."toId" -LEFT JOIN - collection_projects_project cpp ON p."id" = cpp."projectId" -LEFT JOIN - collection c ON cpp."collectionId" = c."id" -WHERE - e."type" = 'COMMIT_CODE' - AND c."slug" = 'optimism'; \ No newline at end of file diff --git a/indexer/database/get_commits_by_project.sql b/indexer/database/get_commits_by_project.sql deleted file mode 100644 index 014bcdfaf..000000000 --- a/indexer/database/get_commits_by_project.sql +++ /dev/null @@ -1,23 +0,0 @@ -SELECT - p."id" AS project_id, - p."slug" as project_slug, - p."name" AS project_name, - e."type" AS event_type, - a."name" AS artifact_name, - e."fromId" AS contributor_id, - e."time" AS event_time -FROM - project p -LEFT JOIN - project_artifacts_artifact pa ON p."id" = pa."projectId" -LEFT JOIN - artifact a ON pa."artifactId" = a."id" -LEFT JOIN - event e ON a."id" = e."toId" -LEFT JOIN - collection_projects_project cpp ON p."id" = cpp."projectId" -LEFT JOIN - collection c ON cpp."collectionId" = c."id" -WHERE - e."type" = 'COMMIT_CODE' - AND p."slug" = 'op'; \ No newline at end of file diff --git a/indexer/database/get_events_daily_by_project.sql b/indexer/database/get_events_daily_by_project.sql deleted file mode 100644 index 888cd799f..000000000 --- a/indexer/database/get_events_daily_by_project.sql +++ /dev/null @@ -1,4 +0,0 @@ -SELECT - * -FROM - events_daily_by_project diff --git a/indexer/database/get_filtered_monthly_contributors_by_collection.sql b/indexer/database/get_filtered_monthly_contributors_by_collection.sql deleted file mode 100644 index eb7aec07d..000000000 --- a/indexer/database/get_filtered_monthly_contributors_by_collection.sql +++ /dev/null @@ -1,85 +0,0 @@ -WITH EventData AS ( - SELECT - p."slug" AS project_slug, - p."name" AS project_name, - e."id" AS event_id, - e."fromId" AS contributor_id, - TO_CHAR(e."time", 'YYYY-MM') AS month_year - FROM - project p - JOIN - project_artifacts_artifact paa ON p."id" = paa."projectId" - JOIN - artifact a ON paa."artifactId" = a."id" - JOIN - event e ON a."id" = e."toId" - JOIN - collection_projects_project cpp ON p."id" = cpp."projectId" - JOIN - collection c ON cpp."collectionId" = c."id" - JOIN ( - SELECT - a."id" AS artifact_id, - MIN(e."time") AS first_star_time - FROM - artifact a - LEFT JOIN - event e ON a."id" = e."toId" AND e."type" = 'STARRED' - WHERE - e."time" IS NOT NULL - GROUP BY - a."id" - ) fse ON a."id" = fse.artifact_id - WHERE - e."time" >= %s - AND e."type" IN ( - 'COMMIT_CODE', - 'PULL_REQUEST_CREATED', - 'PULL_REQUEST_MERGED', - 'PULL_REQUEST_CLOSED', - 'PULL_REQUEST_APPROVED', - 'ISSUE_CLOSED', - 'ISSUE_CREATED' - ) - AND c."slug" = %s - AND e."time" > fse.first_star_time -- Filter events after the first star event -) -, ContributorEventCounts AS ( - SELECT - project_slug, - month_year, - contributor_id - FROM ( - SELECT - project_slug, - month_year, - contributor_id, - COUNT(*) AS event_count - FROM - EventData - GROUP BY - project_slug, - month_year, - contributor_id - ) subquery - WHERE event_count >= 10 -) -SELECT - ed.project_slug, - ed.project_name, - ed.month_year, - COUNT(DISTINCT ed.contributor_id) AS total_contributors, - COUNT(DISTINCT cec.contributor_id) AS contributors_with_more_than_10_events -FROM - EventData ed -LEFT JOIN - ContributorEventCounts cec -ON - ed.project_slug = cec.project_slug - AND ed.month_year = cec.month_year -GROUP BY - ed.project_slug, - ed.project_name, - ed.month_year -ORDER BY - ed.project_slug; \ No newline at end of file diff --git a/indexer/database/get_indexer_progress.sql b/indexer/database/get_indexer_progress.sql deleted file mode 100644 index 9350a2770..000000000 --- a/indexer/database/get_indexer_progress.sql +++ /dev/null @@ -1,36 +0,0 @@ --- Gives the progress of the indexer. --- --- --- If you happen to see that the progress value is GREATER than the expected --- value then the indexer has event pointers in disparate time ranges. --- Eventually this will fix itself but a collector could be very very slow. -with expected_by_type as ( - select a."type", count(distinct a.id) as "count" - from project p - left join - project_artifacts_artifact paa - on paa."projectId" = p.id - left join artifact a - on paa."artifactId" = a.id - group by a."type" -), event_pointers_progress as ( - select - ep.collector as collector, - case - when SPLIT_PART(ep.collector, '-', 1) = 'github' then 'GIT_REPOSITORY' - when SPLIT_PART(ep.collector, '-', 1) = 'npm' then 'NPM_PACKAGE' - end as category, - count(*) as progress - from event_pointer ep - -- Move this end date around to see the state of the collectors - where ep."endDate" > '2023-10-01' - and ep."startDate" < '2008-01-01' - group by - ep.collector -) -select - epp."collector", - epp."progress", - ebt."count" as expected -from event_pointers_progress epp -inner join expected_by_type ebt on ebt."type" = cast(epp.category as artifact_type_enum) \ No newline at end of file diff --git a/indexer/database/get_monthly_commits_by_collection.sql b/indexer/database/get_monthly_commits_by_collection.sql deleted file mode 100644 index fe7a7b016..000000000 --- a/indexer/database/get_monthly_commits_by_collection.sql +++ /dev/null @@ -1,39 +0,0 @@ -WITH EventData AS ( - SELECT - p."slug" AS project_slug, - p."name" AS project_name, - e."id" AS event_id, - e."fromId" AS contributor_id, - TO_CHAR(e."time", 'YYYY-MM') AS month_year - FROM - project p - LEFT JOIN - project_artifacts_artifact ON p."id" = project_artifacts_artifact."projectId" - LEFT JOIN - artifact ON project_artifacts_artifact."artifactId" = artifact."id" - LEFT JOIN - event e ON artifact."id" = e."toId" - LEFT JOIN - collection_projects_project cpp ON p."id" = cpp."projectId" - LEFT JOIN - collection c ON cpp."collectionId" = c."id" - WHERE - e."type" = 'COMMIT_CODE' - AND e."time" IS NOT NULL - --AND e."time" >= '2017-01-01' - AND c."slug" = 'ffdw-grants' -) -SELECT - project_slug, - project_name, - month_year, - COUNT(event_id) AS total_commits, - COUNT(DISTINCT contributor_id) AS total_contributors -FROM - EventData -GROUP BY - project_slug, - project_name, - month_year -ORDER BY - project_slug; diff --git a/indexer/database/get_monthly_commits_by_collection_after_first_star.sql b/indexer/database/get_monthly_commits_by_collection_after_first_star.sql deleted file mode 100644 index e04c16d6e..000000000 --- a/indexer/database/get_monthly_commits_by_collection_after_first_star.sql +++ /dev/null @@ -1,53 +0,0 @@ -WITH FirstStarEvent AS ( - SELECT - a."id" AS artifact_id, - MIN(e."time") AS first_star_time - FROM - artifact a - LEFT JOIN - event e ON a."id" = e."toId" AND e."type" = 'STARRED' - GROUP BY - a."id" -) -, EventData AS ( - SELECT - p."slug" AS project_slug, - p."name" AS project_name, - e."id" AS event_id, - e."fromId" AS contributor_id, - TO_CHAR(e."time", 'YYYY-MM') AS month_year - FROM - project p - LEFT JOIN - project_artifacts_artifact ON p."id" = project_artifacts_artifact."projectId" - LEFT JOIN - artifact ON project_artifacts_artifact."artifactId" = artifact."id" - LEFT JOIN - event e ON artifact."id" = e."toId" - LEFT JOIN - collection_projects_project cpp ON p."id" = cpp."projectId" - LEFT JOIN - collection c ON cpp."collectionId" = c."id" - JOIN - FirstStarEvent fse ON artifact."id" = fse.artifact_id - WHERE - e."time" IS NOT NULL - AND e."time" >= %s - AND e."type" = 'COMMIT_CODE' - AND c."slug" = %s - AND e."time" > fse.first_star_time -- Filter events after the first star event -) -SELECT - project_slug, - project_name, - month_year, - COUNT(event_id) AS total_events, - COUNT(DISTINCT contributor_id) AS total_contributors -FROM - EventData -GROUP BY - project_slug, - project_name, - month_year -ORDER BY - project_slug; diff --git a/indexer/database/get_monthly_events_by_collection.sql b/indexer/database/get_monthly_events_by_collection.sql deleted file mode 100644 index 38efe45e3..000000000 --- a/indexer/database/get_monthly_events_by_collection.sql +++ /dev/null @@ -1,39 +0,0 @@ -WITH EventData AS ( - SELECT - p."slug" AS project_slug, - p."name" AS project_name, - e."id" AS event_id, - e."fromId" AS contributor_id, - TO_CHAR(e."time", 'YYYY-MM') AS month_year - FROM - project p - LEFT JOIN - project_artifacts_artifact ON p."id" = project_artifacts_artifact."projectId" - LEFT JOIN - artifact ON project_artifacts_artifact."artifactId" = artifact."id" - LEFT JOIN - event e ON artifact."id" = e."toId" - LEFT JOIN - collection_projects_project cpp ON p."id" = cpp."projectId" - LEFT JOIN - collection c ON cpp."collectionId" = c."id" - WHERE - e."time" IS NOT NULL - AND e."time" >= %s - AND e."type" = %s - AND c."slug" = %s -) -SELECT - project_slug, - project_name, - month_year, - COUNT(event_id) AS total_events, - COUNT(DISTINCT contributor_id) AS total_contributors -FROM - EventData -GROUP BY - project_slug, - project_name, - month_year -ORDER BY - project_slug; diff --git a/indexer/database/get_oss_contributions.sql b/indexer/database/get_oss_contributions.sql deleted file mode 100644 index b7b9c09e0..000000000 --- a/indexer/database/get_oss_contributions.sql +++ /dev/null @@ -1,20 +0,0 @@ -SELECT - project."id" AS project_id, - project."name" AS project_name, - event."fromId" AS contributor_id, - event."type" AS event_type, - event."time" AS event_time, - artifact."name" AS artifact_name -FROM - project -LEFT JOIN - project_artifacts_artifact ON project."id" = project_artifacts_artifact."projectId" -LEFT JOIN - artifact ON project_artifacts_artifact."artifactId" = artifact."id" -LEFT JOIN - event ON artifact."id" = event."toId" -WHERE - artifact."namespace" = 'GITHUB' - AND event."fromId" IS NOT NULL -ORDER BY - event."fromId"; diff --git a/indexer/database/get_project_event_stats.sql b/indexer/database/get_project_event_stats.sql deleted file mode 100644 index db77cc91c..000000000 --- a/indexer/database/get_project_event_stats.sql +++ /dev/null @@ -1,41 +0,0 @@ -SELECT - p."slug" AS project_slug, - p."name" AS project_name, - e."type" AS event_type, - COUNT(e."id") AS total_events, - COUNT(DISTINCT e."fromId") AS total_contributors, - MIN(e."time") AS first_event_time, - MAX(e."time") AS last_event_time -FROM - project p -LEFT JOIN - project_artifacts_artifact ON p."id" = project_artifacts_artifact."projectId" -LEFT JOIN - artifact ON project_artifacts_artifact."artifactId" = artifact."id" -LEFT JOIN - event e ON artifact."id" = e."toId" -LEFT JOIN - collection_projects_project cpp ON p."id" = cpp."projectId" -LEFT JOIN - collection c ON cpp."collectionId" = c."id" -WHERE - e."type" IN ( - 'PULL_REQUEST_CREATED', - 'PULL_REQUEST_MERGED', - 'COMMIT_CODE', - 'ISSUE_CLOSED', - 'PULL_REQUEST_CLOSED', - 'PULL_REQUEST_APPROVED', - 'ISSUE_CREATED', - 'STARRED', - 'FORKED' - ) - AND e."time" IS NOT NULL - --AND e."time" >= '2019-01-01' - AND p."slug" = 'l2beat' -GROUP BY - p."slug", - p."name", - e."type" -ORDER BY - project_name; diff --git a/indexer/database/get_project_github_metrics.sql b/indexer/database/get_project_github_metrics.sql deleted file mode 100644 index 11e19ec73..000000000 --- a/indexer/database/get_project_github_metrics.sql +++ /dev/null @@ -1,87 +0,0 @@ -WITH EventsData AS ( - SELECT - e."id" as event_id, - p."slug" as project_slug, - e."fromId" AS contributor_id, - TO_CHAR(e."time", 'YYYY-MM') AS month_year, - e."type" AS event_type - FROM - project p - LEFT JOIN - project_artifacts_artifact pa ON p."id" = pa."projectId" - LEFT JOIN - artifact a ON pa."artifactId" = a."id" - LEFT JOIN - event e ON a."id" = e."toId" - LEFT JOIN - collection_projects_project cpp ON p."id" = cpp."projectId" - LEFT JOIN - collection c ON cpp."collectionId" = c."id" - WHERE - e."time" IS NOT NULL - AND e."time" >= '2019-01-01' - AND c."slug" = 'optimism' -), -Contributors AS ( - SELECT - project_slug, - contributor_id, - month_year, - COUNT(event_id) AS total_commits, - CASE - WHEN COUNT(event_id) >= 10 THEN 'full-time' - WHEN COUNT(event_id) >= 1 THEN 'part-time' - ELSE 'no-commit' - END AS contributor_type - FROM - EventsData - WHERE - event_type = 'COMMIT_CODE' - GROUP BY - project_slug, - month_year, - contributor_id -), -ContributorData AS ( - SELECT - project_slug, - month_year, - SUM(CASE WHEN contributor_type = 'full-time' THEN 1 ELSE 0 END) AS full_time_developers, - SUM(CASE WHEN contributor_type = 'part-time' THEN 1 ELSE 0 END) AS part_time_developers, - SUM(total_commits) AS total_commits - FROM - Contributors - GROUP BY - project_slug, - month_year -), -StarsData AS ( - SELECT - project_slug, - month_year, - COALESCE(COUNT(event_id), 0) AS stars - FROM - EventsData - WHERE - event_type = 'STARRED' - GROUP BY - project_slug, - month_year -) - -SELECT - cd.project_slug, - cd.month_year, - SUM(cd.full_time_developers) AS full_time_developers, - SUM(cd.part_time_developers) AS part_time_developers, - SUM(cd.total_commits) AS total_commits, - SUM(sd.stars) AS stars -FROM - ContributorData cd -LEFT JOIN - StarsData sd ON cd.project_slug = sd.project_slug AND cd.month_year = sd.month_year -GROUP BY - cd.project_slug, cd.month_year -ORDER BY - cd.project_slug, cd.month_year; - diff --git a/indexer/database/get_project_monthly_event_stats.sql b/indexer/database/get_project_monthly_event_stats.sql deleted file mode 100644 index 531b68514..000000000 --- a/indexer/database/get_project_monthly_event_stats.sql +++ /dev/null @@ -1,41 +0,0 @@ -SELECT - p."slug" AS project_slug, - p."name" AS project_name, - e."type" AS event_type, - TO_CHAR(e."time", 'YYYY-MM') AS month_year, - COUNT(e."id") AS total_events, - COUNT(DISTINCT e."fromId") AS total_contributors -FROM - project p -LEFT JOIN - project_artifacts_artifact ON p."id" = project_artifacts_artifact."projectId" -LEFT JOIN - artifact ON project_artifacts_artifact."artifactId" = artifact."id" -LEFT JOIN - event e ON artifact."id" = e."toId" -LEFT JOIN - collection_projects_project cpp ON p."id" = cpp."projectId" -LEFT JOIN - collection c ON cpp."collectionId" = c."id" -WHERE - e."type" IN ( - 'PULL_REQUEST_CREATED', - 'PULL_REQUEST_MERGED', - 'COMMIT_CODE', - 'ISSUE_CLOSED', - 'PULL_REQUEST_CLOSED', - 'PULL_REQUEST_APPROVED', - 'ISSUE_CREATED', - 'STARRED', - 'FORKED' - ) - AND e."time" IS NOT NULL - --AND e."time" >= '2019-01-01' - AND c."slug" = 'ffdw-grants' -GROUP BY - p."slug", - p."name", - e."type", - TO_CHAR(e."time", 'YYYY-MM') -ORDER BY - project_name; diff --git a/indexer/database/get_projects_by_collection.sql b/indexer/database/get_projects_by_collection.sql deleted file mode 100644 index 24bd9cf31..000000000 --- a/indexer/database/get_projects_by_collection.sql +++ /dev/null @@ -1,12 +0,0 @@ -SELECT - project.id, - project.slug, - project.name -FROM - project -LEFT JOIN - collection_projects_project on project.id = collection_projects_project."projectId" -LEFT JOIN - collection on collection_projects_project."collectionId" = collection.id -WHERE - collection.slug = 'optimism'; \ No newline at end of file diff --git a/indexer/jest.config.ts b/indexer/jest.config.ts deleted file mode 100644 index fe997a925..000000000 --- a/indexer/jest.config.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** @type {import('ts-jest').JestConfigWithTsJest} */ -module.exports = { - preset: "ts-jest/presets/js-with-ts-esm", - maxWorkers: 3, - //testEnvironment: 'node', - rootDir: ".", - moduleDirectories: ["node_modules", "src"], - verbose: false, - resolver: "ts-jest-resolver", - moduleFileExtensions: ["js", "json", "ts"], - moduleNameMapper: { - "^(\\.{1,2}/.*)\\.js$": "$1", - }, - testPathIgnorePatterns: ["/node_modules/", "/dist/"], - transform: { - // '^.+\\.[tj]sx?$' to process js/ts with `ts-jest` - // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest` - "^.+\\.[tj]sx?$": [ - "ts-jest", - { - useESM: true, - }, - ], - }, -}; diff --git a/indexer/package.json b/indexer/package.json deleted file mode 100644 index 485d18f38..000000000 --- a/indexer/package.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "name": "@opensource-observer/indexer", - "version": "0.0.1", - "description": "Open source impact measurement", - "author": "Kariba Labs", - "license": "Apache-2.0", - "repository": { - "type": "git", - "url": "git+https://github.com/opensource-observer/oso.git" - }, - "bin": "./dist/src/cli.js", - "main": "./dist/src/index.js", - "types": "./dist/src/index.d.ts", - "type": "module", - "files": [ - "dist", - "README.md", - "package.json" - ], - "engines": { - "node": ">=16" - }, - "scripts": { - "build": "tsc", - "deploy": "pnpm migration:run", - "dev": "tsc-watch --onSuccess \"node dist/index.js\"", - "json-yaml": "pnpm ts-node --esm utilities/json-yaml/index.ts", - "lint": "tsc --noEmit && pnpm lint:eslint && pnpm lint:prettier", - "lint:eslint": "eslint --ignore-path ../.gitignore --ignore-path .gitignore --max-warnings 0 .", - "lint:prettier": "prettier --ignore-path ../.gitignore --ignore-path .gitignore --log-level warn --check **/*.{js,jsx,ts,tsx,sol,md,json}", - "migration:create": "typeorm-ts-node-esm migration:create", - "migration:generate": "typeorm-ts-node-esm migration:generate -d ./src/db/data-source.ts", - "migration:run": "typeorm-ts-node-esm migration:run -d ./src/db/data-source.ts", - "migration:revert": "typeorm-ts-node-esm migration:revert -d ./src/db/data-source.ts", - "start": "node --loader ts-node/esm src/cli.ts", - "test": "NODE_OPTIONS=\"--no-warnings --experimental-vm-modules\" jest", - "typeorm": "typeorm-ts-node-commonjs" - }, - "keywords": [], - "devDependencies": { - "@types/jest": "^29.5.3", - "@types/lodash": "^4.14.196", - "@types/luxon": "^3.3.1", - "@types/node": "^20.6.3", - "@types/npm-registry-fetch": "^8.0.4", - "@types/yargs": "^17.0.24", - "jest": "^29.6.2", - "ts-jest": "^29.1.1", - "ts-jest-resolver": "^2.0.1", - "ts-node": "^10.9.1", - "tsc-watch": "^6.0.4", - "typescript": "^5.2.2" - }, - "dependencies": { - "@cowprotocol/ts-dune-client": "^0.0.2", - "@datastructures-js/queue": "^4.2.3", - "@google-cloud/bigquery": "^7.3.0", - "@google-cloud/storage": "^7.6.0", - "@jest/globals": "^29.7.0", - "@octokit/core": "^5.0.0", - "@octokit/graphql": "^7.0.1", - "@octokit/plugin-paginate-graphql": "^4.0.0", - "@octokit/plugin-throttling": "^8.0.0", - "@octokit/types": "^11.1.0", - "@types/inquirer": "^9.0.4", - "async-mutex": "^0.4.0", - "chalk": "^5.3.0", - "class-validator": "^0.14.0", - "csv": "^6.3.5", - "dayjs": "^1.11.9", - "dotenv": "^16.3.1", - "graphql": "^16.7.1", - "graphql-request": "^6.1.0", - "inquirer": "^9.2.11", - "json-to-graphql-query": "^2.2.5", - "lodash": "^4.17.21", - "lru-cache": "^10.1.0", - "luxon": "^3.4.0", - "mkdirp": "^3.0.1", - "node-fetch": "^3.3.2", - "npm-registry-fetch": "^15.0.0", - "octokit": "^3.1.0", - "ora": "^7.0.1", - "oss-directory": "^0.0.7", - "pg": "^8.4.0", - "redis": "^4.6.11", - "reflect-metadata": "^0.1.13", - "rimraf": "^5.0.1", - "ts-adt": "^2.1.2", - "typeorm": "^0.3.17", - "utility-types": "^3.10.0", - "winston": "^3.10.0", - "yaml": "^2.3.1", - "yargs": "^17.7.2" - } -} diff --git a/indexer/requirements.txt b/indexer/requirements.txt deleted file mode 100644 index 0c48677a1..000000000 --- a/indexer/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -pandas -python-dotenv -selenium -supabase \ No newline at end of file diff --git a/indexer/resources/dune-contracts-tables/contracts-da1aae77b853fc7c74038ee08eec441b10b89570.csv b/indexer/resources/dune-contracts-tables/contracts-da1aae77b853fc7c74038ee08eec441b10b89570.csv deleted file mode 100644 index 2d3491066..000000000 --- a/indexer/resources/dune-contracts-tables/contracts-da1aae77b853fc7c74038ee08eec441b10b89570.csv +++ /dev/null @@ -1,13085 +0,0 @@ -id,address -90,0x192d663c3d6813a24a24582a76b8d872c4f6f7f9 -92,0x2fbdbc34b6015e7b40638179aa05a2d2267452c7 -89,0x3678862f04290e565cca2ef163baeb92bb76790c -96,0x3e8524770add176be381a0529e09f1c6c3502a5a -86,0x46e772210f01647fa573b915be287ff9b65ad4b0 -87,0x677f06c6a93e167c1ebd4eafa3a3f9370dfc665a -97,0x805e0a08de70f85c01f7848370d5e3fc08aad0ea -85,0x899ce31df6c6af81203acaad285bf539234ef4b8 -98,0x8ca5e648c5dfefcdda06d627f4b490b719ccfd98 -94,0x96ccf205a366e15e261d77b14586389f80a029e9 -91,0xa02fc0fc9b0b3e23ac26eb0f2d392fd5cda58514 -93,0xa2a7d8bce0bf58d177137ecb94f3fa6aa06aa7a1 -88,0xd1cba36d92b052079523f471eb891563f2e5df5c -99,0xeb29a4e5b84fef428c072deba2444e93c080ce87 -283,0x0dd99980a85dba5cc03afef716926ce5471bde23 -285,0x23e1fcf553a7db590562f8a47bd76a28e358455e -286,0x254d39d6e8b40f31d104ccb8d00dab5dcfeb79d5 -291,0x38a613beedc82571de21f9dfc3a688275fb30ee5 -292,0x4292e4a3418e5908b88194e724e72fc2bac747e3 -277,0x62c0077baa246ab15cacc4e3f9c04f7467cb4593 -279,0x665a63e3c18d121d72cd281ebf6be2ffeb739738 -288,0x6c0459bdfb9352a1d4cb8e80310b3e4d957ab132 -290,0x6c1503b6258d9632ecd9c45c82da8d581d9c7e6f -293,0x6cfbc77ba5ec023a03b2f5c3a4c367e7519ae786 -281,0x6d506b2847df0c6f04d2628da1adaf4d8fb2e81b -284,0x75f68002fd99c700160f4287d76c2fa8b9603b58 -289,0x78b853dd3dd3271fa5edd2944155fc907f411819 -287,0xbb2dd92567cd3308bb0dcd5a9d2430e1e7420428 -276,0xd16ae399f561265d895e0e2703cb34f8e47a9754 -282,0xdd4a12da198b367751481db286bce99717bed11f -296,0xdf756c9cbb0d9fce050a1b5d04d893c9b5406886 -280,0xecb3f41d906b2407fbe679f3e3aed85c27050fb6 -278,0xee9aa6ce83ef6aac5cd66115d2f2aa2fb011b164 -365,0xc629bf86f02ef13e8f1f5f75ade8a8165587998f -363,0xd7f1dd5d49206349cae8b585fcb0ce3d96f1696f -364,0xd832cc2d05b42c687a1190c548395e8fa34011a1 -593,0x009c05bfb6d51fa0439d1473681b472019bf8dfe -573,0x02ff746d8cb62709aeec611cec9b17d7dd1d3480 -629,0x054fff7ee30953ddb739458e11eaad51224343a1 -591,0x05aec4bd3446c60eba98f6d8eb5443f25d7f15f6 -605,0x061ea85f0f2936d382cad1861d7bcefd986d79e1 -625,0x0983b4899a3168c2509569faf1e4e75c57b4aba6 -565,0x0e4e9914ecf0f7177ef999774d46218614555159 -589,0x0e54e555714c7eeabc54e7c270fbca8295ba1fe8 -626,0x0e5b46e4b2a05fd53f5a4cd974eb98a9a613bcb7 -563,0x0e83e0535b82c9cba92197f4203fa7c8fd663a0e -554,0x0ec3473a72cad0281a224a64773c9fe414e458a7 -636,0x0f32eb07181488bb26433869b93b40cdeeb10624 -543,0x0ffe8434eae67c9838b12c3cd11ac4005daa7227 -628,0x101cbc599d01e90d21fc925c8222248863e3b6ea -560,0x1035a101787ac762fccaaa1bcde3f2262511bd2c -540,0x121f77beb2d9b575d53b6fe33ef97fcc20c5fdf6 -634,0x1324fdfcca5bdaa2f2351486772bf51e35f37fcc -576,0x1530e5540132bd8da96ea6eb5ace73b98973b0c1 -559,0x156bab355e81b498072c9ce3cf655e9844a12529 -599,0x167aa4ba31b8285707d1489ad705cb24cf4e5186 -592,0x182f09a75a8190f111c1762ecc904e8727d6d6d5 -642,0x1dbcecb8e3fed0151c2b2bcec288cf8e42022669 -566,0x1ec8baab7dbd6f5a02efcab711e765bf796d091c -553,0x1f895d9e8b0830cb3bbda739fa8afb186b7fbdb4 -641,0x20759732c16f54f7c1166a942076b80fe674ce4a -586,0x24a03b8d77671df7308e818d75a753a125b250e9 -595,0x298bd23e25c01440d68d4d2708bff6a7e10a1db5 -646,0x2de895629db7dfba3c102df9ab8bf2d8a67990c6 -616,0x3181e64b7d83ec4240c0dd5acab65c0078c3cb3c -644,0x3c79d158b508564e5a20211eec948242fb556eff -561,0x3d8a5eb67a7841e00ffae8f0764a58322ca02c66 -597,0x3d8f58774611676fd196d26149c71a9142c45296 -548,0x3e9c848f5b352083470e8906a4ec78c601f98ccc -572,0x3f8d557fd1e9043d4577af6a230579e4ac1c4cc7 -535,0x454f953ca4a4c6ab3f2dbb158158de55e4195abc -580,0x478cce2fc43db8ff314181815f2c588296284148 -568,0x492e66fed798ad0576ce16e5be427b71c6d2c42a -609,0x498d9dcbb1708e135bdc76ef007f08cba4477be2 -551,0x54fcb3b792e19d9ba07ee61a71e93deb124cb957 -569,0x56d5241a333aa1b3c203d7604de23c7dd5d4d943 -639,0x5705a1fcef82cb4735e456588af37e8e1ddeca2c -610,0x57c41fd87046ae9ae17fd3bf50fda1a8b8ab5ca3 -537,0x5be63bf19d30e48738b13f7b07d33839bb25044b -545,0x5fd3815dcb668200a662114fbc9af13ac0a55b4d -632,0x671dc21d4d5e6bf0350ec64a2b28e5407d4ae496 -590,0x692287540111a8a9b3323427e729073d9aaeee83 -651,0x6ddaf44f12a20c6c853dffb6687093754da28b89 -637,0x6dfd3a052bb73e609d9c2381dc48de5e2662575e -635,0x702f75b59498645f2fd5dcded0b50d7fb2f0fd2b -602,0x7110d7a3331d0919808339705642553df9b0b52d -652,0x76cb235605f9da2f423f45bf52ca5802133beb26 -574,0x776c1e69b858f1b9622085052fb7b64a196ac602 -604,0x77a0914310dd8c5ec2813a0deab498aeab2c8f42 -600,0x78c5c7aeb5f768dbaa0e729525981d3f710737be -612,0x79286dd38c9017e5423073bac11f53357fc5c128 -547,0x7ca5b3b707475fa977b10fb4fa217983fc8e32cf -627,0x7e08735690028cdf3d81e7165493f1c34065aba2 -539,0x7e9756e1427b3c38f001f9d272d8d62bef63d619 -536,0x7f2073af8f422a94a41df58ed6d9da0ed92c378d -557,0x8273217252254ad7353f227aaecd2b1c4a326fa2 -578,0x84654e35e504452769757aae5a8c7c6599cbf954 -620,0x885fa9c95fb16d10a22f07e7870f3a5252c128bb -650,0x885fedab0182699ec2f2663f776a04150ed6f7af -598,0x8b7fa05a0d43100fcc1dad71afd8cca1b81fe8af -611,0x8e7aa69d6cbbf34c8f9443bdee2601052c015529 -617,0x8eb53a4fd9d2727a49e9e68a32108c18049bff86 -588,0x8ed9f6343f057870f1def47aae7cd88dfaa049a8 -556,0x9021edfca93a0f83b013097d68be81ae4bde30d7 -571,0x90265ddc7959c098eb277d9d3e1529fce16d90b1 -575,0x916b9d7c1f5b66e42d4e4ce69c739f70b55ae6d8 -607,0x94141d7bff3d04d61ce65b917bdd37b655e08f18 -653,0x9438d5768ae8be4be9debe9eee467ca54bff026e -601,0x9b26e8bd7ebc0177b06e3168410947a5db6fdb12 -615,0x9b7bc47bdb7b07bcb34c0dd6406fcdbe4bc28898 -645,0x9ba600819d0516fabc00e840395aabe5f46fcc59 -643,0x9bcc5f7560b243e37e0adf8379fa6d9ab49a425b -546,0x9ce0115381f009e382acd52761127eff61061482 -630,0x9f48ec33d69968a76525fa8e09cca54ad9394e74 -640,0xa8f46c3f5a89fbc3c80b3ee333a1daf8fa719061 -638,0xa99800a92de57cb15a2c0dfa878f60389dabe421 -534,0xa99ad11a59ab3f47bc163c033535a26892236ff6 -631,0xadc76430bb7cb8b17ca329972a73a2eea0affd4b -541,0xaf19c8404b5fb377e97f3c1b9eb595b61a42d621 -567,0xb082d9f4734c535d9d80536f7e87a6f4f471bf65 -555,0xb1494dcade9b7678692def8da0129e28a209b026 -606,0xb2c04c55979b6ca7eb10e666933de5ed84e6876b -558,0xb54d1833aca99b0e50dfcc7f55a9165c6805bb9f -550,0xb62f6b72d974a5e3253c6d29630f591251ce6b10 -579,0xb666abf97f0a2ea624a6fa0d3fb92121f3fb591b -596,0xb9835152a8a0b56094a9c83a2849002a325dafc1 -647,0xb990c0d9b7e143c850bd8879e94fe8ad69291df1 -552,0xba8559dfa1f2b767cea440fb1ede9bfb4a9f518e -613,0xbcfca75ff12e2c1bb404c2c216dbf901be047690 -562,0xbf4a735f123a9666574ff32158ce2f7b7027de9a -584,0xc53f28c40de7fdd184ebecc94de2d3db5e3234eb -582,0xc7c145e5ec1e811b1149fd4527a23c9bcab330d0 -542,0xc91d959071091d58d601b291e43ea1f8e4f0c8f3 -654,0xcb695c104a579643b1a6a3c801e9116ba38a00a6 -633,0xcd2e436871a3f4984fb8b963aabb118ab2646878 -538,0xd068870efe7326e1dc7ae7ba489924e8138b9509 -544,0xd155f5bf8a475007fa369e6314c3673e4bb1e292 -619,0xd2367b534c1eb09867bfabe13cb72d2bbe5c9b05 -583,0xd3434df0cdf7bb81a94e499895f93de57288ee9e -570,0xd3a93c794ee2798d8f7906493cd3c2a835aa0074 -549,0xd63ab09ac2048a7ecac92f0ffad5f104edd0e032 -581,0xd7fdb47441721ac006958906e26a3d6384777ae3 -618,0xd8cb782eb540dca07c68abacdab6f82ba72590fa -594,0xda345746cd1bfceb2b5e5e35581e2519e0003578 -585,0xdb8bbf2b0e28721f9bac603e687e39bcf52201f8 -648,0xdeafc27ac8f977e6973d671e43cbfd2573021d9e -649,0xe00bdf935e4feb3cb5e0601d88a999d30994605c -614,0xe35fec3895dcecc7d2a91e8ae4ff3c0d43ebffe0 -608,0xeba30beeab051376500fb03fcd5ec32299594645 -564,0xf3885ede00171997bfadaa98e01e167b53a78ec5 -577,0xf66932f225ca48856b7f97b6f060f4c0d244af8e -587,0xf8126ef025651e1b313a6893fcf4034f4f4bd2aa -603,0xf81ad33a29c7a85cd9fbe4f3e96dfde50c7565ff -692,0x0485954f55efda230f9027ffde40466467965610 -697,0x06bbd082ff7b063a8f467a0f7e3d78bb002f4337 -690,0x08b129a7e3105bba38387ab940d5546991b209c2 -709,0x090559d58aab8828c27ee7a7eab18efd5bb90374 -710,0x0f58dbeae68161450587b6e2b521b545b695f3ab -693,0x2066264f7a87a6669594c1f041a9abb47f0365c4 -696,0x21a3ed04b3f5e17bd158aa30a75c7429bcb03169 -707,0x246ebb0514c69767547213e2ff56223062628ef9 -682,0x3b11cea427a3c96ed9771335f2567f80f4226ab6 -686,0x4027b4e4cc1e4004bdf50b91f4377375b0658cd4 -689,0x40e8cb3440c0b05eb20522d1f63397e5b36efcf2 -691,0x43919b6b8eff5563984353ffea349fb395129b6b -694,0x594af86dd3b18944360589db73ba84c1d6951eb3 -712,0x607b309dec0b5f03bcede2aa5a04b126950b9986 -701,0x6094b6ac9ec50a4ab541d7451857e4ebb33dc2ee -705,0x613982643f4c97dddf9aaf4cb2e8a02ff99c92ba -695,0x64c288f59360f7e081ec9558f5b91650b4a9e5c8 -711,0x7c7232ea5fd22f48edb6d45f58cd3b796b1db7ba -677,0x89b81e6ae2faeb168e0837cfaf8a4e6f29d9e660 -683,0x98764589f3e94a94e4b171a4d6902deb5543676e -678,0x9c49633535806beb2f26b0b0491dd3553536cb23 -679,0x9f176000f69e78ae398636d964c4441308d9fcf1 -706,0x9f5c792c4d11e67e5fac82f22395d1d50e75eb40 -698,0xa19a4e499ae49f240ae61463be670d12c3eb7e6a -685,0xa6e8f7a652785c56efde13e4fc03b3bc4c6fe218 -687,0xad797f6472bc29cebb7db950465f821157d9dae4 -700,0xb9dd61dd3b905fa0c899a708bd135f04efba5f7e -680,0xc9e46b48c47fe6a807ad61912bcf278b24a43b53 -676,0xcd8fbbea2f2b49304545aa997ceef1efeba2b25f -704,0xd322f4e5fa3ec65739d9605faa100b7d5ef36f51 -702,0xe01eec69a943dcc5cb8aa51d96e3f2eea76f2789 -681,0xec37fdb1bb827a10db02c6225960c24231913f22 -699,0xf6f4f00317f772233d0ca487f2f94331d760f12e -708,0xf9ad07eba7cd32774f35000a16de52b9ee71e05a -684,0xfe42044a2946bed7cb99613a718f4e2495337d53 -703,0xfe63667ead82ae168782e80492393f8d4d6ada7c -688,0xfebc87e4a9a4c1a44daec949a15171462ca8ed61 -719,0x3730bdc5ddf4286a8778dcb19da638db1da981ad -718,0x71eeba415a523f5c952cc2f06361d5443545ad28 -831,0x0b2402144bb366a632d14b83f244d2e0e21bd39c -813,0x0b3e006a6af5126e625c0e228adf31ea494246a3 -819,0x126783a6cb203a3e35344528b26ca3a0489a1485 -827,0x1aafb0d5aab9ffbe09d4d30c9fd90d695c4f0881 -832,0x4a330d059b67e1b4fc575500cd805d70725ea1f3 -817,0x51b5123a7b0f9b2ba265f9c4c8de7d78d52f510f -829,0x53b56de645b9de6e5a40ace047d1c74e8b42eccb -818,0x6cbdd436210e19621efaa15db5c730038166b2f3 -828,0x6dcc0484472523ed9cdc017f711bcbf909789284 -820,0x796dff6d74f3e27060b71255fe517bfb23c93eed -822,0x7c9fc5741288cdfdd83ceb07f3ea7e22618d79d2 -826,0x91175aee6dac41b9c1f749ded077568ad93b84ca -825,0xa321448d90d4e5b0a732867c18ea198e75cac48e -815,0xa5f208e072434bc67592e4c49c1b991ba79bca46 -823,0xa6a377d75ca5c9052c9a77ed1e865cc25bd97bf3 -830,0xae9d7fe007b3327aa64a32824aaac52c42a6e624 -824,0xb91e3638f82a1facb28690b37e3aae45d2c33808 -816,0xc0946f51ddd63e12c51b23f5814b43c9bc8aa700 -821,0xe69fe4b24e6288df707b68bcc2ad15c42ae50f04 -812,0xee91c335eab126df5fdb3797ea9d6ad93aec9722 -814,0xfbc2f62b6a366a177d44706f04b74ae2d60bd556 -928,0x047ee5313f98e26cc8177fa38877cb36292d2364 -906,0x06621dff1d667e464145c29e3c90a52b172bc422 -931,0x11ca3127182f7583efc416a8771bd4d11fae4334 -901,0x138a8fe6537b0f287c085519d54c2063fc05f200 -937,0x1907c69f21e04b5ac796b87dc3ee4e278b5615e1 -916,0x1a86bb2bb53b778e8f0d6000e56424ac3253cbba -903,0x1b6a6f5b6815b87ba3ed3f022870750f9c57f5ca -904,0x1be0861e15316618872e2000e564ab1df1b3006a -900,0x2fccf897309f15be6627ede1691ef34eb54d1d57 -917,0x300b5be28cd30e6cf580b9eafaeecbc0e11528cf -898,0x30815c496af3b9d0c148f49b847081e4685ca12f -927,0x316eb4c12edb9bb9e8721663444bb9818e6fab67 -930,0x379c62556c665f1edd25f2c2a0f76bc70a53b2e4 -923,0x3bed4d42859593a1f16934b8c24792e781052348 -897,0x402cf55cd702711320e83382c786996d0fbf81ec -934,0x41b7db94188e0f35333f0adaefd36780336c7a86 -905,0x43c476939ad8217693a6c1e4d4655f8a7b2feef8 -942,0x4610074f164a78ea56927ce088d786a7c9a0a0d2 -939,0x52efad2fb19d2a95ea2c1ff07585817096a9af65 -932,0x57f928158c3ee7cdad1e4d8642503c4d0201f611 -940,0x61cdc90a8ae37358bc58a859877fbf29fff56405 -895,0x73ca6abf09f636bbd3f5838e7e4fd3ae1da838ec -929,0x78ec127a3716d447f4575e9c834d452e397ee9e1 -922,0x7d8ee5298bd0b4857c245be223d9ba5a64faeb0a -933,0x847c8a790dfdd53f502cd97f4c17f904687dfb30 -894,0x8656583094ad52aaccecf23336f08d9d6f716d8c -912,0x88221c5fdaa5266e7ff54eb7f2751003313a5226 -943,0x8978a99db900c99acff33bdd0baa5852664d72bc -911,0x8f0ab8a75d6e7c762c11bbc63df4ba3b268fadf8 -915,0x90927c982c4c657c3065a7c3070bd05a0d0d9a75 -919,0x924481dd36f80e4c124d0018d941add526f5aff5 -909,0xae88c4e320c284aed8c89c45946e895cb262f55e -918,0xb5fbfeba9848664fd1a49dc2a250d9b5d1294f2a -910,0xb6a6dd4f6eed46f71bc581f69d4c96e6c2c0a70c -935,0xb88ef752f2b0d705d9ddccde7a7e90638149c4cb -941,0xba258c29a5d8e89d99d4e9656069e1ba50a66e75 -921,0xc26146969607b37e578a590db12b3aa885710f56 -913,0xc460e23914e425be5512ee6bc2c2e0e7e09b57a2 -908,0xd237f0ccfbb0f4fbb8cb8c16e59487f80ed3fe94 -896,0xd6ea754b26b73e2196d02285e1ac8e1afedb5c38 -914,0xe29bd1215729cb4dfdc7baf3f763391ffdf148b0 -902,0xe773335550b63eed23a6e60dcc4709106a1f653c -938,0xe82ba3ef327528ca96f88a6df120bc7cc51cf3e2 -907,0xe89fa795611a1da4cf5e4c7ec908f4244f13aad1 -936,0xf00cdc6de55c724165fded6b4fa70cd601443d9e -899,0xfd28148c5e245b8878b47251a15b08715c9b23b0 -1018,0x0a8a3866c2e6fc7845aae1096d54ff9ff3afcf8d -1016,0x17000cdcccff2d0b2d8958ba40c751fa9b4be089 -1015,0x227f1f14213bece5c084c41fd73b9f4f7c48c108 -1020,0x2f6427d6437d69a2a2ae5cc7cd6496fd4c170365 -1009,0x2fbab6fdc1b9236e74375ac4331f867967e6b309 -1017,0x32c2757fe876013d2d6cb3460071676f8d70a49e -1011,0x6196ac4c950817d23918bb643f4d315ebe0a09b1 -1010,0x74436167012475749168f33324e84990c8013647 -1014,0x7fb69e8fb1525ceec03783ffd8a317bafbdfd394 -1012,0x919df3adbf5cfc9fcfd43198edfe5aa5561cb456 -1013,0xa2e08590f6f0ed44a65361defce090c00e8e6e10 -1019,0xb0fc7612bbe59ce5967bb418e7207cd176ca7681 -1166,0x0684ed97c1ef124d60bf1b4f5168d79ca257d56e -1178,0x16b321c99ab31a84d565ea484f035693718c3e71 -1160,0x19f69c73cfa2fd9a01b5840c04da6c2615a4ff0b -1182,0x1fe0f15546858d9a1e84ce2e7908b160608267c5 -1140,0x209ee6f924a39bddc9a57c0e263dd5e29ceac78a -1157,0x2b7f68170a598e507b19bca41ed745eabc936b3f -1174,0x2dd8ffa7923a17739f70c34759af7650e44ea3be -1142,0x3157e0bbdc7e5dea0f4c33a0ad7211b9a4ff19ee -1168,0x3a9cad689a510a7c410ee1be17929cdf78efac8c -1146,0x3ce5af2d1455c581d95b979ab7e64b1a7d1b5537 -1180,0x4351bda490d386c363832e3fe06ef0329e4efcfa -1177,0x451032c55f813338b6e73c1c4b24217614165454 -1149,0x4735dd1f2c57d5792094facd7d2f00f03f18d489 -1139,0x48a5322c3021d5ed5ce4293112141045d12c7efc -1145,0x4d083d94520a6cf295a039aca39e0df09998a053 -1153,0x5ea2321abff78e81702ce877319cd775e0dc865b -1167,0x5f1e8dc1c296a26188e1e04ed4bb6d1432226650 -1138,0x621ce6596e0b9ccf635316bfe7fdbc80c3029bec -1156,0x68389d5c1ecf72750ddd274e2c59b77e28284fcc -1176,0x690aa2591e57180cba5a6123e9d462907a5e1c95 -1181,0x6f620ec89b8479e97a6985792d0c64f237566746 -1154,0x7327f67674d9bb0fada7fa93a52f6182d2be55bb -1141,0x7a603d06007fc09f896fb75644365ab091a7b91a -1143,0x811cd5cb4cc43f44600cfa5ee3f37a402c82aec2 -1169,0x8158b34ff8a36dd9e4519d62c52913c24ad5554b -1151,0x894c9fb45aecc4f757c2d07a692504e8e29a5747 -1150,0x896aecb9e73bf21c50855b7874729596d0e511cb -1155,0x8c14a40c488e16b055bc4e250563b5480047f01e -1144,0x8e1e582879cb8bac6283368e8ede458b63f499a5 -1152,0x8f00a5e13b3f2aaaddc9708ad5c77fbcc300b0ee -1175,0xa43bf6193a89d28edb529ab5ca9ad7506798f9f1 -1171,0xa672384aa77d1566a915770f5987b7d4c3ba6f68 -1165,0xadf040519fe24ba9df6670599b2de7fd6049772f -1162,0xb205d0aef84c666fbbe441c61dc04feb844444e6 -1183,0xc12b9d620bfcb48be3e0ccbf0ea80c717333b46f -1159,0xc9e53bb96a8923051326b189bbf93ee9ed87888b -1164,0xcde2d0988f3bda4a956be9cbf5d23573958a6c15 -1158,0xd2b75447a9e1f7d26fb063779f519db170b0e62c -1173,0xd342c153f7a58dbe6cd458424c831ab0c2629e49 -1147,0xd58fb16eace4693b2c641cae6850a82763c00a34 -1172,0xd6a78766514cdfc1a1fa188a7782b52313133705 -1163,0xe6320460aca9e4a4385058eefd7d4d70123fc9c9 -1179,0xeb0908806595d06643e15ad9af62dfd0653b570c -1161,0xf18d727c034f47ae2c0fe221c1cf4a15f0557b5f -1170,0xfdc398d782200333047b3bcc81cbf0482c65dc81 -1148,0xffceacfd39117030314a07b2c86da36e51787948 -1203,0x02800d3d33e7e9d32c586fcaaea0412e5a57792d -1209,0x158a3bf73c07a64225c1568d4c85a1d78480c0ec -1194,0x2208705b6f232ef498d46514b5130192789e9dc8 -1198,0x2c49bdff3acb931491d1c0231952d9718c516539 -1204,0x2c7a8122a1b512adc848024ce72e837001ecb586 -1196,0x338f48b6c18a49fc7dd7137498617d06a5e089c7 -1195,0x493f1b81b05e29e86d9a4762944ed8b5fd2924a5 -1205,0x71762f1fdc36afc769a25dcd73226c6edca6832a -1199,0x755db0a2c1041b20ad123792181c55a3d6e2ffde -1192,0x7ea8c22e6dcd7bd69eb180664da68e1f1f11d696 -1208,0x815f667a542653d89d44ff9d09b11dd7d4d78004 -1207,0x81d10cbea2b7e2ce9589cfe48442a83f013885ab -1193,0x8860dc637a513027e4883a77a5567cfeaa718ae2 -1201,0x978e214ce2e34d14cf80ee6c0951909cab18abb8 -1211,0xc6433cd73071bb5fd607a0bd3cdc15487590825b -1210,0xdcc3a56e97a7448c64341e38867241f65168817d -1197,0xe2f50042766bdea8bcbfa0ff6a94bab65f897826 -1200,0xf220408d5f8cf3a2d43126d3413fe3acbdf94363 -1202,0xffbd0e92861b9cb4b7ae17361786868e6f2f9324 -1327,0x0669e8873a8d76f8eb1e8b4fe57e72ba0e97dc59 -1325,0x4df88b4eaf8359350649d9135a8f977cc4641981 -1320,0x5d322c11399b2cc0be96a04457e46bc050f8078f -1321,0x777777773fdd8b28bb03377d10fcea75ad9768da -1322,0x7b8efa83216e9a7fd17f071839b5386c7bdb2fea -1323,0x8cc53ea4554cf11c99ea872946c8469d5b6eb399 -1324,0x93e461adc4652ded074f0c36a358e58ab3f47ce6 -1319,0xbce880cd2edfa04481b5e9ef4969edc64234b222 -1329,0xc19d09179b7c04d98cda5596debbe4101539544d -1330,0xc25fab5a0e90166baed37f9c670c88f6a0aa85cd -1326,0xd54036d8d7cf3273dbdef21b44ffb82767d64d12 -1328,0xedf890b3d973b9fdc85e85c246614597484dfafb -1388,0x017cbf62b53313d5ee3ad1288daa95cd39aa11fe -1384,0x0e71c96a01754c29a41b554ea2ba5f3f9d49eb5b -1376,0x0ea810c95d9940bf1b878ee982905de310ac6981 -1389,0x178b526358eb7e66cce357f452285e5debb1d57f -1400,0x19382707d5a47e74f60053b652ab34b6e30febad -1356,0x1c8f469e3c1d87b108ec69b165037d5d7365f8c3 -1392,0x22e7f6359ee5a65b7e769838f983dac90fed259e -1391,0x2afe484dcc386edbb1275dd34559df948d3fa78b -1403,0x2d306498a7faad3bf19ad8dec3584a7d3ad65db5 -1372,0x2eefbb883b6a1b64a75b2aa18cd4dfbe19e684a3 -1353,0x33aa8f94428c0f891dfd77dd141878abcaefebe8 -1394,0x34fa07aecd10f45bc72934b31d4a601d04045d8e -1362,0x3ed788455ec4cd272c578f7218bc5c5a8db3b9b6 -1381,0x4c2ba62e549df0c34a0e93948af417f76d0e92ad -1357,0x4e9ef003d1f643868329b8e0b1bceb1d7ddf7cb7 -1402,0x539505dde2b9771debe0898a84441c5e7fdf6bc0 -1373,0x5add2f7ebb50246cd8694da0fb106eeaca3312e0 -1379,0x5c17f04d95a593bbd8b1e553f40cb3ca5655dd22 -1390,0x6104d21888cd996918c8cba7480c71271dee3120 -1387,0x624b944afafa1456407de3ba3fb713dee0e619c5 -1385,0x64ef984fdaa7d13d71aadeee50e5826a69b33c76 -1404,0x68c80d3d6567b5998f78ac0c81467d5d4a82e781 -1377,0x6aff1846413bf15a8d7e72c799b96bf37e424b13 -1361,0x7b8a1d28ef8d09b442c6070316d92401fbad36bf -1359,0x7cb59d76eedebd0889c851c82a8498150a1651fd -1363,0x7f29188ef5fb39e11da6c131beda2c3ca9475c58 -1397,0x8128a04821bacc77ff3ec70d88fdef947da1ff02 -1367,0x866a4b7ac6a77bfc1bede3b04fa1b9851b86ef1c -1407,0x8db4a31683f0b8af64efe13c0d304da6fccfce13 -1360,0x8ebe970beedfe7616a50de707ad53199068261dc -1374,0x918b1df459f3caed6224aa7f0c21ada55df164dc -1355,0x94e38f592d92964ebe0684a65f1318b91dc54e9e -1396,0x973b9a7d490f06fea3dd06432cea3b097e9cecb9 -1366,0x9a92b76fa1782f60bcaf76e7dda1e2b8dc9e2493 -1369,0xa18019e62f266c2e17e33398448e4105324e0d0f -1399,0xa7f92bdd0c468d91861445eb30446a3700fc94ca -1395,0xacad4d26a2e61112ab1bcd86e5e361554437fe3d -1401,0xadce6fddd868e865e4d56000832791fdeb29eeda -1393,0xb2af500c94f7b4dff189120c97cb25d8ad5aa028 -1406,0xb4317f54ed4623834ce6c12050e7cfbb212dc4be -1364,0xb4887eb77ac0757883df4fa99a1ae5cd088a28c2 -1386,0xb61a23ec0c576d6864ee81522b6d2d60300c8351 -1378,0xb747015fa5550039b807135f22c596bd19a1900f -1405,0xbc456c278eb11d26cb0912a8a2e1fd93c7c624ec -1398,0xc7ebf549c1454165e385a4c984b499c6e46c3ec7 -1351,0xc954a6e12d9d6ca3f2c6aa8f286d7a4b302c611e -1350,0xcb9ee0bb0cef5dbf21aad6c420473ec5c44657f8 -1380,0xd2a4ba582dc9fabf3293c2c46e0afd6c4b5719b1 -1375,0xd7c751fa32590451548b100c4f6442f062c9bc8e -1354,0xdd63ae655b388cd782681b7821be37fdb6d0e78d -1358,0xe1aa88d62d3a6b48f5d1d0c997d14c2323180f83 -1382,0xe29f72c3ebf6db89a7d25c25809ab8e03d42fe7c -1383,0xe91172020777b03a94627dbe0b94c6b8389fb99c -1370,0xe98c509f6c8bebdd62beedf78b4d276bb41f2913 -1352,0xed51c628ae26e3355742af878b1f39051937fb1d -1365,0xefe2afe3f2e03ead747c61ff0fdabb1697441d84 -1371,0xf24a56ca4d5bd33ecb04a9f4734e6e8d57c51880 -1368,0xfd5b3e34b53bf6f603155620821de612006c8a45 -2355,0x00d59bc35174c3b250dd92a363495d38c8777a49 -2332,0x06824df38d1d77eadeb6bafcb03904e27429ab74 -2363,0x07f544813e9fb63d57a92f28fbd3ff0f7136f5ce -2334,0x08947e304064b3f3ef2b99fca7e549c5fc3f75d4 -2351,0x09236cff45047dbee6b921e00704bed6d6b8cf7e -2352,0x0a16cb36b553ba2bb2339f3b206a965e9841d305 -2514,0x11bbf869250424bedc5f162094aff8b4a45be841 -2345,0x20ffdb7d91544f8befd4f7a85e13bb9d773655ec -2362,0x3460dc71a8863710d1c907b8d9d5dbc053a4102d -2333,0x3c8b650257cfb5f272f799f5e2b4e65093a11a05 -2357,0x41c914ee0c7e1a5edcd0295623e6dc557b5abf3c -2340,0x45ff00822e8235b86cb605ac8295c14628ce78a4 -2337,0x4a9ea0dd5649ec4b6745c60d1769e2184c1782dd -2361,0x585af0b397ac42dbef7f18395426bf878634f18d -2348,0x58f62efb1528d006d22771623c3c3e7953cbfca2 -2342,0x5d5bea9f0fc13d967511668a60a3369fd53f784f -2329,0x5f2f6721ca0c5ac522bc875fa3f09bf693dcfa1d -2330,0x64dd805aa894dc001f8505e000c7535179d96c9e -2335,0x6666b2df7a328cf775778ebad368f5f13e39ec4c -2364,0x6b5d62915266d859aef91f826b6acf17fad2d033 -2343,0x6dc9e1c04ee59ed3531d73a72256c0da46d10982 -2353,0x73746410b0dd4526e1fa00d0854e99ba54aefd30 -2356,0x79bca9bcc19e157cb5f8c5a2f4d6cb951b1f8dce -2359,0x82b83a0df68aaedbd187eeca8f242e9d4880cc68 -2367,0x846e822e9a00669dcc647079d7d625d2cd25a951 -2331,0x9560e827af36c94d2ac33a39bce1fe78631088db -2360,0x95885af5492195f0754be71ad1545fe81364e531 -2328,0x9c12939390052919af3155f41bf4160fd3666a6f -2354,0x9c7305eb78a432ced5c4d14cac27e8ed569a2e26 -2336,0x9d4736ec60715e71afe72973f7885dcbc21ea99b -2341,0x9e8676f76696524eb49d9d7f3aee97bc52b7c8a5 -2347,0xa062ae8a9c5e11aaa026fc2670b0d65ccc8b2858 -2350,0xa132dab612db5cb9fc9ac426a0cc215a3423f9c9 -2339,0xbdd6f9662e904a9176aafcbdded45d076b5170ef -2349,0xda03dc399af3f1545748243aafbc5050a63b17ec -2515,0xe255e0774416604dd53f75ea8301157dcce6eb03 -2365,0xe3c5e29ea198e617ebc707718707beff9393fa51 -2338,0xf4c67cdeaab8360370f41514d06e32ccd8aa1d7b -2346,0xf74741f2184929c988009abc6c758ab3f2318cb8 -2358,0xfaf8fd17d9840595845582fcb047df13f006787d -2366,0xfb1fc21d2937bf5a49d480189e7fed42bf8282ad -2702,0x225a86073c75f704bffc177d6d71cb2df5648393 -2701,0x4064206b0a1f5ba603dd8016ee36141f54506051 -2703,0xe60cfcef001312aab2548fb19a508efed7370ceb -2990,0x075b36de1bd11cb361c5b3b1e80a9ab0e7aa8a60 -3648,0x0e29d422e1b89088eeb3124b16b43b3826a6ebb8 -2982,0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad -3647,0x40d51104da22e3e77b683894e7e3e12e8fc61e65 -2984,0x560da69ef841e1272c65ef9eba538870f8c0c484 -3613,0x5dc88340e1c5c6366864ee415d6034cadd1a9897 -2988,0x61ffe014ba17989e743c5f6cb21bf9697530b21e -2992,0x62094cda36dd8945a2c158a4c6c8865c5b34fef9 -2989,0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45 -3641,0x92e1fc4ae999315f17ddab195f816bf3262aca4b -2983,0xb555edf5dcf85f42ceef1f3630a52a108e55a654 -3646,0xc3756c1bf5020a317b9b64678ceaaf0ac92f2209 -3645,0xce6d8305b04916f7a10161667352ed332aea22fd -2991,0xe34139463ba50bd61336e0c446bd8c0867c6fe65 -3643,0xe39cb42320021e2dc0f926030f8ee7de39d93040 -3644,0xe40b4a25328dc87bbe396662bac0cd1c1be6e883 -3642,0xef1c6e67703c7bd7107eed8303fbe6ec2554bf6b -4826,0x31fe56609c65cd0c510e7125f051d440424d38f3 -4825,0xfb2dc580eed955b528407b4d36ffafe3da685401 -4925,0x3f5af9cab221dc6bc776a329493e070a3b5adcb0 -4924,0xb45a2dda996c32e93b8c47098e90ed0e7ab18e39 -5178,0x2e1561f99ee84e1f02e8ebeaafccb05f4ecc7564 -5179,0x46d27f14305fb231f54c38dfc779f512d4bf5704 -5177,0xa9ba7a3e513b2e24434eaac4ca99409126a5c1fe -5210,0x02612d20cc087670a959bb12ca3c5fd56c8a3db3 -5207,0x1293a54e160d1cd7075487898d65266081a15458 -5205,0x41c9b5639e3f2f6c61e9b78b2c6ff3746e79d91a -5206,0x6c84a8f1c29108f47a79964b5fe888d4f4d0de40 -5209,0xda534b567099ca481384133bc121d5843f681365 -5208,0xf94d0d0471ae14b6310d94fa4f95e5fc9f3ffc17 -5423,0x00808581f24f017f2a4225cf3bb6cff968982d1f -5416,0x04cdaadccb15214357fa65547e32bbee3017988c -5420,0x0bba31eaf82c649c0b9579f5f44374c74f4605ea -5441,0x0d42db49ac6bb7b86a2e98ea2544a693a45a2625 -5409,0x0e1d366475709ef677275e4161a20456cac2071f -5373,0x10c06f8b3695813276b4a921c06bb3b122aaf9d2 -5397,0x135fc9d26e5ec51260ece1df4ed424e2f55c7766 -5422,0x14efd43439345a2ac8adc6b8243609429bcacd14 -5438,0x1d751105b5eea16ef544b85a4c094bbf11fad134 -5389,0x1f83fa6bca0de79bb4b3149390686a45bed56378 -5405,0x20f3a3eed63b01853599a8d267750bbfcd6356f9 -5384,0x227549075404eb458452e8bd3bf6638dfe6a4764 -5434,0x28673750bef2c88f276c449268e84640dc89a572 -5376,0x2a4714efe5e839dd1cc0e44b6a1ed7cbcb56ba8a -5433,0x31787f747d98a24fabdac09b1d0a23f91b972d91 -5388,0x31d8eaff8af8c1207644b1e04980d3e821fdde05 -5408,0x333f344c3a68f5379c42673a4b78e0ff163e4554 -5421,0x347ba60b11deea7521542dca371650700248a5ff -5415,0x3ecc47016c47948ed6c88fff689429fa0141e92d -5430,0x42d3048b595b6e1c28a588d70366ccc2aa4db47b -5394,0x4616fc6060d6d6176f49edc6bb6975197f0d2e4a -5402,0x4a70ee403a71f39dbdf53f2b7bac728ea6787724 -5412,0x4f247c69184ad61036ec2bb3213b69f10fbede1f -5406,0x52cad924093414cf7e0eadd597ad985b33d17a7c -5407,0x58c892e6bc196371f7fb425177b0e2aa906bc007 -5390,0x5bb3dcac11fa075d4f362bb2d0de93fa821f1da9 -5386,0x5fad30c707c8c4737b45a4b800a439b0a6ef8a88 -5413,0x6284c3c151fe74bd0096865019a4092134397f20 -5396,0x664244560eba21bf82d7150c791be1abcd5b4cd7 -5404,0x710baa1e42ddc0bcc972920601b0236b1be600a3 -5436,0x7181aca2a01bc98596b1d5375c97389f0d136b2b -5400,0x7185fbf58db5f3df186197406cec2e253a1f5fe6 -5398,0x74af262d0671f378f97a1edc3d0970dbe8a1c550 -5403,0x76ce2cb1ae48fa067f4fb8c5f803111ae0b24bea -5429,0x7c487845f98938bb955b1d5ad069d9a30e4131fd -5440,0x7e80648eb2071e26937f9d42a513ccf4815fc702 -5424,0x83ccfaaa705bf3b734b50121d47b82d58ae91796 -5435,0x851ced625ec2f8032db5a0f24811a594a2a5a47f -5401,0x8a4cd3549e548bbeeb38c16e041fff040a5acabd -5446,0x8c4b615040ebd2618e8fc3b20cefe9abafdeb0ea -5445,0x8cbc8b5d71702032904750a66aefe8b603ebc538 -5393,0x8f18067d118b1dd1d7a503b0b6ed255341068029 -5442,0x8fca03e0e8c04a9d6824fda9305a09f98543a450 -5399,0x902dd246e66d8c3ce652375a723f2a52b43b9aae -5392,0x929b90117810d1dee0d75c9c94d5d613fe7837a5 -5377,0x9cf91118c8ee2913f0588e0f10e36b3d63f68bf6 -5428,0x9eaec3dbfb930360faf2e286bce4223a80ab6da0 -5391,0xa01ce7cafe37504040dc365e72f2ef77131964ed -5448,0xa2a4cec0ec6b39299fcf36c9a6f4bb21a15e5abe -5380,0xa7d58d2bd95330c7efb2b6219759aec359a9e270 -5418,0xa825421c5d8747eb1a0195ce2518ad40cb5d8320 -5444,0xac4190bff783b19812137c38e7d3c724b655f1d5 -5382,0xac63f537964b0c98141bdd676c7ae12d5de7788b -5374,0xaf179d48586909c04d6b01a6552a4704128a1397 -5411,0xb0435b47ad26115a39c59735b814f3769f07c2c1 -5439,0xb39a012110ec01bfb140bf6fe911b0da44460805 -5375,0xb6ee8d1284773061b9bcb5ba1e72d4f86477c412 -5451,0xb9ada198ec57d629d70f4dc65eb8804e97770f4c -5378,0xb9eaeaaefa1cfd2f1252bf2e8d574b04f465d228 -5385,0xc45c3d9c54f26dec50604feff7dc46b1c47271e3 -5437,0xc6b309fdc8f3e0eb20c0f0691f2a81f2fc8c3e36 -5381,0xc771ed66f869a14aff49db331cf13c003fe804fe -5383,0xca0b071899e575ba86495d46c5066971b6f3a901 -5387,0xcf4c511551ae4dab1f997866fc3900cd2aaec40d -5431,0xd85da690ef288a6976de0e85fb2aad512ebafbf7 -5443,0xe135ef65c2b2213c3fd56d0bd6500a2ca147ac10 -5449,0xe4c774b279406d8166f93e59bfcbaf4f4d1fa418 -5419,0xe87765b2e864db282a03e3b88494e08c2635e4a9 -5427,0xe97011c377178257d6015c3480d93be49eb0d623 -5379,0xe9d53b11d6531b0e56990755a7856889fc59848d -5395,0xebc1977d1ac2fe1f6daaf584e2957f7c436fcdef -5450,0xef5e6ee37747470f094034178a2dc9a99590d18d -5426,0xf12832a5917659786fd4ceb40b32bdc211834de5 -5425,0xf3cb865903f74445e89c0aacc19fca3d82032c7d -5410,0xf58d5d56aefdf755dbbf6e636b83f39af2f31977 -5414,0xfabf408b0a476672c0ba05b9efaa1a3ed1149d35 -5417,0xfb5409cc083d93a2494873b5a56669c59ededdfb -5447,0xfb967f8ebb9b9cef32dcb4d33fc1559cef16bc15 -5432,0xfbd7d24d80ee005671e731a7287deb6073264dd1 -5875,0x0024fc033708ab6917e3003cf425a289cebf1c9f -6122,0x012122615a48d1a0c5b15e147baa54ffe89b9fc8 -5550,0x016aadc3823044026174c64732491dc3e2c3041a -5992,0x01b93887c08b40530fc6f4000c6a745c81e1a3fe -5966,0x0221240a18d3118400f07d15df34ff460da10a47 -5955,0x022ca1d3570f6c258ed47c381541d8225176e385 -5938,0x024148079617fe41add3faadbb9ccb208b78aac1 -6009,0x02668a6d9b8c9d4c8aef7916270e945c3bfbbbae -5729,0x032540e69963235b3a1f56e61f8659a425655083 -5925,0x043d7d6b42714d79e92ce01aa45789be4a83a8c5 -6046,0x0496b41645e91a71d6839b90f57c356f2f68a6c9 -5637,0x0499a85fcc6c41ea00d6afef803b9474b042d617 -5918,0x04fa9a26b0237ee5046f2451d2b91b8f35c718ca -6067,0x0565b1ab5cee7075b32c2d6a5b9da44b708fb898 -5675,0x05ce8b455e3695bf56caeb329ad1ae4501fa4a20 -5621,0x05fdb7b90cb47edd5c2856059baded89f11dd0fd -5834,0x06011212ec56b65133b13c4ad3f11f1d40aa2344 -6154,0x061ff6f9a9fa4f4071c1a2d454f2bc3cc22a3485 -5696,0x06aeee7a39d1ab39fe42b81cca329fdbe7f41a4f -5583,0x07a06ff3ac30ccb592598bd880a36fe50d42e2de -5895,0x086cc1c04ef1aa4797c18a946c5688bf3949e89d -5555,0x087ae43c00035fd3e1944513fd570f416faee62c -6116,0x0886e6bea150ed770b2ee3341332339c5c08d9cd -6120,0x08bfe49094940702b33ebc5ce7d0fe84ae9608d2 -5926,0x08e2c2ba4f48e1dd103c12a38d515ba980f4e5ae -6124,0x08ea9c467ba8042caee81d7b56dfa539ba05e021 -5877,0x092e6b8dfddb4b732c89deb4a700868cc5d58b70 -6119,0x09489201600e5eea6b7d9cf4bb2cfe677d7efa9a -5695,0x09b7b9e47e8fd3329c43154c4acf66180ce64a95 -6066,0x0a47d5f27149270d45d74abd45fa30e567ab9b7d -6028,0x0a6851c7d112a27019d84dccb9ce0c0cd8b75325 -5548,0x0a9397378c427e39bde0c53e6626523b7d08aacd -5667,0x0abc94987f6b98bfe6fd40f749e445a2857a4ee3 -5547,0x0b69063cfd89b25620f93a81ee9b1e4b20a79049 -5814,0x0b8f398d8dbb74bf2dc475d02e802334777be900 -5823,0x0bac9ec4126ce5b62bee2e99823c2b2e96c396b5 -5685,0x0c09f6c136e144a5d84b4161b2fc3f245700f62e -5587,0x0d1098469d651161cc8211e2973afd5ad1cfb7a9 -5663,0x0db550f1f2b538d763393760fedc33be7945c577 -6084,0x0e12de09cf0a8c5653fadba8b3cd441590093b74 -5556,0x0e4641f7ba379d20e8bb84819b07c9234d0bddcf -6000,0x0eb894b1cf3415538b9d64f12a3b3ab013966f50 -5689,0x0eeff6deac317ac3f5ef286d069d213570eb0840 -5867,0x0f392fc503432f9d5336b10159d0d27751540161 -5874,0x0fa6b2516ce46013182f3f1f31aa7bfb04fd409b -5657,0x102f559555022bdf6630b8c84ecbcadb6e90cbdf -5539,0x1071f3b2893f41094b1c84f090ea023042bb70c0 -5818,0x1122494f58831eb5812f9bf6c222f9a0b251279d -5763,0x11e6ad917b3d6cfdc282a1dc117ca0bdc0b26821 -6068,0x129e14f5e79f1423b79d309a2a7ee43121554c2f -6137,0x131f611470d831f56e2fcaa72250a5d768056dfe -6140,0x13d6616b61440c2b80fb9354c1c28504e6c31855 -5568,0x146444d363028c5495f7d57a4c49e641ce8017b6 -5832,0x14c50f2527ff4c68fac12394883233c701938636 -5778,0x1582272e85e4f058018f933f7ad924250bab1881 -6088,0x15983b0adb819b284baa05011f8a6a23402b0e77 -5651,0x15c9eae416c2e3fbecf2248003a2b2677127e9db -5987,0x16012ef3eec79db8f002a05c7a8102786e5c6c38 -5559,0x160ca569999601bca06109d42d561d85d6bb4b57 -5891,0x1687fdc8a409be442e2dd3de42b1fcc974ac9ee8 -5596,0x17019988cc39733f5b02338a6adc04ded7e1afc8 -5982,0x170a5714112daeff20e798b6e92e25b86ea603c1 -5922,0x1751ddce8d8b917223e05d8bd015dfb81bf1fcf2 -5600,0x1757cf4c1edcfbb42dfdff91644b8d8d5e90f904 -5836,0x17d8eb2ea0e9d640b97927bfdead5cc83f81a216 -5869,0x181b43ac8f2bcda81e0f40b16cb74b109deacfc9 -5882,0x184c1da4e33ee3093af05c9100ac9b0d5e78efcb -5610,0x1853bcf689fdcdb4fd2a74e89205598855bee8d3 -5885,0x18d28fe27829fcf030e0346dde7c3316c0e49039 -5624,0x18e37a53cac94ffb4a3aa8544fb4b6c2bf055019 -5757,0x197802dd60fc7f2ce1a3f0b5bc5ab17af848bfa5 -5880,0x19b207721196c01c66609246308f4035eae0104d -5764,0x19c185494fd9ed3c8191fe21e965a4e3d2dcb781 -5721,0x1a0bb66e40d2b998a0c5fe19ff77dcd428a76ccf -5840,0x1a59ac5d4bdf35b5255c6d301ad23f6eb1530d57 -6133,0x1b0ed6567a53eac1fe6d859d3fb2be11738cf0e4 -5956,0x1b3dd18f0a31926a8cc24df87c69b0ba303a04a1 -6128,0x1d70b630303ad656697719b5ec78b93855236324 -5678,0x1d936b0ae8fdc2b240c5bbb114a9b699211c9752 -5517,0x1d94777e3a288a7cdab316f17e43d31800fc58b0 -5672,0x1da1c269b59e4c523ae37f6678357939634052ba -5588,0x1dc1a62f7f333347b3885ffeef71cb4d00829fac -5977,0x1e368780363ddadfec3b5382c397183778dab63c -5735,0x1e4ada1e0390ec9de281df3839fcb2998deb9b67 -5937,0x1e95faca5f99dcc8e517fea193a3c3b0aac1e0a1 -5737,0x1ebce103b528bbce8c3bf0bd594cdf18d284cef7 -6115,0x1f8e523e195c622ddf6a80a8db01687ff6a3caef -6057,0x1fce83884dc4263a92825543522379e37d7917df -6113,0x20612e6a0ddadc322b7b48e3c4246fb23836210e -6104,0x2062b7caf899cf2ebc9d07ed45d323f6b096383e -5927,0x2113a7d5d203be717083746bc6040c3fb8673389 -6086,0x21382a033e581a2d685826449d6c9b3d6507e23c -6056,0x2167af59e68ea29387ee2eaa60b7663d6158f1fd -6031,0x217d47011b23bb961eb6d93ca9945b7501a5bb11 -5633,0x21c175d6e37226a3c107347251c20297b47517dd -5511,0x22e12a53cb40533707881607f808c7939cde9e0c -5991,0x234678962dd56ffb71f97f0f0f45a66624145470 -5845,0x236057f9f13faa625da73cd1cb1e838f15d6c6d2 -5599,0x23823f1c7c4ae2acef3bccfcd5f282ae078150c2 -5525,0x23ed8fff5a8f79d01a453cc9935fa0435893825f -6139,0x2447ce55d230c01fc430725a8613d852289f9985 -5952,0x24a88a9a74e68fa13fad083c549a03ea1db93853 -5641,0x25659203022644c4766b2bdf60d276924bddb4a2 -5766,0x259913466c08d5e3824237002b6c3c95d926bc91 -5904,0x261c4500a87f0203ce5eb6706645f7f1bd614b5d -5673,0x266b5d4717661f4ef37af4379c066ab8e046bb22 -6058,0x2708e239a6205fb7b8f27b6f2919844d5481b6bb -5726,0x278b5a44397c9d8e52743fedec263c4760dc1a1a -5698,0x279c0d239d0c036e74aadf872d604af37e341373 -5703,0x27e9d8250d19ed277a2fb7cb3439841e6fb9f7ab -5590,0x2808e10705b58b11d05c447f5b2f93836a3b9997 -5773,0x2a928a7f0a54edfc158e3082b2b66d09d7211700 -6034,0x2b47c597b0a28372c13fa42dcc510e27a6d8ec04 -5652,0x2b89275efb9509c33d9ad92a4586bdf8c4d21505 -5912,0x2b91c14ce9aa828ed124d12541452a017d8a2148 -5883,0x2bc8ae379f7db9310e92daa074fcc7caf89c5c55 -5569,0x2c1e631adf3ea9111d126c06b6d4cf7d03124d60 -6103,0x2c59cef6fb58250aa649b91ecd1f401ada629370 -5964,0x2d385352674cbb8468723bc01efc00998c10fc96 -6117,0x2db7789c691e4d086ba167f34923bbbc9d844703 -5665,0x2e48d87d5d70ad31ca77edb2dadd748cf2c49f72 -5902,0x2f1b03447b23b3a78f5e708a052ac3498bb72c87 -5581,0x2f2103f22d790ac5fb1c1c071eafbde5ca85817b -6146,0x2f3c6a43f419e15b0ef5ef04decca2576025b303 -5649,0x2f71f4a2d8bab9703fff3ff5794762bf5b6c7e29 -5900,0x3030678f4f022ac162b4c70fc22aa4de56ed8486 -5636,0x3090455a284eb87a8dde46d3352be73c17deab0a -5606,0x3094b8e3148f580735786dfcc5ee26ead419e8d7 -6106,0x30efe43c4bb21b7f282218923504ddb4118f1815 -5666,0x3198ab211cdf3e4d13a698e1fb819507bca2e579 -5921,0x31a20e5b7b1b067705419d57ab4f72e81cc1f6bf -6087,0x32d7426f380100406c56b15d1f9c0b8581b2c20f -5852,0x33425d42926283bc19c273110284035980bddc01 -5898,0x335d3c3802ea2bba9db4968317dc8bc2d3139f4f -5862,0x339e4557cd9c6f7ea0536d80418e692b55b8a33d -5509,0x33bf0378242c7c814942197ea4ae584b52681197 -5622,0x34ed3d919ad7f533bfafe9295363d9f37c0c5658 -5779,0x34fb310296b441625469395ed5765296f6426636 -6085,0x356163fc5350a3b2440c73cd27b150e337e508e7 -6022,0x35aaecf71819f30d793b7dd39c20a96ec43cf39b -5620,0x35f4c54c1e36e6c931c116ddbe2fc62e95a8d3cd -6147,0x365d7334f2c079df86b5e6aaa80d5ce896f498c9 -5715,0x369fd489391115bea7082a524d13be09e6922fa5 -5936,0x3751774fc967973d69cdd020eb15a1cac23e3650 -5697,0x380aca4476cb3ee3912e2479384b34f3327f15a9 -5578,0x3877ce466be1618dc4060eb53b9989831539b2b1 -5740,0x3898b385a644af75b826338446369e4e7ff2f47d -5911,0x38bd9206b0867a5282eeed59e67a79e2f9831585 -5718,0x38f1487bd60142ae3633c082934455f06b613c6e -5544,0x38f84c15d4c3079b5f94177c97858ceff24647bf -5899,0x391a45f31c1837e3d837c23e05f42a098329d50d -5711,0x39ed4a44c5a390415f1b1dde2c77a61168aadfbf -6062,0x3a537cf3838fd0165f864dd8cca5c15deb266f04 -5560,0x3aa0814cd91f33467e6eb5ba58e56ab73699582b -5553,0x3ae1bf5f9481a42940feb4a1dfcc943128088280 -5973,0x3b05d4c58de4e96ea093032c6d70c7e4212a4c31 -5959,0x3bd77b8fe52242797c29df251418873ae34f0641 -5586,0x3d112016604ef2920bf411edf0b0212ef4a1cb6c -5848,0x3d63af087bf9ca6cd603689c4360d4765b490c0e -5602,0x3da91e5ed564ee46d4d734e1f99f395f0969101a -5534,0x3defaf45093c01a5b0a34587ea3726b911d523a8 -5799,0x3dfe6fab38d20d189cd7d28b04f8714c289de2ae -5815,0x3e238af2906a767fb4cfaeb666f9c341d5cd82b1 -5617,0x3e2c83f5131be75dce34b08a8168969061821997 -5997,0x3e5c7a991bb6e51938a5100b082d87dde8147cc4 -6037,0x3ec543c220443ad0d30f53fca52dbfa346763256 -6107,0x3ee33f28c8dcb83c7e6a55875f00a75c6ab67551 -5972,0x3fe7ace64a2d33f0c03749c20283201e3bd3bd70 -5879,0x3ff20410003767ede94c06c5df56968d90abae6c -6130,0x4063bfac9b1215c55452b961d428d98bce55cd49 -5701,0x408b71808c85b7603721e6a0d941237d70d1906f -5755,0x416c9c053bb551c20a0d794ec08e607e24dc70e1 -5928,0x41cccb4935386e0471922d460d9194b7db486d1f -5786,0x42792efed59f628495dd9f5e2190819468ce0934 -5985,0x42af0890d254387110555a46d261b9e4f6e327ac -5981,0x42b6e9b5bb96a02e02e651d3104fc3546e1313f2 -5684,0x42c6a55ab0d8b6caed2573db14c8799b45d21ebc -5821,0x43318de9e8f65b591598f17add87ae7247649c83 -6059,0x4393f1470317da64e277b29d96e5bf203f28efbe -5577,0x43d19841d818b2ccc63a8b44ce8c7def8616d98e -5670,0x44ab6573beef3483cde5b0bc1d25127ff89c6947 -5886,0x4538584c46badffe261e049fa25296cf35b26391 -5942,0x456b46311e59d6ab7569c94071f15fc76c850425 -5536,0x457c4fd48d73416c55018b340814f7d3e853b511 -6047,0x46b1ec25e5e7696ecbae9441d3f79d01b118e154 -5939,0x470a69afec851c1c4022b4a1b9d3a729389564ff -5976,0x4726b4c7b2d2a1efb07a459fcf5224d5f76c56f6 -5681,0x474499e5202d8cc7898358526c8d00b43333326d -6118,0x47691bca9f8f24baffd1115ffcdeb137badae1e5 -5855,0x4846c5513932923484cf249a88e2149e8171b84d -6005,0x48b30c659923a847627b43db51a4acd056ddcb5a -5761,0x48e2982016ecab46c8c2ce64fdb909931713e22f -5842,0x4986e171a7d66d5acfbf4299df7893a97636b02c -5827,0x49e458c7e1dbea6105fe907b0241fab764dfe340 -6003,0x4a395967687ff1dab35bd924a62bc1de4e54e793 -6032,0x4a78dfc97c59a11fa971407ca757e375508dd00f -6064,0x4b50cc59f8a59036c6b63924b60eaef09530590d -5844,0x4cd7c4be42266903299afc72284e273d0b49df48 -6156,0x4d040bbcb4a32cc1cc439b966f1330fcf0dc2238 -5990,0x4db7afb6b777fb47101c86bdeb56669ffa8cd656 -5605,0x4f1f0665ef05eb734d2a19ebf9948aabef898e7d -5727,0x4f2b4e7d0a61622d2bbaeeccd34888163e175647 -6072,0x50b532adaf5378f59dbd619dfd03e4d4ab3586bc -5934,0x516c96b8cbc19f70b3783ccdc74327b2d9f2d644 -5980,0x5179a757a3b29d2665a0638651f2d3c6c945d062 -6015,0x519579c64ca400c5c38750c21e6573df16d01dde -5989,0x51b774ff4b48e73ef8f9699da827ef9e33485abe -6090,0x51f1e255a9f82578bf2c004576c4bc84cb9dd8f7 -5582,0x53275529cf0ba240896f37eb2bee17c6c79faece -5642,0x5417c847b6ce4163c43116e8d9670395ba08b503 -5946,0x5442ff0573fd54a4f6c02a7a0d8e22bdc1a3c671 -5971,0x563312c2c4736efb40e86bb5e538269d6eb7c0dc -5744,0x565ce8cf5ae8e2adc6849eecc6c3ba07ba56c4a2 -5945,0x566d358f3386abac32cbe750489306c73caac9c2 -6109,0x569099940b5d659c6df24f8cadc410c5598bb141 -5653,0x56ce3546436ab45e7fd259841a1a7082a05e715c -5804,0x56d0a5098ad74f5e635797753644521173be89db -5897,0x56ef50083fa7aeea923feed7907f4714e4c7a374 -5947,0x5713ab44042d92c642444bd2f0fee9c2336f9e3b -5645,0x57ddc52fe5967b7a82e69aa9b8925b30f2531032 -5731,0x57faada9e5a3098a38151e4de8426135b0fa4fd0 -5575,0x5830e9e30b55cb5fd6102805fea2cf951f6afb49 -5694,0x5a1f813e4adaf68223eeac93966cfa03cf1267c5 -5754,0x5ae7454827d83526261f3871c1029792644ef1b1 -5872,0x5b10fea6c4f16d73ff447906604ed9f448498ac9 -6018,0x5b76e974935c846da918a97e5ffd33e9c9e384fe -6040,0x5c64a0be3cf1f5655d28c80353ee7f5bd8326a83 -6157,0x5c73aa8457861d07d60c16cfce20e884bb6030ff -5881,0x5c779cfae4b6b7a02c32938ddabdada8ab60fb14 -6108,0x5c936675212cb832aa82ba4b8a1f237cf8db97ad -5748,0x5ca2e53f00902dd64c7b8436a8f5d10807a84281 -6125,0x5ce95b8bd157f14b56871c8d69264d9b72f537e7 -5974,0x5d3174731f2a8c564fcfb2120709f69059642d4e -6129,0x5d68d6d5b94b05995e0c5e391362ce7a6b2e741f -5929,0x5e2b49c68f1fd68af1354c377eacec2f05632d3f -5978,0x5ebc0a9278a87caffc090f8e1e53f2fe60f76f9e -5558,0x5ed430c56e028e8b9b992fbb18dbcd58b5d31c2a -5601,0x5f8b6a881c7f97a73ba21a64f36cb4e5eca6bba6 -5878,0x6002a8f2a8dc0b704080765ce96aac63d5f7b224 -6143,0x606b55b96c833277e972aa18e4ef7d3f53bc6073 -5688,0x61823ee732849921ec34808563f6cc095f77ecea -5690,0x61c7f4643da0506ea9aa10f70eec0d6b63488820 -5513,0x627907c5cc27c755c0860450ece6b4480615a45f -5756,0x628225f89804022fb621aa5a6a75c67f3c0886ea -5720,0x63a6344e7dc0032900c261437838b0aa9e869c90 -5846,0x63d6dd43f6854a06548bc88e890c9afff5d29f4b -5516,0x63f072928cac9ffd799c6d0f985f9ee3985e9a84 -5674,0x643e4388fb918a7905441afe4b2906c059a1c83c -5838,0x648200a8d65e477b7222b89019bb877209d1b0cf -6101,0x64c9c8f5e06f6149302a1098030295f1e37bc8dc -5907,0x653c6adfb4a0865b78fec02fedeb5d02d8fd83eb -6073,0x65682e98fb7519220925b3f4f209b83957da0176 -5640,0x66269520acd32e54b18731b5806c9e1cfc64c99c -6026,0x664a9c4c58a8032e455feac8a1c7ba7d3c8c35b5 -5609,0x671f9654a594f8966b19c0b466f306e1dfe912a6 -5819,0x679c0174f6c288c4bcd5c95c9ec99d50357c59e7 -5910,0x67c5ca02b4570abd1e0632b2eece2fc0d9581072 -5965,0x6808a70a212cc2ca0838f5703a6c7bd080fa354f -6098,0x69941ca6528d05f2930d655fed81640c83d48b79 -5627,0x6a07e7b1479658b74261e8359f58c083f3fa667b -5750,0x6a282c7e0656c3e1dabb2fe0972e8ea2bd109fb3 -5710,0x6a3f7168b2e5e23992a89d7106b5076cfb52a405 -5864,0x6a4c622c3f3252393c8a819278b925ca58418215 -5801,0x6a63e89de430257f5a654f4bcdc8f902e2c5469c -6027,0x6a763c3d2f317d67727110c08a6cd61386ab16ac -5851,0x6ab00f0b713fd6288fc849b25e580ed2ad51d39d -5574,0x6acc550248f0ef1a99f2b39af530197fce7c3184 -5896,0x6b33b99e3d3213df963375e6d94aba968351f803 -6024,0x6b56a36f6dac65ec427a95eba6ff62b16f4791ba -5741,0x6b6309efe76f28331e640397be0c45141ae5e686 -5960,0x6b88627e43be7cbe48cf616c6576f5aa15423f35 -5831,0x6bfcac33502f030d6dcccfda5e810a236aa243f1 -5541,0x6c2e0015b431f8c5f6b40138af803504e601d91c -5924,0x6c36ced2298cc5a61257c85388851c91fc9de189 -5538,0x6c7fd4321183b542e81bcc7de4dfb88f9dbca29f -5743,0x6c96d32b94526918d3376868d287ff1b92ce07de -5514,0x6cd8019f6e61aeb99d9170a5fedb44b23305ecd8 -5557,0x6d3705368323900490193324277a221d8db62ba7 -5919,0x6d4393fad7a2928c10cdd0bcdd7702b39086d8b2 -5769,0x6d53c852fe7e0b60d54c70e454cbbccbce2c39c4 -6082,0x6f45357697de8ff54d914d47aa24a35222c1db30 -6150,0x6f5a76423396bf39f64f8c51c0b3deb24990b116 -5901,0x6fe6ec1c8d435b7b8596c983ee72358a2f5436fc -5591,0x70164a1ec9d21c4dba2b4f9b23750f9c7d03286b -5594,0x7039ccd356e0995bea3d748dd69677243f23b7f0 -5767,0x705d97a0eb98a1a611bebde00fd3dcf380ec04f5 -5998,0x717ee7991cfeeb79b1d8e7d1dbd982fb7982d0fd -5808,0x72376a5fc5758dd2b36c61960fd1936430a40a32 -5975,0x7291826de06edf5426e13f07fe4fe0df77ffaab0 -5664,0x72ca0765d4be0529377d656c9645600606214610 -6079,0x73043369f8efdd6e0fcb09c1201ee787583a7425 -5863,0x734c6c0fb8068afc4d795af32b65ba31bb38fdc2 -5828,0x73aae33a9ec285d542fdc1ca7e293eadad5247c1 -5745,0x741ad22a9444d1b5e6606a3587effa5b8995321c -5643,0x750dcadca8e9e7f9702c85383119ea8034450993 -5542,0x752e92343fc2bba40439126ab870fadbcf17050c -5742,0x7550ac81ab981bc67b740af457e090e0f4cd092e -5816,0x757bdc3043dc21652169bb1d0ffc51516caefdb8 -5940,0x760e1556fe5ab98637f7fd3f8e550e18e4790e74 -5585,0x76711e9f14254071ac8767ae675fecf793c22beb -5551,0x7801efa8e30606d2d4c9848852b56ff03efdad5c -6042,0x78e9354077a65672eb2bcb546c8a5ee749f83806 -5961,0x793fe9d56773158b3666ca7a9b6c95044c956275 -5656,0x79599f6dd939108f3872cfa67456fcddc66e48f8 -5512,0x79a1219209e91bd7e46e9ae89377adc34c05f18d -6095,0x79b7bad941e2cfb11181173906deeb07733354d1 -6017,0x79ee39abf33e6f14f137033469d733533b5662e0 -5890,0x7a07c26166dc04f1319e58d630769585ada2cad8 -5716,0x7a911614e0a916fb59a14ebd5c186dd076d1edbc -5629,0x7ad35a998847fecf33fb1f23c27d3159a9c59d3c -5768,0x7b3241c61889185188e3b444abef7298fae6b1e1 -5941,0x7b603f09c9b93dbaa2cf56f233269b6751d74cff -6149,0x7b7aa6f42d20a5c0df31901e1e1af5f4f968eddf -5679,0x7b9a3a5599b26a3243b50cf6e1506f7be180ac3c -5603,0x7bb0a8506c763f24e3b5b6cbeae7e56e5787d754 -6123,0x7c3f725291010669ef77a404f7d83e10a480e0e0 -5614,0x7cafbe06cbb6d7f7dafaf3c62d200e9bd6a1f38e -5865,0x7d1dbb0eede15baaaf8fa6d6d9025b0390263e35 -5847,0x7dfda202789a8d35539260fb22b711b55a335674 -5817,0x7e7184bc0b0156cbf8347a03cefce3b2c710328a -5733,0x7edadd097402aab262b7886640bb020ab0afdbc6 -6014,0x7f804722948571f575d06a52c7a414ae42f6f89a -5758,0x7f9b2539e0793d31eb3c32305298f113f08d0089 -5806,0x7fb934d61c0e732c47a50fabc2def884cc1545e6 -6074,0x7fe0e7f9b8ad85c0f2dfe8073ebe5f41d67a7b9e -5545,0x803201e28df62c25c0a463827132a2baada0fe79 -6091,0x80559f187ab7b373c39a04df434f33d5462ac1bb -5783,0x80ae4cf36564ac107744946265a331b7b3321828 -5604,0x81423f567904b940dc3c37c32ea4c4b5da6db8ca -5522,0x8213c4052a5ac643e7e88b1633c1eaa332dcdab3 -5739,0x827ac1b47e1b0ff773e9476527261f6dc0e840d5 -6035,0x8285047f33c26c1bf5b387f2b07f21a2af29ace2 -5705,0x83a6a83eed4c97b2b4b60a02df1ac27c0db5a175 -5963,0x8415e409a5566a1333815c2e7735cd71b3db229d -5647,0x8481fb8f367941774435bb4922972c0369c8908c -5791,0x84e964be3b910e8a8633856b69ebbddc7ba55006 -5576,0x84f6cdb2b94a0a943bfc66bac5861b141e5605be -5565,0x85870ab75945964b1f2c50307d71fcd6f5d9730a -6138,0x85aac38db1234fba6e06d86e10cb1289d7d79657 -5658,0x86089daab2444560fd774307b5d0d854d7e9279d -5795,0x8612704049460f7a999fe06d6d11830a4baf6cec -6075,0x87de148465727b34cd2a43773738390812b12a56 -6054,0x88faa35a9f4f4fbcfded8d4a42048c20628d6884 -5807,0x890b559e10ad1aae73713f2f1924aab4e60cf505 -5870,0x8947da500eb47f82df21143d0c01a29862a8c3c5 -6006,0x897563772ce3ac574412f18ec04581ac377c814a -5923,0x898392a5bd0029fcd55492c7cbd7442bafcfb71c -5630,0x89df505c693ac7e07d8d5d6308fe0bd041033cdb -6050,0x8ad0b66851bd6d50d82f623472a0bf6e402381e8 -6033,0x8b2e97c18d8ebe2cadb173db3827376633bdead7 -6071,0x8c9f8b38cdcf1911cb470326a435c4c1b192a80d -5728,0x8d47b12ce25e2dc6866e8aac0dbd823da09fadd6 -6100,0x8e258beef8e860b21afbdb649b1ff26f7b149677 -6044,0x8ee4f3705a530aa60749f2e9cef46d73aa1b376c -5810,0x8ef3a39a2d1362fcf62213012618c4d463b2a0cc -5563,0x8f7493e91e03dbe921baeb15c9f6adf4f8830479 -5736,0x8f8cc357aaa401cb6f7e01d56c59aa8cb2132018 -6001,0x8f98690b1ddb25e43a50ff878fbd13e439ca52b2 -6020,0x8fe6fdc03eba902a2c259faf53ece2ad7a5c4d78 -5714,0x8fe726b27e89cd484981b2d27160186920af2d17 -5579,0x9043cf6b9319b07f52d9c8d8964a8b457a64e0ca -5850,0x91001550d523216e91e9e099234b4d48ab77bb7e -5931,0x912f889034ddd593a087c4e59061e549891e4d4e -6094,0x91c5a519023620066b5fec2f28e4d899ded79465 -6002,0x9227334352a890e51e980beb7a56bbdd01499b54 -5951,0x92b8d6c884d9268437e7b4e7bf96e78614965942 -5861,0x92fa0dabe2f6d8b5b64e9d0cc869bc737c15bf9e -6061,0x9350b6808eb0727929031a79f516094d66e4ad18 -6131,0x93714e258b1f5e987ef1ff59ea66d51964952f62 -5824,0x93b192fb45937a2774bd66b861a2694902d1d465 -5618,0x9434bdb4f2d0233904e0301f8bf09ceaf9c72bcc -6112,0x947c3b2919276dd0baa8da86b3534b7afdfe2e59 -6110,0x947e4612bfe3c56eedc15b829b0d1a1c019be420 -5634,0x94ad305456516e5b621f295fda2c8b55250e71c7 -5908,0x951159a8c1bc97f70d200ff39fa026ce31134783 -5858,0x9530c16d2a169daa38ee73748661999f2b2f0c1d -5876,0x95486a41dccff81874c2f61cddda9ec8ee1bcb34 -5562,0x955f1032678be2d76d9eb4d1a73931d6cb59114d -5592,0x95820b7d30904bea4419c0cabd9ac93b35d488e9 -6155,0x95a38b4ca5c9187ecfbdfb501ef61d46755bc075 -5803,0x962654fe9534c616fe1f3bd7c12065d54551bcb8 -5944,0x9656ce991ed988e83b8c67fbca91f1618bb09e54 -5988,0x966e38c5c3143ec903efe4e3ca74c23306f17035 -5913,0x9712ef637f3834f1fb884908ee105d5170813646 -5970,0x975c85207239cd7574019bf098b8bef49ac0c9e0 -5706,0x99a63e20e6f78dbbbd3a07d9f9330a860ef2b4d9 -5969,0x99bf9d0d03689ac2a8c20c17fd757c5763abc5d9 -5781,0x99fb60a3281467b138ad4d3a7ba575cd9f30a17f -5552,0x9a2017cbf88b994dd7d3894d4652f0ef5f557973 -5533,0x9b91d27568c51cef3c4e04d504ef3ce11d5f2427 -5833,0x9bb3fd74634ecd2db310fbaeb0be1891720e644f -5950,0x9c7cc98d31713e43b46cfa10b3b5c3ef66bc043d -6093,0x9d3abeaf22ddf68e72b865ca3b23ed880a3be41f -5625,0x9dc42a2535b7161222862daeb4a301361098d800 -5520,0x9e0b492f029f969affbfd78ca17302c2da2604e6 -5535,0x9fb1ef2b3cf9b62955ad18fc5e03e47fdce15a4a -5787,0xa01ad492b19314befbce4891b927a030d660ffa5 -5873,0xa1086d77493a1b316c927e7ed3ab2f06ee6b6c9a -5785,0xa143e04a0090cc55c13a8280d52192a2d008dd6f -5917,0xa20ed099894dcfc46af290705ff5ae4d318b0235 -5784,0xa234095bc421b97c1fe104331edc5299a60485c9 -5738,0xa25816b9605009aa446d4d597f0aa46fd828f056 -5765,0xa2f24cc5ee1fd06ae3f8708f37ba064f7eebfa98 -5826,0xa35e6a72670950335ad701ed9f4ba65b5fa99e9c -5812,0xa3c2b9ade3c2345700c0fb878cb91a5a18293038 -6021,0xa3e242c97a213cace27946123731fd63a06db5c9 -5822,0xa484e256d614cdeb221f3e7445a7a955662eb404 -6051,0xa4e42fa4ccef534516c2d9ea387285163d6dd747 -5916,0xa4f794b346c2984175fba5f410a6c6fa7276926e -5871,0xa59727442212227bbdcfec1e883677a930707412 -5510,0xa61198118d595ef9762bf32ef4fd7ec005e46d75 -6148,0xa68034fc725fa0e6ae2e5260c18efb78f27a96fc -5915,0xa7fb4a668baa9d287f7ce0454366dadf87018ac8 -5856,0xa84062253e6fbc0c03ab100a7953ebe88be86415 -5611,0xa8a415def7aba752726bf8fb1c980b4ce52cf582 -5519,0xa8fbb95cac84b94a54a2bd41ca130cb46dd24349 -5935,0xa9218c6d51b87eb3ee74e14cb5d50bde00be7021 -5518,0xa970c125c2f26255f3adf053e735728269f2817f -5593,0xa975550c2064b0b2d7f5e5e0175e5e9dcdfa2ae1 -5837,0xaa7ad7b27fc419d00b4bef275e7f6020953a824f -5979,0xaaf0cae140a7deaf14c97f5c410201fbe30e33f5 -5887,0xab4b1cb0e9ccdab5c30ca6c54230e2ed8de2a840 -5829,0xab599b2697d836e665706dc59566363e2fe0995a -6081,0xab8f1ff41e58bc5a4587161a9b95f71fa39e1553 -5537,0xab9b794b292e60055129e006766167630bc65368 -5835,0xabbfac523537082f1c55097acfee8c07c8958885 -6008,0xabde2d8663f0b99ab3fe31ef1af1d32aadd39996 -5638,0xac1d6991441d2113ac812bea5da4b20af01274fd -5598,0xac47c388d52c443fd854848ef1fa57171679e7b1 -6135,0xac724bec64ad7591f2e6ddb5fa57f4eadb78ee6c -5798,0xaca11c093bf6d22528bfca298ae5aecfef68f9ba -5860,0xae19f47b8bdfd0c667796682445c0ba8d549e41e -5967,0xae56177e405929c95e5d4b04c0c87e428cb6432b -5933,0xaea1be15adb75a49aceef392a6d37625d993952a -5732,0xaeee46e31e5c34eafbc5ce31bedd3453a181b46a -6141,0xaef7a2c9c95a0d11fd35a73f0c58770860e8a7c9 -5894,0xaf88719228ab83a1260f3a0e021a80f3db150b67 -5776,0xafd4dfe320883cfd9f6127d30f6d7bece90a285e -5830,0xb019cb0c09165a96f221427ad027b8946199d976 -5954,0xb089a5b34f061ff658c783afe948abbb1ead9886 -5948,0xb0cbc48969e48993456493a04f8dfa4996da10f3 -5589,0xb0dd7b602656c315f970616e72f28d05a5d8f29c -5793,0xb1414f8b3ab67aa4e18d36ef9e342fcd2d5432aa -6126,0xb17e469585ba51fc7973659dd8d96e516ea4f4a3 -5669,0xb193e73b9b4ad9ab815b4add8e435ec258c684e1 -5958,0xb23ea6561953b406f4806cd177377d6940420608 -5820,0xb2d2c60e088cb2a531d8e93efab53455a7ad82ac -5702,0xb30e9725fcf5e48886655b962defa9d9e0de1ade -5676,0xb32fe0d20f995fcd540f19326a258679bcd0b9e5 -6011,0xb37da39a77e334befb9b66bbe5d13f62c58195b5 -5540,0xb3a103e08a9dadf3ccd06824a65be86b33cdb138 -5707,0xb3bc321759ec7203d18f179142b9548a2c2e4007 -5932,0xb3fc7dfcf8a93d7f7482d8cad4ab13a57d221f6a -5943,0xb47fb03ffacefa464aa0b1e575ddd42760a2d6fe -5686,0xb484027cb0c538538bad2be492714154f9196f93 -5909,0xb4a2132a916e34168a06fe34f269ad941f2598f8 -5734,0xb5137d1a5b2a02c93c4bf64794caabcc1264a499 -5543,0xb59e9915d505e43f1231ce8f8258b8c6168ab891 -5607,0xb5bec32954a3a75fce4b5b0b38bc6e7ccad4c4ad -6041,0xb6bec915bd5188acd8b30da8d9ecb2ba83b9f86e -5722,0xb7cfa3affd1313410ffc3a891af5675d658e526f -5920,0xb8109ac56ee572990e6d2c6b4648042bb1c33317 -5608,0xb85ffccf08ccc82d0a3101d78822f293aab44adf -5949,0xb897c81fa890ae81d3b9665c76fe610b696330b8 -5668,0xba6f3af1836b2e9eb347dca0490c62cdcbbfb2bf -6134,0xbaac5464bf6e767c9af0e8d4677c01be2065fd5f -5796,0xbac6c6e28bd000a72fa18735cc24ceee49f51069 -6004,0xbbc6bf169235ad9ca1b7d0f6a4593bb770e8c6cd -6077,0xbc4bbd7adb2f1dd79e40dd4029bcff97be9bb1f7 -5789,0xbc4de74ced32de8d5aeab3bcd2f47de58070f0cc -5968,0xbd2e1e91d41d5083fb460148a2e37a340a356213 -5639,0xbd39d496eeba3842521e8886a04312795c2bc799 -5730,0xbdce653912f089371b9dcb83a1b6b93d6e991418 -5573,0xbe086e0a2c588ad64c8530048ce4356190d6a6f3 -5687,0xbea0a27ec250dd09a06a787caa8541bfc1440ce9 -5524,0xbf40da2e303c4a7cdb1a7b4d444b10b8e9b91cd9 -5549,0xbf7f22f78d25fc1dc77f18a93e8e6e153487f6ac -5612,0xbf8523c0e36012291c32bfbd266ea497db705ac2 -5662,0xc0201ee722280f3f86a20c0aa9fba2ef6fcfd26d -5986,0xc07520843239c2caa28101d8241d9132669b4271 -5723,0xc1274f9b143a8a33843446d39d97b431b3e74163 -5800,0xc13025ea8a2a402112fca956b248823c30720ec0 -5893,0xc1a135343c8e90998bdef974eeab18f135cb0cad -5700,0xc1f313e9748d40250a25bf8f8e1056df929cc62a -6023,0xc20b7a0b0fcd54e40d65646c3e149c0445ed86b0 -5903,0xc33cab50ee69bbc6106462b873a19c132eb44cc4 -5507,0xc3650f57dd14ac5e5ca86b350ea7c73dee36ecd3 -5725,0xc392133eea695603b51a5d5de73655d571c2ce51 -5889,0xc44dfc6ffaf195e2535fc13d75a79d9238459782 -5659,0xc45eb8d37917ab65d06806f35cc54e09dea135ac -5529,0xc647ab2cab7d6da6081703382ecf2814d8b7c03c -5984,0xc6bd4862d9b41030ab803dabdc7b2b25ef3379d1 -6076,0xc7428e4da6a6ff3571c3a9f88818622ac4950aa7 -5717,0xc79339a3f3b36c57c96e457a2b44728f221c7d44 -6111,0xc7d8c34048d8bf6eb24fc76c7a61f47754990c81 -6030,0xc8db2b4d011f5b9c53a5807103f6f229b21a407b -6078,0xc9183e3306a06e64c2a83e655756f70a83152536 -5635,0xc922f4cde42dd658a7d3ea852caf7eae47f6cecd -5760,0xc9fd24b3672dee34bf7e4ab9c4c9b12779fd3dcb -5508,0xca14ec99ec5e2b3fcefc2f6256cbbf9c2f644b55 -5626,0xcaa383b59ec3d8f1fa8da2afbb8b80bf959eb7b6 -6060,0xcafb9f5e2d2960387dcd016d32b89441e444d166 -6055,0xcb933e7adb71c191e0022035a8f48248747d0113 -6070,0xcc134245424fe9e10a011961451d5ad6cd0c087c -5515,0xcc36fde645729a52a92f08a2f1aecebc3326f790 -6039,0xccf4fdc780401010aa7572102bf82a769b741a19 -6096,0xcd9c0e99396627c7746b4363b880939ac2828d3e -5888,0xcdaa51fa12ac485153f5b74f7ba8ed12b673ebfc -6065,0xcde1c5d8998165fb417f58b1f258cb0dec38ba00 -6151,0xce674ba3bb5128a43717282e54831d769d5baf69 -5914,0xce8895d6a19f34ae1f6ebb2310b4ce355dcc8985 -5930,0xcf356646e70db0aaf3d7a3f9d4f412695e6d615c -5794,0xd0418b847255b9e55e1f1d5a52241ada23e5197c -5570,0xd051b22871f23d12eedf54d695420f43c9d3c268 -5774,0xd13160ead61030f09837b3e429cf2f1a36193dd3 -5682,0xd1fe5a0256acaea497ecd30dc0b816cce95e23e9 -6152,0xd2103833656218c6fb0f5742b48678ab217f4f1f -5631,0xd24f796c1ee04a2e68fb7e71c5bb8259fce2745d -5595,0xd2bc26305b8aa6ef4b9c5954bcbd49975ad05689 -5994,0xd34294db117aeaacd11649b754eb3a89469975fb -5906,0xd41469706802dbbcf78e985c50092bbbde9431fb -5805,0xd458ab8971cca2d51515cba564e9498cf81cc36d -5751,0xd47285b394960a5552631af2aec25558a6a1b823 -5811,0xd47e4307959f2e7111827b0b83a4a2f5a2b0005a -5644,0xd4c0f91efa1ff4cc32f86693f166125818d27277 -5693,0xd517215990cbf485e7a3f0158102e4868db61cdb -5708,0xd550bd32b434ba870642f94710eb220f542ed3f3 -5530,0xd59cad563741a26283d96f89e9fca6be853c99ad -5527,0xd65604822e0a2c75ab03a07dca11c560682c796e -5777,0xd671dd46ef7c5b2fb6bf3164b444aac69843a91e -5613,0xd6a960921cbb5462022ceadc4ad158e05cbfd168 -6048,0xd6bed7d753dcbbbb8937dd009788d6c815ae3094 -5759,0xd77cc57ca411c67f00a8df9e334fd0d79e9bfcc9 -5962,0xd8bc9d6840c701bfad5e7cf98cadc2ee637c0701 -5628,0xd927170e3669cd14fa07937b778b1649257f8ee6 -5749,0xda10fed3bf26bdc131f408ec224c38816c2b15cb -5699,0xda26b8eb86ddf2028533f82a6f5451a232373f8a -5996,0xda5f4a6dec7afbe179cb797ae015912dfadd4d5f -5531,0xda80876ccf328c1cf4722d6451dde9ae8b24d127 -5999,0xdaf1a86b6e4467a8c4412372b5c78ba5bf9c241c -5747,0xdb9233f7b5378946407e50b5a53f87a223dadf95 -6132,0xdc18854698c80f2d3b9251c01eb910690ca9d512 -5724,0xdcd5c5dd3b108fabd8a61a6e0549ffefa99d166c -6083,0xdcfb00385846d8658e734c73b01f5599ade90577 -6019,0xdde32b09da4941a4762100a4eb2ae3a98cc70264 -5859,0xde4f5406675a3ad513528f93a8f17c98ce98451f -6036,0xdf08cede8eabf56233df21d454874ddf452fb54b -5650,0xdf0a5bf80e8009f2702e7d164a7bdeaa79013eba -6099,0xdf25ce158f7d3e5bb24cb8bed6dd1e043d48c5d6 -5857,0xdf3eefc2ed0f31947a67f0e817dfd92717630e38 -5892,0xdf593a822b0ba768816ba5a3d06fa3e1a8f7000e -5680,0xdf9dd5c11c87bb5e1cb90ee07c3037981e2aa1e9 -6045,0xe070a4d5a17d458a237d20f23b49737da308561c -5780,0xe0fa68b4d3fca2518ea923d65507a11f20135356 -5554,0xe1095dceb4e4184bfb6ade590998fae94b815f2c -5753,0xe10a2743a1ce709693d3ad5f06ea47df3e0b3ec9 -5521,0xe1fd1c3d915f500a65d13ee462c78bde4db0ff92 -5825,0xe247ac7de11c1ac0e075666d032b003a35cb75e6 -6089,0xe2881cad27db4c27fb3814ad97cca694b80c0fa0 -5995,0xe2886fe51f2afb6e226218fe99c732907e3d1c74 -6007,0xe2889edd73c84fc116185638f88eed328bbdd865 -5683,0xe2de80837f8d9b59ba4f23d3dea02d1d842131c0 -5691,0xe2f77e6154a644a70035b693dacd6580e3378332 -6049,0xe412f48849412eb71e6572b5d73e30954778c0e9 -5704,0xe41cd3a25cbdeda0bc46d48c380393d953bd2034 -5661,0xe431892c52960d04e5827375c4fac96631c6c6a6 -6063,0xe4d249b3dc1ebb2181d7aa0a9d6359b01394f00c -5709,0xe5109cc91f2c236fbee3752af06dc92d4bdddc84 -5584,0xe6947368ddacde47cc7fe64303125ab9d8421530 -5566,0xe6dc152e7b0c7614f589fa1f54d149cd3cdccc11 -5788,0xe6dd36180cb089810d5fe11bb766cbbc51d7d23b -5772,0xe7f379dcad66a5128f44a996d276cf55e6d8f2c5 -6121,0xe840e8e7bef7783f8f51908ef9e77044c1d4c58c -5528,0xe853207c30f3c32eda9aeffddc67357d5332978c -5648,0xe8e022405505a9f2b0b7452c844f1e64423849fc -5719,0xe8e8093d44f84be11974e3183d219e94fc9c36d7 -5660,0xea3b0afdaf4d5b4e64e51c2af43860831105372e -5770,0xea71aacad9c7105859581aa7048126d6f98b391f -6038,0xeb0f592b88f1a3b63d81b6324a9490e8bfe62070 -5993,0xeb4c074daf45800aea4573ce19dfac8cf2ec8bf9 -6092,0xebd11dc0ad74a44ee1fdb3e41c8db0c2ae032b3f -5546,0xec1101a160d5b29351b0dee11c823b16ea81e7c6 -6136,0xecbad7fc6ef238d03c6d6787480dad1d7a560a9b -5712,0xece0eefff3003d23e366c917104c6cf5cdb404f1 -5884,0xed2917eb2e4ac54321272d5e27ad7b22c5532ab0 -5849,0xed5c389ad19c5a839ef93233dbff6e969ac9ecb5 -5957,0xed8519750eb90cbe9ba8573324a5c4102b97a85e -6097,0xee3e5aa9f33b475a0743a2896a3264777e1e0fbf -5713,0xee97fc872e94de22c92b1eae04f836340068eeeb -5580,0xeed063903f9f0ddc044507bdab5d59d91d8fbac4 -6013,0xeeeac1cfbeecea670bc42e76163e10edf125c458 -5790,0xef60ac2186827ff57b3561571cb3cf8dbd0ac9e1 -5775,0xef9865a2d2dc2322d8b4e6ac84b13d4121ebff4a -5532,0xef9e3a581a2d7916b05d24c2ef54a505b3219e72 -6080,0xefe06b9de2c94f172791c51ef7fc88fa75c64d26 -5619,0xf078aec9c73de6477ec8527d7a67ee1decb2c1b6 -6102,0xf089f0182d994686c865cff4fa99a0fe55465d30 -6142,0xf0a7d192ab5273b07c9cf5b4fb11c96a20a64b73 -5866,0xf0c573f825b9efe4da8ddd161ff1ebb756233eca -5809,0xf0d52e9cbe8d091c5d11f8e5d56ef7ce8c0d93c6 -6010,0xf0ff8a5d04fa5189dc51604b51d76954ab1c7467 -5843,0xf10424c47f3105757d3155879d5060b50f82a8e8 -5572,0xf1299a4b78799c5ff9bd19f0e96c070f4e0d96de -6043,0xf1b35fac306cd6fb0008c229f056314c139a3c66 -6053,0xf1e574df617174e3911e8fe43587c7bb24882589 -5813,0xf1f38617f59eb073e8b0879eca526762d9f34830 -5839,0xf238b93e4593cdc181bc56bcc776f9d8c346cbc4 -6052,0xf270bba56b1a8ca722acbd6e6fb255a51dc065a7 -5905,0xf2719ae0a51bbd6083b8c7ba28c7e963e22521d1 -5571,0xf29ba1a2f947d3c416382dd853a1831e0d31de9a -5616,0xf307efbc0dcdd1fb9c97e73a51bc7bd642efdd34 -5782,0xf329b8748216b9f9e39c98e1b60e6e5da6b91731 -5615,0xf3510f0a565fc33f956afa84dfba745434cd6641 -5868,0xf3b63b29a2813959a4273e920c6c5df06bfc369a -6114,0xf4507b4e2ff47c8008072b232ce332e89a167f4d -5802,0xf47c8f70a2251798162caf7cf9014152b5ee3761 -5561,0xf4aef21d906992afadde7a9676e1db4feb6390dd -5623,0xf534848023ba851c6265e8f99a919c4d5fd05cd0 -5853,0xf6a6e6f90b4864d0ce86c03230db723077039aa8 -5692,0xf6f31f00876fade235fc01392e41258420b8fd7d -5671,0xf74c347f3b0bba9000c1882ff91139376f0d8cbc -5854,0xf775e40f6f331f6c2efeac5c3133a685856e9060 -5632,0xf7bd3887fe25d2d4108a9c92a173e126b9d71e85 -5526,0xf80350859238421479f451fe55eba610153af57b -6069,0xf958a078c85069e2dced28ff27bd80db8f1ac945 -5983,0xf9dcbe0732fb68027333fae86a88ac602e239deb -5752,0xf9ea58018436e01aab73bd49084d9d2558905d4a -5746,0xfa2a9d305f2009f2a7a2a95ba501b51f9a06b815 -6127,0xfa4eb2b83345c80b22ba6f15de2e474e3c03e6ac -5953,0xfae0177f052c9f020e6e984accf98241ae35facd -6012,0xfb1d1af9d849ce7fa506ae30857e5fb850e9a1f9 -5677,0xfb6527ba5700b1d8ed135469b15bf8264aeb8335 -5655,0xfba2474a02265c0f0a7fd0e9f75dec5863b4897a -5771,0xfbffebfa2bf2cf84fdcf77917b358fc59ff5771e -5762,0xfc050b42a88c046d54b7a4e3da42d8c9c38db266 -6105,0xfd0740a2041edc475c3fa224875bad9916c73ca9 -5597,0xfd4fd1aa23325592cea9b9d83d1ae64fbc56611b -5792,0xfd99fcb3e53338c6e6f9f0855c24b39df4b1536a -5841,0xfdbf68a25eee0a1619e2b8d255a1cee9369717c2 -6016,0xfe0796f85ea9348748ec01d60285113d12c39819 -5654,0xfe09453b10d10953c482f6cc6ab367f57ce195fd -5797,0xfe323c6a4ec6e12bf55552fd939768e4cba7db60 -5646,0xfeb75318ea0a838c0c8b694e84602cfdc5b8030c -5567,0xfed78b2d1daeb1b82c528293c73601dd8436b383 -5564,0xff8a28abc89f6788a1786c8455a0beed6e08a1fd -6029,0xffa5162aa8e835df11d999287068ab25f22a734a -6025,0xffbd1c988b2cad06b80dcc07ff9187963a01149d -6153,0xffbfb9694c960c5d04f95dc907bb8a58fae0f969 -5523,0xffc72be6daf9072664d4bf9b8dd90562ce20aca5 -6228,0x0348e04b4b7c3133bdbb6415493b41692459b13c -6224,0x0a3b938d51f1b6d7bf960a0cb6ac9f1154d0008c -6236,0x15ff312a9930a6499e4d747773a098b013e05a66 -6244,0x16a0c272fe8af1b4768e288467f9ac771b1c2b0d -6234,0x1d90fdac4dd30c3ba38d53f52a884f6e75d0989e -6241,0x1f514a61bcde34f94bc39731235690ab9da737f7 -6235,0x2217aec3440e8fd6d49a118b1502e539f88dba55 -6249,0x232e84eaa8938676289d9ee651e80ed369609a66 -6229,0x24005ec2445103050d2d72f691de287d9e4672a4 -6248,0x36df0a76a124d8b2205fa11766ec2eff8ce38a35 -6225,0x3b47f0e57a6c537ecc198833728ccd5d02e29771 -6233,0x46fcde1b89d61f5cbfaab05c2914c367f8301f30 -6240,0x49df1fe24caf1a7dcbb2e2b1793b93b04edb62bf -6220,0x5a8931f2b235caa2eabf3f07cd1154360c933e17 -6218,0x5f1f8cc5023a0ec2f53a73afa8829d3680cd719c -6239,0x6108feaa628155b073150f408d0b390ec3121834 -6245,0x6237b5fe025ab6477f19fbe5e90515b4d77357ad -6232,0x6e4c88ca7b80dd8fcd629eb720c3ea33ecc08d9d -6222,0x82b3413d575aa93806308a04b53c78ae2037da11 -6219,0x875f4ed8cb5f30c87c708d04d4fb9dc560738bb6 -6221,0x88c81e6b9f14580ece9729ec699dd800c206172e -6252,0x8a3b1b9f4e3fabb80623c215aa88f97bcaf53d8c -6243,0x8a5226cc1ed9645bb2f90a9ef4cf889fe8e97615 -6255,0x8b2e286afa241307261622abd2878ad8ec9f0723 -6237,0x9761d46ef36e07131e8c56af06e35cac23b9a91e -6253,0x981bd9f77c8aafc14ebc86769503f86a3cc29af5 -6247,0x9d1a282ff8429b8143ad33a21a324382407cb88f -6250,0xa516b9c7378799799e6dfadbdabf45d5b584405f -6223,0xba47316035e6c95b31cb55bfb93458ad41e4da04 -6230,0xc20099a3f0728634c1136489074508be7b406d3a -6231,0xc5a39712229a109fee54fd2d1cb78f9a3151382a -6251,0xcaefa712bc750e820329815bbdcce641282bbc57 -6254,0xd355c29bb622bd4f58dba495dc8921985986efbe -6226,0xd4a6a05081fd270dc111332845a778a49fe01741 -6242,0xd7cabef2c1fd77a31c5ba97c724b82d3e25fc83c -6238,0xd7cb8493d47e737009fd13d275469b93864fc5a4 -6227,0xe0374614ca02012963f22807d374a39c49dea803 -6246,0xe092a4054832a6f1bcc12188ebde7d6a362894aa -6956,0x0007f3875a53e285ad2a8e4bbbb8d98394216f85 -6770,0x001b7876f567f0b3a639332ed1e363839c6d85e2 -6952,0x002089829127a5769db0d53372d5d53a4d1a87a2 -7329,0x0060b9f4eaa97b310bf74947edcdfe2967e54d7d -6691,0x00751b4ccef0800bb4fa33b685363d4cff1a981e -6740,0x00b8d5a5e1ac97cb4341c4bc4367443c8776e8d9 -6611,0x00fc152c7dc2df709161dcc8ba541eb77e612d4e -6717,0x010a2f40b3b09fdbc60cb06f1b7f1a2a757d0ef2 -6890,0x01639200a40123af589ba03d154d167da5a7ab51 -7617,0x0194e4bbd724633546d543a28125c579dfec057c -7614,0x01a43786c2279dc417e7901d45b917afa51ceb9a -7321,0x01c916ca09fcc66a017c67f0ef6b337efbb29398 -6854,0x01da457aa57abc0dba3fc26d6c350899f04e8417 -6507,0x028cc633432a98b42e05a1175ae9c19efbb88237 -7719,0x02a26df328e08c12ce3a5ed428b83dc5e4c2ee67 -6881,0x02e85b8dd638a5fabd9d67802a92721d2e18fc6d -6741,0x031a448f59111000b96f016c37e9c71e57845096 -6769,0x0329ed0ce632ea6f675119c03aebf7347193d466 -7017,0x0333bd82e1f5ff89c19ec44ab5302a0041b33139 -6463,0x0358a837b925adc5143477fa40eeb7c115dce672 -6790,0x038dc05d68ed32f23e6856c0d44b0696b325bfc8 -6600,0x03ba372db86536a8360c7ae9921b3ab0420336c6 -7689,0x03c0544195a86028abdd8e69239a03b7ca0bc283 -7021,0x03f836ad0d3d90485ae4071be69fcf9e8cdec865 -6944,0x04194cd5916c77fe82dcde7b3db051d84b2d33a1 -7481,0x04412b2ae241c602be87bc1114238d50d08398fb -6713,0x0454e103a712b257819efbbb797eae80918dd2ff -7692,0x04631b9297919e98334c5abaa762c48af071b4ef -7200,0x047b3714f3b3adfc1aa1f31c710ca3ca967aeaa3 -7524,0x04915c122eba1cd7629a2a00d9ae2a4c145ab21b -7083,0x04b50a5992ea2281e14d43494d656698ea9c24dd -7127,0x04c10988e88bf0b52f4ebc8d7c418573de3cf93d -7251,0x059681217e9186e007864aa16893b65a0589718b -7419,0x05e1b1dff853b1d67828aa5e8cb37cc25aa050ee -6843,0x064f4eb632670aa373561a4b67234da7cebb4047 -6877,0x067389a8384be70d5f094be6e60935cfe33e8e28 -7331,0x0679e0fa9ecd77e2bd900555cbe10a5dd519a5fd -7401,0x0681883084b5de1564fe2706c87affd77f1677d5 -7710,0x06ba4e6246a0c4bcbb53ec860be5372fc40629ab -6848,0x06d988427d44007edcf1d4b705461c5383632169 -7630,0x070e7b0447e096704c54923826ac0cb6c6472400 -6772,0x074b8f19fc91d6b2eb51143e1f186ca0ddb88042 -7039,0x075716638434d5fccf7601b7de48e398def92960 -6566,0x0782f4900dcc9e97dd34372e3e6cd3150eac767b -7364,0x07a82aee97f1011256a9db69e2d4cec2ef2d84ec -6502,0x07e88bd412e2571e6257b5ae2fec8be3683ffa59 -6595,0x07ff3d9e1bc84a5ee39aeb7d7b09f5855aae191d -7751,0x08388dc122a956887c2f736aaec4a0ce6f0536ce -6564,0x0868d8421b6c4f13b392ea4b72f8012884c45b74 -6567,0x08941749026ff010c22e8b9d93a76eebfc61c13b -6797,0x089af032687993426a628184cb3d0610d2cda6f2 -7742,0x08bcea94194a1d63379123073cb254b77f7721a5 -6850,0x08c2c8d7175c5766a8afcd1f7b69a6d999e21824 -7711,0x08e52251d4434eadf1f2ce01d419b6249694d9e5 -7064,0x093583d2135af25ab49a4e148698ee0125bcf2f8 -6652,0x0940b0a96c5e1ba33aee331a9f950bb2a6f2fb25 -7300,0x0965efeb0579c9bf8d15a77a4f14ec623421d902 -7082,0x09793aad1518b8d8cc72fdd356479e3cba7b4ad1 -7576,0x097b1ec678f135fa31c7d4c0d92b34940db06251 -6776,0x09f9d7aaa6bef9598c3b676c0e19c9786aa566a8 -6802,0x0a0a22189c8732ca089d6fb6709e65140a446a41 -6697,0x0a1059c33ce5cd3345bbca557b8e44f4088fc359 -7063,0x0a7b5caaba3ffc775a0ab83544400005622f62d5 -7506,0x0b08d567d1893d9e0b48e61d1f9ecd9d7c6b11ee -7134,0x0b1e9543c3cda15d431dcdc94724f3ff3cadac1f -6915,0x0b2c895915361ebbf7eafe80741a398fa85649ab -6835,0x0b6364488e14568a125440f3c42ad59698fb9e38 -7346,0x0bb16a341eda6b5e0768b55eb091cd914be96bc1 -7288,0x0bb25623946960d8fb1696a9d70466766f2c8aa7 -6636,0x0c2ed9b23baf9c5f486e175d406728d3be46d2a6 -7707,0x0c35263a2c4ed7bb812d3b4f4a5da1a623fb2b7b -7335,0x0ca3985f973f044978d2381afed9c4d85a762d11 -6737,0x0cdfdbf84ac56d54657d3895602bb2982fd1efe4 -6747,0x0d52252a83462a977de567ef5bdb0794db258d14 -6557,0x0d6185c589ea031e5cac284c64f6208d21c7edd9 -6851,0x0d69505994ac920aa28ec5d3783227072f236e8c -7293,0x0d8121e17b74e537286304c7804a5bc592a7964f -7286,0x0d9a83b625d9793371bf4777de4f24f57f4527e1 -7476,0x0daea0367d7b82644c4db4d992cf4ba39c8a359c -6930,0x0db1b224c5203fa22cfdfa3f92519d150ad86612 -6569,0x0e07d4838c0321c086bf283d2bf1b740137c42ff -7618,0x0e1a5c48f3ae7c629155afabbbcd5442627c7ef6 -7738,0x0e4695edb83fb23e6b12afa3660bef09610791de -6991,0x0e48c8662e98f576e84d0ccdb146538269653225 -6519,0x0e54ac93f8b63f66e4785892ca6a27201878df0f -6760,0x0ea09d97b4084d859328ec4bf8ebcf9ecca26f1d -7411,0x0ea666319f4f0eaa7f16711f2fe7f4b159957f48 -7743,0x0ed899bde78b8f647d8bb1a44cd2dedfc087188c -6608,0x0f5af2a8a4df79e354455788fda73bed85ab435c -7436,0x0f6877e0bb54a0739c6173a814b39d5127804123 -6837,0x102aff35c746b44a639a1ce344a1461306835e86 -7376,0x10305c1854d6db8a1060df60bdf8a8b2981249cf -6565,0x1055b51310f447c4717b1aacb7e55b06a252d4bf -7693,0x105f7f2986a2414b4007958b836904100a53d1ad -6647,0x110701593b2664cbf3f8469c6c6a6378ba4ef124 -6844,0x11ac553488b2170a9ad751a5455d0c9a134c982f -7057,0x12120acbe7d6402104b68a2e86c071fcdd1034fe -6692,0x1228c7d8bbc5bc53db181bd7b1fce765aa83bf8a -7130,0x128d0eb5e26cf95128aa9ac395f88dce705aedac -7049,0x12e6caae021c78a6c700d605e80f2a9b76d4f252 -6894,0x136b1ec699c62b0606854056f02dc7bb80482d63 -6538,0x139af9de51ca2594911502e7a5653d4693efb4ed -6552,0x139f94e4f0e1101c1464a321cba815c34d58b5d9 -6536,0x13d6976970f25cc0be2be45ea4767f9769f6e35e -7177,0x1454ba0f1c5fcb401cfe72e028114fee022990ee -6594,0x14688dfaa8b4085da485579f72f3de467485411a -7682,0x14a3f810db4e4ec74906d2538de00a4c995f229e -7564,0x152da6a8f32f25b56a32ef5559d4a2a96d09148b -6516,0x155ab3a2587674e8c89d87c9ca39a06f4bbaa2a0 -6865,0x156e18b355b36ff3b9cb36bcaaf3155d95d3319a -7528,0x15df53aecf14f4525be5f7ac8240968cbf5bcaaf -7720,0x15f71cb39f39a3b30ef610a15ce1cbe766cb069c -7119,0x160971404036fbedf63baae78d5ef0ec65e01abe -7407,0x1633adaefbb54b5dbe5d19c326687a7e3208c565 -7280,0x1651e832dcc1b9cf697810d822aee35a9f5ffd64 -7541,0x1681212a0edaf314496b489ab57cb3a5ad7a833f -6806,0x16d1cee82608b12c4116626dcc1e69685561bf4c -6733,0x16f7332ecec126f8bcd791887d6c576f35525f2b -6745,0x170cfcbcc56376df6276ca93c27b2adb31eb1490 -7501,0x17163332e58c05085aed6fde4fc745a9dd58374d -6598,0x17628a557d1fc88d1c35989dcbac3f3e275e2d2b -6698,0x17ccefad2ae2784af7012af04c76bdafaa4e7b37 -7414,0x18195caf157e36d708df12bc3648dab6d3d80a25 -6680,0x184d5a9f37d0ca2151102958d174e21db6605069 -6674,0x186c50515e6ee95cec381d7f6fe72c67085dbcb4 -6789,0x18970122d688b97023edc44da27a51230dc6799c -7623,0x18f763fd61efd3d9884981cc2cbd6845351e4b70 -7379,0x194ad8766c7d104305a476d7f637d7606b3cea80 -6783,0x194ffc3d2ce0552720f24fefdf57a6c534223174 -6518,0x1951c6b2d9dd9a3cf10aac5e79a7eca0a5300bb5 -6796,0x1964841dfc505d7e8e3c062e26df775076526516 -7228,0x19ba5013824a45ee0f9e4738c8618d40ba11234a -7728,0x19bd719105008c82da4b4fd8112c9d2a97c0930c -6579,0x1a83bbccfab68a8bf901c298c5db5acb8a2c7b05 -7445,0x1b9d6cd65ddc981410cb93af91b097667e0bc7ee -6681,0x1bc086973125088135046cab79cb3e96744aae53 -6601,0x1c7a2e680849bc9c6ab8b437a28885c028739b82 -7185,0x1c93b858e90398062c67088484a300fd6b7c9283 -7026,0x1d46c117e7e9a5dd242724a1952911ece78e0831 -7239,0x1dad8808d8ac58a0df912adc4b215ca3b93d6c49 -6649,0x1eaed534ee8d25da73a4e21cde96e4fca9c46187 -6855,0x1f29b6a034e88e8ec76c84878a3eff3ba9a5390e -7142,0x1f51f6459fa3494b1c6efc81b2be206cf5a4d3d1 -7716,0x1f53699b435326b6e264727b5504cc28006bed8b -7087,0x1f6b92eb7aa3daca3dccbad74928827cf003f9a4 -7268,0x1fb664858da319b68fc5f6d47dd6ca0d994055a2 -7304,0x20026b04c176320da51a36ae2a36e5a393e20783 -6618,0x200a21bb1b58d0dadda498a9c3f06acac1d6d79d -6513,0x20d156aced91e2b140747d27d12be1ca786fe901 -6959,0x20ebfbdd14c9d8093e9ac33e736ac61bbac90092 -7512,0x2107a107d1043b2c442b8de40d6696c29bd2c5b8 -7443,0x210ba565130f2af399fd8435a279b22894e8d096 -6449,0x2114d1c571cb541f3416a65f8bccff9bb9e55dc5 -6440,0x21853bbcc6fd7c8963f117a435a0ea67d59de2b6 -7418,0x21b5fc927b4475cb48763f9f11e350ad3eea085b -7631,0x21f660512a18dead0664f189af73d74373ddc45f -6914,0x2214cd24c13d7b36432c66e73984bc44d479cd51 -6972,0x2227af48ec971e3c786f3e06064cba455724d6ba -7081,0x222eb6ce2461798382d044abe53edcf8752b47f7 -6719,0x22602469d704bffb0936c7a7cfcd18f7aa269375 -7262,0x228bcc970003ca7588337604afd9fc5d92bf1c8b -7722,0x2292865b2b6c837b7406e819200ce61c1c4f8d43 -7353,0x22a99cee1763f34c0f067bf54cb10f71c917400e -6755,0x22af5e22f07715fa2e267d2b49062949f71474b6 -6795,0x2302d7f7783e2712c48aa684451b9d706e74f299 -7590,0x230de326a341f1e14edaaf2f426db4e481b95383 -7221,0x232a504a64f3caa0c1effaa4cb4478d12d26b778 -6475,0x235b51dd2d0f45f20f5b42e84a45aac9ea90b20d -7236,0x2365d3e91e585c974c28a5b82d6aa266f68a44ad -7211,0x2369d37ae9b30451d859c11cabac70df1ce48f78 -7070,0x2390844d5ad4f63958b824120e65715dc01e4d58 -7498,0x239847700d9134ceeaec306daa40b569cee1d5a0 -6588,0x23b2558318e4955dfc3402567e22b1ff102db3dd -7714,0x23d5291fea095d8851ce3bb711a10b4982923d9a -6833,0x23d5381713841724a67b731026b32322228ca3c7 -7653,0x2415c2f2e6554441f613d82472cdbe7b63680448 -7546,0x244c689bfa19f046124e75339887f9918317b919 -6815,0x248ab8ac4bdb4ef401ccad33b5fd7fb82eb31ed2 -6699,0x2493291196f02794465b89ef50f80c60fa8d0e89 -6871,0x24b4b6703a2ee7ba75a4fc859b606f0bbaeef4ea -7613,0x24d29234e34793476000fa068e858de4380646e6 -7649,0x250d34097961fbe8bd18c696e5ad6c1c9227395d -6801,0x253914cf059f4c3e277c28060c404acfc38fb6e2 -6957,0x256b054bcdac3fd6ba28038af2c31bc8187e44dc -7645,0x2585754166e439ed27943eae30b9a6fd82bd67b2 -7110,0x25fb7fb838e12547ebdac462bdda777ac63f605e -7315,0x261287459761d6d2211ab1a3eeb082f308b9e5e9 -7709,0x26a035d9a2ed696eacc3816674c66a7eb73aab70 -6754,0x26bb77b83e7d64571b403f6f436eb2d05309043e -6630,0x271396a86396edb0ac810400f412859704185ffc -7303,0x27169242fce0d089bc2114c49338c2fa90424ddc -6555,0x274d1dbe298993ead5ac1b25624f53786d16006e -7079,0x27665271210acff4fab08ad9bb657e91866471f0 -7389,0x277efafccb3683fd0dd5facca8f37e3130d359fb -7552,0x27b9c4af1bacd39fb216a3a27c9f1e3cd8570b19 -6847,0x27be2efad45debd732c1ebf5c9f7b49d498d4a93 -7123,0x28224ef515d01709916f5ac4d8a72664a7b56e98 -6823,0x2891452e2068ac9471e3610e956510c46b4effe5 -7197,0x28bde5c67624b2ef7fa27c9c9b0678fe3009913e -6486,0x28e1ce29acdfe1e74e6fa18591e1d5481d2085a1 -7199,0x2903c913bbbac9fbb6c4a080210ced9eec1f66a6 -7040,0x29533e33226e5383a36eb4bf515ff067379ed841 -7559,0x29601bafee838f75679e0024e9b8e44d6f1b6f2c -7647,0x298cdcc0dfece79d7337328080cbf1742b6f7b54 -7241,0x29ef2ee9eb05ce154d63aeff8d40b559ec45c52d -7517,0x2a1fa1fbf771c353ebfe63472ebbb16444a9b0ef -7106,0x2a3507e712be8c14971d59f0f9c21eb560177fe3 -7350,0x2a656e9618185782a638c86c64b5702854ddb11a -7135,0x2a7472073d67c86c22b99dbb7be9c781b0e93669 -7413,0x2a77e3382e205d586ca6e4c9f8d7dc6e6bb2054d -6453,0x2a837135a1f18eeea18e3bfb16265550bf705b4b -7656,0x2ab5ab7574276ec45a62415c79ab958e706f49c6 -6921,0x2ae5608ab3fb3863a713715f3f59216bcabc5150 -7706,0x2aef3f9e57e2695c32beac56d79bfe4efb55bf63 -7363,0x2b3bb4c683bfc5239b029131eef3b1d214478d93 -7247,0x2b8ce8a8abf039db01b2ad7f512b9157467eb264 -6575,0x2bf61b08f3e8da40799d90c3b1e60f1c4ddb7fda -6917,0x2c15259d4886e2c0946f9ab7a5e389c86b3c3b04 -6856,0x2c2cd8db955d441d51001946265bf3b777b0d585 -7374,0x2c550a8a74ccbbbcd185a30d60f236cc270105f2 -6849,0x2c5e2148bf3409659967fe3684fd999a76171235 -6993,0x2c5fc227af9100f8d6fc963549de3e3bada8c3a9 -7622,0x2cc4707f6aef86cdba05f45da98d365a66dfd5d7 -6500,0x2ccaf0500142f8e563ff361c3b34abee16b6205d -7120,0x2d1758b6f7d420c505f7dad30d0f078e1617a73c -7202,0x2da2a20f43156ddb295bf16e873c6f2c7f2815b6 -7260,0x2dc99068ad1d5b437b63c2afa5c3d30a23084764 -7580,0x2e0c03da27cc86824bb6ca7418788d55349e622f -7597,0x2e247a7eb175a776f0d8513f82d8b4aa03ae8c3a -6466,0x2e26d4bec1464409c154f653a238ce5baaa00198 -6585,0x2e40e8328f30f5c0755404f023426eeae981e0a8 -7599,0x2e8bc819a444c8b26892ca42839fcfac73e177b7 -6864,0x2e9918026db125c692aed46e18697755567f4c1e -7494,0x2ea06e73083f1b3314fa090eae4a5f70eb058f2e -7234,0x2ea0cd59815a743f28e930d5e33ad6fe23bf28f7 -7752,0x2ec454957c0e66266398076f066faac77c48d88d -7310,0x2f94942764c997e777f7ded68679a5ecc6b7514c -7557,0x2fd9a39acf071aa61f92f3d7a98332c68d6b6602 -7125,0x2fffebed35931abd9014a39ae41fa26d511e31f4 -7594,0x3045fd12137712a06303f0ad7cf1a8d75319c01b -7104,0x308ad16ef90fe7cacb85b784a603cb6e71b1a41a -7475,0x3091acaeaf27985f33a8a4b0eefea49dc60c8506 -7140,0x31a1659ca00f617e86dc765b6494afe70a5a9c1a -6868,0x31a53d5238391d1449b443edb5476b5d8df23239 -6874,0x31ec26de77aa4c859b7a15a37d2fc9eb61289c14 -6735,0x324a32dd025c8b10f68ce0ee457757af5e930524 -7190,0x325a0fa8e3a0a40fa82bc6d222b1af765ab77b36 -7565,0x325af017a497953734cb7b1f51580ff9ad1122b1 -6786,0x32a31c67343d14933101b96a1d78ff597eec9c29 -7726,0x32a357ade8497ea57446b4bf5099fa9f0918592f -6548,0x3382a15bd1956e908a1ef2d027f92b2bfc84558c -6821,0x3384e48e5b7e00e135e69a87111264b8fab0683c -7539,0x33c3c4d49dee4232498b3efa5336c627801e998d -6621,0x33c8daf48c691c54897070c3621fdecc249c7202 -7109,0x33d4613639603c845e61a02cd3d2a78be7d513dc -6897,0x3403a3430e4a62577e70fb48e070268d84a8f910 -6628,0x340b5d664834113735730ad4afb3760219ad9112 -7062,0x3459276e8f644f111539804e08253799b176ab83 -6862,0x34783a738ddc355cd7c737d4101b20622681332a -7183,0x34830e0959fdeebb0f3c8c463afe064f210dbddf -7430,0x3493abe179eb25b8e01b879ecb921e86dc6ec5ab -6960,0x34c2360ffe5d21542f76e991ffd104f281d4b3fb -6931,0x34c4fcabc426844d321a96748af52937348cbf78 -7248,0x352b138d7f6500fc71a014f3c7ed48b6d99fbe4d -6999,0x3566f292b47c2afb10a2b9cc9a3cefbb3cc8ee4d -6578,0x35b9d048d4cb99bb34d59fbf962e86b8ee44760f -6605,0x35bfc780c232171b5830a76632a70821c1a1b087 -6773,0x35ccac0a67d2a1ef1fda8898aecf1415fe6cf94c -7279,0x360b8b3193d533da9c3756639b8ba0431f587553 -7423,0x360bc3acb4fea8112d8ac20ce1e93b9b70c3d85a -6645,0x362b702b552d134a164accaeead3a7863b6727c4 -7540,0x3639a4793bf2c2705b63782d207f5ce15e68865a -6444,0x36841f7ff6fbd318202a5101f8426ebb051d5e4d -7004,0x368a5126ff8e659004b6f9c9f723e15632e2b428 -7114,0x36fbd21e66802442dc77354d993684b981a50671 -7358,0x37006dc82da2a3a2c86a1d998204802aa0445a32 -6901,0x3713bc72567a6c797ac69678f72a5a0d80c56dc9 -6582,0x374f2d3353bd664a2b25508df4a3b58939999e1b -6522,0x376b309dff447cb163394b8550c55d6b7414bd10 -7212,0x3773dfbbc894bfa9df22855fcec1d1572efc5e0a -6535,0x37f35a28197f335df0a5990ff92ded3cf5e8d949 -7573,0x3942722d61d784347f38caa493b1e073ab321b22 -6429,0x395ec94bf3e3dcd3afbd82cd03197731411e396b -7254,0x397e6740a14cc409ebbbb3ce0632a65a5eb5cb0d -6693,0x3a52b21816168dfe35be99b7c5fc209f17a0adb1 -7397,0x3a831d691e0f56f19ede8c48e8513a9d84a49334 -6643,0x3ac19fc691cef4d6d760b41d675a6f5001221f2a -6762,0x3ad002e469d7a78b3ace7710018f5ef3524a8581 -6545,0x3ada6d040314676b1e8a4bc1a0a16060b42a00ef -7232,0x3b84784eee56b9bb62abac5888ce10cb077ef645 -6498,0x3bb34a671d1c25ffad74065cd3fa7ea5880731b0 -7296,0x3bfeb366a5bf7080078c74346f1d82074987b84b -7290,0x3c30cbd400f8e7c099ab27cf28da843cd4433ffe -7342,0x3c5c3677b46b2b90811fee2dcffda0eaa3826b19 -6730,0x3c8a1f04e57d7f047089e1566a5af98240b472fd -6441,0x3cd650db31624c867a4969492879d50470200b98 -6781,0x3cf1be829c5ee73630482d2e92bf9461adf1b213 -7396,0x3d368332c5e5c454f179f36e716b7cfa09906454 -7099,0x3d39a6eeb58ee0ebdfb6022283882956532ef7a7 -6443,0x3d3f34416f60f77a0a6cc8e32abe45d32a7497cb -7495,0x3d869950817920eda9fc9a633ab7f06b97444dd7 -7400,0x3dbb0eaa91172ca17eac4fd07e5b8958a21840f9 -6629,0x3dfbb7d0770e6eb77fbec89c0840e9a0f29c76ff -7167,0x3dfe5f3806ce358d1f8433070cc523c9b22930f1 -7295,0x3e4ea2a0679dec8c9040d6538b45425c72be80d0 -6863,0x3e5c8eab6b3af1b13e41b18f1e07547eaa9a42d0 -6761,0x3eaf011a9b8393cedd4f331eb5dd1739cf7ddd71 -7579,0x3ec9646844f80b308aa0910731a45bd5639e95b4 -7166,0x3ed04ceff4c91872f19b1da35740c0be9ca21558 -7086,0x3f30190416ebde7f50749cf3579af1beef2e7fa8 -6514,0x3f542137f3551cc56ff71386a11136d4588c1b41 -6962,0x3f8dc633227dec2e4a52ce8db91c4858a8e3ae78 -7670,0x3f9917995e1a55060b984dbee9d7358d9eb7ac8c -6942,0x3fb0c5c54f1de3bd8083c6a0f21701d36a004ad3 -6606,0x3fcc706dd9ab9ac3daf6f205abe26712ddcbbd3e -7117,0x3fdcccf40eefe389a8ba73ff2aeda10888ff8f3e -7556,0x3ffbc2f1029ccb193d011757e2bbd57538711d86 -7287,0x4013bdab428b478e234373c773733ca149febd2f -7561,0x4022ab250b5c32c286a3953bc740368d6b68b067 -6624,0x40542fea068ed07247645581c04efa1f83ff04e5 -6777,0x407e5a7548869ce520354d8eca127754625cc9c8 -7191,0x40d4d04b7eadf803ea706b44f44cb0d0e24b7433 -6841,0x418d195155058abc4a26d59c8a6ce83a7d52288a -6927,0x41ae759b8e75f4ee544cd08b4369e5f4719561fe -7088,0x421def861d623f7123dfe0878d86e9576cbb3975 -7121,0x423ddc17a01e3e44a0bd125ec2f645b8b9ad3259 -7096,0x42aaa33a13bdac31d8b9e04d9d5db7f9fc8c0119 -6701,0x4308427c463caeaab50fff98a9dec569c31e4e87 -7233,0x4315e1500ecf3b103ead2be4b16bae97c44073cc -7724,0x4334dfcb85eab5da80b162f930b56f47ebb156f1 -7402,0x43406c99fc8a7776f2870800e38ff5c8cc96a2fe -7316,0x435e6e499610b6de3510f0cb047d3575c7bca6e1 -7122,0x43c5f53ffd4f909aed9b51c562ebf1d762c2b496 -7235,0x4421b110251399fb2b27317e6399dc038d42039d -6785,0x442b69937a0daf9d46439a71567fabe6cb69fbaf -6899,0x4434f56ddbde28fab08c4ae71970a06b300f8881 -6679,0x444efcb5006b7177db79dee4eaef6fc45106a69e -7386,0x4475a7e7d0151110980c573435aefb636ecfcea7 -6908,0x4489c9fed1ae2f0ffe738182677cd06268e119ca -6767,0x454655897e83f8f54fa3c1c91619e9261e595e7f -6467,0x4589e0e6eb3df2c173a7dc988f07b9eaf9604fdb -6997,0x45c55bf488d3cb8640f12f63cbedc027e8261e79 -7451,0x45c93d2c1994a70fd2af98da6ba100953b96a768 -6814,0x45de4460808129a34c0ba0cebcdd429acd50a207 -6596,0x45efa1587820488b4f3c0f06a08b3aa871e7b42c -7269,0x4673a25b84097df253a6ff9c314f11ca60bf8c05 -7578,0x47336f1045f9c97d0aa68cca9cffb02d453d4837 -6955,0x478d479bac034f89fa28d8d2ab430ec973a3a0ac -6732,0x479b0baa4acf9bf6e7fb8419fc230d249481b8b1 -6920,0x47d82a95496c9c656b0237a7e76e62131e5f0002 -7223,0x47f3797b0c316be51d4ed2bb951eaa9dbda43d52 -6826,0x47f99a380cc020b5db4b000af445a232081ceee7 -7165,0x496b1c5eef77e6ea7ff98bb22b5ec01dd4cfdeda -7398,0x49700eb35841e9cd637b3352a26b7d685adafd94 -6875,0x49b35be7d96888c02f342552ab218d859599aceb -6744,0x49c48c4381add1c60716fc5769f33fc33b3374ad -7308,0x49dc714ead0cc585ebac8a412098914a2ce7b7b2 -6819,0x4a74fc79618b018b0db1dfeea225b38666d32871 -6832,0x4aa0dabd22bc0894975324bec293443c8538bd08 -6523,0x4ac03ebbd878df59afac4ba2dc934f59b19b044b -6515,0x4ad2d14bed21062ef7b85c378f69cddf6ed7489c -6650,0x4ad314a760a6173326ef5c828f51ede93b432a12 -6878,0x4b0dc551d2d45dd5992009bcf80470cc02df8754 -6563,0x4b7c5880a2488bae680cc1ec48f3e3e8e74da54f -6948,0x4b7ecf5283e441a3ce6320e5f65eb69b61c0d465 -6731,0x4ba1bdbc15281775a8437eacdbaf8fbfd3a6a224 -7694,0x4bd5674a720c212ff515dd51d4e5d304ff16b3d0 -7480,0x4bf3c1af0faa689e3a808e6ad7a8d89d07bb9ec7 -7281,0x4c017d10990d2a095b1848d0cffe9235fbc0465b -7756,0x4c0f7b167e7d280d97471f5a17f4eb214e15a440 -6834,0x4c2a2390c2908e0e489f8a487ce332f7cd6c1357 -7428,0x4c305d7c88793381f7a2902d38da4d91eb798b17 -7227,0x4c35c27ff8f0dd039b4c4f0d670f2d58f6215c9c -6529,0x4caecc78ab2573844ed8026aa150f5c0068320ad -6503,0x4cffd27984d9337a515623ea4a6a827ddab1098b -6714,0x4d178b91a3b16a124d2a90d944db8c70a334fbda -7074,0x4d7a0980db5cb48413dc96f784792db4b1fa68e1 -7635,0x4d7ad1d420f5a0d53af92e9d3346155ea2260a10 -6476,0x4dc1636f5b4c1143cb03772a93923718e4320543 -7739,0x4ddd30fff71bd03affd9a6e27c7c8c0cc9731b22 -6947,0x4df6e29c07c51e5e7f7a98ab90547861aba42b56 -6958,0x4dfb42753f735fbbbb2b1312f72eb05d905365ad -6479,0x4e14b9793f580d57206c9e657f8db62726f7bbaa -7277,0x4e1f44e48d2e87e279d25eed88ced1ec7f51438e -6599,0x4e38cd9399195e9bc4b37e17cca32dc147a3dfb7 -7486,0x4e8f55f1948d3e61a1fd3cd38ce05efc77bffeb5 -6635,0x4eb7ce307de597f854124d018ec9ede0a8d57931 -7108,0x4ed08210706f5b74584cc7f03b38d800dc27936b -6939,0x4f5f4617482b6399d63329c5070e6f37020de286 -6879,0x4f7720d55a680c6727d2dbe8734f5ab218d64cfd -6448,0x4f7b031b08fa58d68f5dff9487dd8643e28ab08c -7696,0x4f7d521a7cf8fa3b6ebf5d960907ae83e2888fc7 -7717,0x4f9c7ce72255cc04ca2159793a59efe3e6f40aa5 -7677,0x4fb59e8dafcd398b2ca7fe2af5a7405cd0d22278 -7362,0x4fc5bed961d41ba5f198de7484637800dac78eb1 -6816,0x4ff54624d5fb61c34c634c3314ed3bfe4dbb665a -7033,0x50197701bd09f8dbdd6716b1b9080574819a3776 -6807,0x5076c4fa56fb78b28ef4a542c7ef2b0f95186e58 -6480,0x509072a5ae4a87ac89fc8d64d94adcb44bd4b88e -7715,0x50a40d947726ac1373dc438e7aadede9b237564d -7198,0x517d0676e80115c1aab36332b1732567ce6d7010 -7496,0x51a8e343841eed0439de080cdc5d96c4a8568860 -6490,0x51c033590346816172811dc2a8921c8d87041cb0 -6885,0x51c4559b6fbff882e9bd87340b66c527f48a4a13 -6757,0x520d28e6babc090712584a373dbe72918f73152c -6584,0x527b99e3d31f71d2414cc2dbabe0d527f9160926 -7417,0x52c4612dbb84282332300a5cdaba7acd7661a94b -7448,0x52cca59bfa0228f41ab69558f5f2ee3739323c02 -7041,0x52de9d44d192c09c047c66440a976fb67e697bbb -6903,0x530f4a84e99ef78a5c2a4e64cd5d126b40c99242 -7275,0x5374761526175b59f1e583246e20639909e189ce -6817,0x53bad7c8ce47fa070e5bb25adea796409e0e8058 -6763,0x53bd23eed35c00ef09d4ebd8f71005b0dcc97e1a -7640,0x53c3f071ab1032f2c52c0f3395c308b6ec5036b5 -7593,0x54632eb3a818e1158a5ff3e9342e3b159fd61f5c -7289,0x546ee4a4299dc3d3b64d6a889759e04acef6b92d -7188,0x549dbdffbd47bd5639f9348ebe82e63e2f9f777a -7007,0x555491936e96592ce5d714f761325c4dc0c5dba7 -7513,0x555a66ae81b447f3e4533a8159e2462d17a7e7c3 -7025,0x556f920a83faba9be8019a33cba4898fdb38d130 -7297,0x560f562be696baefa0029c954cc69352bfb33e41 -7444,0x56569760b712021d2957d8a8b06b319d486d7907 -7518,0x572f816f21f56d47e4c4fa577837bd3f58088676 -7674,0x577b7dadf6052d8dfa3c394143a4021b433a809c -7514,0x579e622b355ac69a3a772d29a98379044cbf7722 -6558,0x580f76af1fc4bb29f0032eec6e0f7460d26b5f56 -7270,0x581b5da10b5cb8e7005939e9c9498d5900639423 -7684,0x581fa71eb5b5d704d0c268eed58e48f801338f7b -7192,0x5868405d4e6c805ebac392d20d51518a18b5c8de -6792,0x58a84e0b1fe1a5f6ee7541b26a30de044d0cc6ce -7334,0x58dcff20b24a2943638735de76c8ff1466ca184b -7741,0x58e178b0cacd1bc56a2cc408030a1f69edc315f7 -6511,0x58e6227510f83d3f45b339f2f7a05a699fdee6d4 -7238,0x58e7da4ee20f1de44f59d3dd2640d5d844e443cf -6829,0x59127b40b8050fa7ef5ca5cd4f13333af1464e31 -7680,0x595f37e1b21870571ee99fbe815d6790d817c0ba -6884,0x596c22788ad45e413ba5fd7001ca43b9d877b4f9 -7006,0x59798c3ec713213c274bd8827642daf2a798181e -7359,0x59b007e9ea8f89b069c43f8f45834d30853e3699 -7237,0x59b01789bf268c7c77451d02758621990bb50bbf -6893,0x5a155c378d66d8eba2262e744738a76d913e5945 -7368,0x5a439c235c8bb9f813c5b45dc194a00ec23cb78e -7149,0x5a56fe770a4b3a70f67d746ff8c244b7439d6f1d -7615,0x5acd4abf5ddfb7f27b5940d1aef640d6b67a2cba -6570,0x5ade35cedded082bdb27a94e2a20ac327a67172e -6922,0x5ae6b5dd28f44689e4d17f47aef1f863c36df556 -7387,0x5af0072617f7f2aeb0e314e2fad1de0231ba97cd -7133,0x5b37d50f3b7f03884d7cd005cdc7120f5060808c -7051,0x5b6beb79e959aac2659bee60fe0d0885468bf886 -6654,0x5bdbba15a8e03d7e21eacf566c8515972dc3aba4 -6906,0x5be1c70475422255684e4f312e57fe782a5102f4 -6451,0x5c2b0fdb3c828f087fdda19cf7f6ff7c51022afb -6758,0x5c4fd006f3dc9c6a2259b2fb82cfd500056978ec -6813,0x5c59426a609398a9753522e840f422faede70a5a -6734,0x5d7569cd81dc7c8e7fa201e66266c9d0c8a3712d -7497,0x5d79eeb57a35ec8b6b9ef55052c5c9f919c3c3ee -7044,0x5d89892bca7aa5619fd6168d38f73bb84d777e9c -6445,0x5da48d842542ef497ad68faed3480b3b1609afe5 -7727,0x5dca1c6c75f6410cb4020a4ab5657fef716fcfc3 -7450,0x5e20ba46a2d2e50a8d1c577f19fe7b875c99da64 -7611,0x5e51817910c53a01e7ee90b8640a66768075bf2e -6873,0x5e744132337608c075d4fce7f32f17bb8d9ff8ec -6478,0x5ea2544551448cf6dcc1d853addd663d480fd8d3 -6669,0x5ed8d0946b59d015f5a60039922b870537d43689 -6846,0x5efad3e0a9cdae34f1dab66369db77693c6748a5 -6672,0x5f8203298fa0b01386b0f879f0aa3ddd0470c896 -7663,0x606ad67d3a845b7f3c71ce7329874141fa4b5cc3 -6639,0x6110df298b411a46d6edce72f5caca9ad826c1de -7491,0x6172289961007908442a0437891dcd966f368563 -6457,0x619676df5c402d0eb008104e9a658c1f65dbf702 -6634,0x61a7ae08b9061f96934a2093cbe59f839355fff5 -7337,0x61c7bc9b335e083c30c8a81b93575c361cde93e2 -6728,0x6209043fcef509ba5624054da26eb8d52a12efcc -6974,0x62137897f2b09c588327d80cbbfb9c6018c352ef -6546,0x621d72b38da4cf68285d76ff0e0dbd8fc8f93e3c -7208,0x62b6f3733ab95ec88864f03f758de4377d6c751d -7641,0x62d560e670da20e176a5a6592a911c4502b08674 -7431,0x6330d5f08f51057f36f46d6751ecdc0c65ef7e9e -7338,0x6345d77b30e60ffcdceb2e303b432f0665dd5a9f -7322,0x637340ce915a6e2eee7c02521ac334c30c219d0d -6682,0x6393113a43a4a88b9f3d53b4b21e7feeb5d3d821 -6675,0x640cca1164154b7f5e7b5c2eef6d757246cd3e2b -6505,0x6465227822e54a658840d3636c474220f8538dff -7170,0x649f44cac3276557d03223dbf6395af65b11c11c -7583,0x64b9f6c2b5d089d6c36ea12618c7b007ebd101fa -7340,0x653a13fde5508248467d7b633286d6044f7be745 -7412,0x65801db5076ff94ab85ad3b394af0ec56ebe8c72 -6613,0x65df3ec0d5fd06a2f29c68e7894804b496945ef2 -6539,0x661c2479bffe01eb99eec9169149baa5dabeb883 -6966,0x6680180094df2421a9c5140b207f95759c9080dc -7654,0x66bc85ee738f79d60e9793c468d4ead944b850cd -7602,0x66f541d5f16f2451de9de5098d241774a33093f4 -7604,0x66f916cc0b0b26c1783974a60cef9b0afc382825 -7744,0x66fc48720f09ac386608fb65ede53bb220d0d5bc -7595,0x6733817ffe93747e30a329cd6bd1aed2dabd0e84 -7740,0x67e010545133038292d124e49d1db6459fff82af -6610,0x6825dd6b5b83fbbff1049a44dc808a10fe9a6719 -6590,0x68287419fcfa1c186515e99a35ff3c970b3b3c66 -7111,0x68a8b098967ae077dcff5cc8e29b7cb15f1a3cc8 -7662,0x68bd83b894de9a65b1f013646813f9ace038e674 -7366,0x68c4cb7adfdc6de5298b2f1be6bd9de3c3be6c47 -7029,0x68ec62373c0ccd1954f6f17fe6df68f4b1acf56a -7410,0x690215ec6809605de8f89a15283250e6f59470dd -7255,0x69125856cfbadba925ce8345d0a5de98f375920e -7612,0x692c746f443031559e9816b50c99165fd452982d -7511,0x6940e7c6125a177b052c662189bb27692e88e9cb -7608,0x6941ad5ac604d2329f96bea75c7b25d19cc06701 -7538,0x695fdda0e1546ca369df3cabe8ed33407cf62341 -7327,0x699c039c9e21d49b8c38768619942dfab6e8e38d -7581,0x69c3cc1d56d8b861645c79870b09bbd05c88e6c1 -6925,0x69cc4951d587d11577f16ca9b7d0e9d2745b27cb -6861,0x69d255473d0d15dc087cbf4962a65839aa28a2c5 -6845,0x69f5f465a46f324fb7bf3fd7c0d5c00f7165c7ea -6912,0x6a01a4b20d6883d66df40bd61b15fd8527a2627e -6620,0x6a071d06b9e317a5bc0780dd5e7ee8c41c5c3cb8 -7563,0x6a15ee080c2fde246b674918bff3b52d414525cb -7522,0x6a5a1e32216377fc03bffdc9b33fe29c2f14ec84 -6859,0x6aa31707bd6acf24063a806570160978e62752a0 -6902,0x6abc19f21d5ce23abf392329ef7b118c7b5f2aa8 -7250,0x6acc3519d10e46e5a228615c9d4b57cc0113a212 -6825,0x6b6a42623f9655926833b120e6502eb01315b37c -6812,0x6b9cbcdae03c4cdfd9fb9d987c74856ac332fdcf -7305,0x6bad3be3a7b3853739729833425a8b22737d0dac -6576,0x6cb4eece70ef0eb8e7f81f4a0de0fb1521e77f74 -7077,0x6cdde3102cf1347c1cfa312e4ff5789c2aae9480 -6655,0x6cf29c515a33209c6eca43c293004ac80c0614f0 -7377,0x6d099fdfc3f40f1b04c9b8b3dc1702b540cb65d4 -7283,0x6d4a64c57612841c2c6745db2a4e4db34f002d20 -7712,0x6d5403b5b195f0f26aaf5e2a7fd58ab1d0fb2f3e -7405,0x6d62aa1535c7c33d7f6592562f091d193e180c57 -7609,0x6d9bde78d5562b6be4b6a70f4f2511fb1a8f9e10 -6651,0x6e3ffc4161931793b7fd084e761c0d12126fd376 -7490,0x6e7209c0db7110fc5606baac492ce4dea2efed8e -7658,0x6e8d3f3705f23ec1b93d7eb69fdcb97c152223f7 -6447,0x6eabf41d76756d6973dfd3d034333d4f4ffd5b4e -6694,0x6ee44d9e0f868833a5543bcabc3bd1a7d843edb8 -7218,0x6f86ac26002d943b7f2433ac2b53f142fcb039a4 -7408,0x6f910a87565c581e101fbba25fe5b2570181794c -7210,0x6fbcabaa42ce1818f23a0b909dbd9bc7691fc1e6 -6820,0x6fed9c8de9886557aa7f4bf7784cb579d38f833c -6631,0x700edd66fb75516427c793f5fe376f6fe1acc932 -6700,0x7034e2e579be5fd233cc1d1955af3fa87cd0aebf -7164,0x703f070bc98f835edc5de183a537d837a7c65c9f -7009,0x70aea7b455510640217cbcb78c5f2d29db5a4a01 -7574,0x70ee22558a577c9c3c7be0a5f9fb494c20e0545b -6512,0x71452f3f9c557dada476434a8c64d5d3410861e4 -6895,0x716f25e9d1c91a66c3d8a7a62baf366b84096801 -7550,0x71f42ca320b3e9a8e4816e26de70c9b69eaf9d24 -7037,0x71fd2f49f289d75d0c1e108c97fcb2a4c54ab424 -7695,0x7221d65b8e46a380102ebe986c01a9481e75bd9e -6571,0x722e8e89dd4217fb27447053b2cb3aec336a2f1f -6690,0x73046bb688eb512a6f2f759e3348017c3e86b712 -6689,0x7322e8f6cb6c6a7b4e6620c486777fcb9ea052a4 -6858,0x73230988dbfaec3cd3a34927ce57052838a21bc8 -6676,0x733a69d080b10bc897452ef783020cdfe012974a -7747,0x73575b75c222bb2d6c2240e725caa654b8ec6bad -7204,0x73a959bb4caaf4325591e89fa9d2307c4c72cc50 -7032,0x73d3c278be973624aea70ef89c61113e55317ab6 -7203,0x73d8fd1dc5e38cef6754fc5009015caaee218461 -7507,0x740444afa4f5a7cd569a7714d5252a35dc1fe299 -7596,0x7438ab1d006a01eebf8ef2c07ee54d475d9c43ac -7225,0x74550d009e8b0dd1cee5d12136bb37bd18e9aaa6 -6458,0x748f78a5dc4a4325bcdf251dee85e481a2dc6929 -6696,0x74db8f9fa2b772db63657f5e3135eddf3566aa36 -7163,0x74dccf74644485a6920d0cfe1a4d34b2f4216181 -6470,0x74eb1e2efaddde923f92f31c209a788475a20a1c -6657,0x754d58ff3a1f0671569154f6c1e02274dcdf7e06 -7638,0x7552b73fa041de429b30027d869ef82788b0a57e -7503,0x75d6271e2110d630ab32592ff989d9e9107e36cc -6592,0x7612e9c4c643fe9088d58fc29e37ab889b61b31e -7330,0x7624cab96f14c89fa9bea1e05e034727e6624f14 -7375,0x767f446fbd3f2e5c91292d9fa51a44102a89117d -7257,0x7690af487c06674785daef91ef6576464b46d249 -7737,0x76bb1edf0c55ec68f4c8c7fb3c076b811b1a9b9f -7429,0x76c6c768ac45f0a3d266ff3e73257937fc962fe6 -6648,0x76fb9f147d40480b6e030c09cd53fdf912c6178f -7090,0x771206ccc65a561f61ed385e3bf55534fed4d9c7 -6688,0x77175f250143095c8b2e92d46cf7b9c12215aba4 -6668,0x7719b38f7fc66d551fab4e7ad375fbb2dfee7f33 -6550,0x773098f036fcffc88cd5d347821ea2f8312cc12f -6774,0x7733b88bf971b9c77cc3c1b64a2ef4fc06b08017 -7441,0x775b40c294b10f821ca82a9560d6203eb2355610 -6703,0x77769c01168011f26a39b71c9c7ba0607752e9ff -6799,0x777a913412d576532120ac1d266d4c908e584db2 -6526,0x7784b850922d995965846d8a89b0c90bef470f15 -7171,0x7786941777d8f2509dbd254b14b46cb7d3a15740 -6985,0x779f424d3b3a617beb4a0db1c21d5505de297a8a -7000,0x77a05b8f5b55f962776eda3f280c49fdef1da8d8 -7103,0x77d7870419c0cd2d24b635795b1d3e8ac724e287 -7533,0x77da808032dcdd48077fa7c57afbf088713e09ad -7671,0x77fb9d52099d4207108c26ab22f9d8bbf691a37a -6743,0x780a7206313f411db5f32c79b15b1c80faabed59 -6677,0x78c2a942c140ff9712cbc9723a7bcad804a4d9d4 -7307,0x78fc32b982f5f35325996655a8bd92715cfefd06 -7553,0x797c42ff20162b806b813b78e5ced6d69e06a09a -7263,0x799588fc3e97f5e856ff85d5b5026d7d7e43092f -7734,0x799654ecaf87e769c56f722c82fbc7bbcc4f621c -7482,0x79ae4aac073c6f153644647af14f202ca8cc39c5 -7420,0x79f1d073a1493d112f07743c784de81cb56147d5 -7182,0x7a13e599119c0dc81bd3e42ddacdc9f5ef5d7906 -7516,0x7af6be46f83d25902cfa49c9e16bec54893f25cb -7146,0x7aff10fc89b162c7abf77974d190e7959cb456f5 -6537,0x7b513667c34130fe2f63812a40c71ec104356e87 -6934,0x7b75c4857e84c8421d422e06447a7fb03c398edd -7181,0x7bd2c4ba88bf5b19e283a7ce4a3b0e0fc5682553 -7354,0x7c1560f20907ed41ac740873facf9e3dce4f18dd -6798,0x7c5fbebdfe7659bee68a4aa284575a21055651ea -7345,0x7d0256e0936103b32aad59e80257b59e988e75e2 -7217,0x7d109db6d68bc03bfba4e5f129922fec300ab7fe -6510,0x7d5bf858398dea0186988a2bfdcf86ae22dd8612 -7105,0x7da4a19e5b6286a499a89a98a64ab51b5c8c87be -7193,0x7e11c004d20b502729918687e6e6777b28499085 -6803,0x7e2f4cb0b71041dd9e2cb254e0f696c37efab63c -7521,0x7e62c949f55305c0679b4e883198f93ac4d8e2e9 -7265,0x7e68d715cb77a605442f183e89b1326f9ecfcf6a -7616,0x7ea760077b84f5e9a5d8a51bf2a49b91d7cd5aa9 -7089,0x7eabbe25a35196eb5d1dbe466a9ce42078ec6f6b -6632,0x7ebcbbd835d17661b26b8afae75b5f94bfdc12ca -6964,0x7ed583f9559ff588f3f5be0e60899e62ab266ec8 -7661,0x7ee729a994456a58f99e7dc7994081b4f5dc92d5 -7285,0x7ef2edd3623312b9a82e25647e12f1c77d0ea012 -6587,0x7f059e5faa4972b098c7539a6721c596bdc942de -7050,0x7f4b56a8fe268666d95e64f16a96bbdcfb89df54 -6953,0x7f87aec4938770e52af9a0b5239521bd9c53f28e -6827,0x802e609928a2fc12e587766c078f341bad8c4d4f -6506,0x803fd1d99c3a6cbcbabab79c44e108dc2fb67102 -7383,0x80a10068a95d2334d210a130a96410faa892229e -7655,0x80c4db56a17c91d9bbeb8d0f53c9ef97aa6d57f3 -6597,0x80cbc948c5dba55dc829472373e9f20203e13eb2 -7003,0x81875e4a7b256762381f5adf95dcb4324450b01f -7537,0x81891ebc7cf265b87d8658ec2e703fb703392845 -6704,0x81b5c28e3905233bc4b96b182160593779d2cb83 -6627,0x81ddfac111913d3d5218dea999216323b7cd6356 -6525,0x82193cb7ab7a0c0f6941375c551cdd3fb66e79b8 -7312,0x823005379687567b5590fcc701ce4a9dec7b4e09 -6508,0x823683ff054ec00eee9fa942f2f6d945e924a86e -6718,0x824da469b59ec0e6e6bb5d611888abf440970414 -7172,0x825020cdb4872efdb85bd794a44f80bc75b439fc -7054,0x828eb47350a644e22da23b402b729a75ce07b5ad -7572,0x82dcd3e7224dda8df6a746d70f1cce80df4384c2 -6753,0x82f962af60e6627d3ea5db5fd7e1a57f7e1ef2b8 -7687,0x8327aa139bd7eee62730a2cb9b9a86821810d4db -6497,0x834ef1f87120fbd01e419f279bcfc0ded360d5aa -7137,0x836804b2c489704068185d190c1763b9a5adda93 -7639,0x845e620a05074f44905661d8b3b3ec29dde08768 -6880,0x84810bf5dcb0e4651ba2848e08965a2869ab80e6 -6531,0x84e12ccea1311da4090002279c2f46ec828199f3 -7053,0x84ec90f9cd4d00ad95002d88d35f99cd9f66e393 -7093,0x8518f879a2b8138405e947a48326f55ff9d5f3ad -6923,0x852210f0616ac226a486ad3387dbf990e690116a -7629,0x854a3500f1443ba99f746ca605d8fc25f0d06f32 -6975,0x857d91d6c63892b383cf10cd15285604ff9976b8 -6509,0x85875a05be4db7a21db6c53ced09b06a5ad83402 -7276,0x85dc119285db5ef687682ce57050b6958fae5867 -7207,0x86213682d530811f53664e3f6ca97731e1c76a56 -7668,0x8649fdb91a47281ea1ee67b83967b0d00f79a560 -7721,0x86508410fd82c863920f194da49a0835717c3673 -7483,0x86770a2940eff6a778768592b42a6668ffb162a4 -6780,0x869d17fd46f76f0e439bec2992f2d45b40253f88 -7600,0x86bbb4e38ffa64f263e84a0820138c5d938ba86e -7588,0x86bda28296662351630627d5cbaad0de38c3580f -7664,0x871d5ad661e6c68d5dbd1ec3f4795f40d73ac4ea -6831,0x8737577818385bc0814c9f52ec810534d2e6b596 -6751,0x873fe70030c5e9105cd01cda9e267ac896883cb8 -7757,0x875bbc7285d52ca957024999315717d45ba99276 -6805,0x87ae62c5720dab812bdacba66cc24839440048d1 -7047,0x87b4be9115592ab0805cf90c1a32b20ae2a0168a -7531,0x882e4a84cbd9b55052c85e1ff05289aa577659b9 -6818,0x883a0e7b329df75476d9378462522cf2f78fab3d -7253,0x885b3ff84a0fe82eb97a23093421504e42cc8f74 -6653,0x88b7bd7a245f2fb597de88a6eda0caee047f607a -7686,0x88c8316e5cccce2e27e5bfcdac99f1251246196a -6940,0x8943f8b7d4c2db7417add9b6579eac5f8da43f95 -7619,0x89698dc9ecd95337ad64fda7df773da5007926a8 -7392,0x89ac2138e70a13111123932dfaf100e41361c2c6 -7754,0x89f16bffd72166807a18faba307cd21ec6143563 -7527,0x89fe4828bf24cafbe96f804885506a7111065541 -7215,0x8a5609a110181839599064bf6be25ddf4cb73356 -7750,0x8a6ef192d45e8ea930bc7f4f0ed457f213731bbb -7031,0x8a73882e496dfa575a9e50a82aeb73fec4b68ac0 -7548,0x8aaf3d9a538ee891282ee338811281111feccdfd -7001,0x8ab13ca3b6591554a086b7ad2a012d25c3efd704 -7449,0x8b2f7ae8ca8ee8428b6d76de88326bb413db2766 -6990,0x8b55cea4692cd05c7fd9af204a27bc2e6505c89c -7360,0x8b9b5f94aac2316f048025b3cbe442386e85984b -6456,0x8bc8d8dfaef57e531955880975071e438481c80f -7072,0x8be1893e1d29fcf2b966cdd083efe30dbf78d7f6 -6711,0x8c14db69b1778c7bbb0683b2dea21f79b9b5f059 -6869,0x8c2c26494eae20a8a22f94ed5fa4b104fad6bcca -7667,0x8cc84e2c8be0b7999b65a6b2bcef4dff1f433ce1 -6872,0x8d08a8a066e9606f854a3c68fcc730e406319996 -6935,0x8d1201b074eef198d5e6957f4574e2e4fc5ba9cf -7555,0x8d51bf0759e1a01c15f91940baaad08b6b45a637 -7216,0x8e1b638ef5e796504be87fb81943e613875ecabe -7252,0x8e9757479d5ad4e7f9d951b60d39f5220b893d6c -7370,0x8f1c7653e0d29cdc1a1f1220eecc7db1bb82cdc2 -7015,0x8f765028e9701462c07ef83312cffd3a5b9a6652 -6889,0x8f7b21bf5f8490faa63386f6f6434c6ae8d8a120 -7637,0x8f9e23d4766f6584553f72a1a9b76ca7059483fe -7309,0x8ffebd461a98b4c3743e918ae9b1cba3b7afca01 -7500,0x90241392dba7faa1d1d96c6927f0fbfe93fdf226 -6888,0x90276ba2ac35d2be30588b5019cf257f80b89e71 -7094,0x9053e51047ba7f0141f3d1f23ac7ec6861bf9fba -6460,0x9074b2389baac5ea4fa9d1b1d37589142888697f -7313,0x9099a250b5583235f7bb5d9adf1eaf97ff20ee5c -6482,0x909c690556d8389aea348377eb27decfb1b27d29 -6800,0x90efaafec5b183d09bc5b2ce81e8a12e4c2a6002 -7378,0x913bd76f7e1572cc8278cef2d6b06e2140ca9ce2 -7240,0x918ec8901cba19a42c736047229adcfd1f826613 -6540,0x91a480bf2518c037e644fe70f207e66fdaa4d948 -6941,0x91a4f7125a6f7ec14c41d39f0ac681e8e387da1c -7571,0x91cc4a83d026e5171525afcaed020123a653c2c9 -7131,0x91dbc6f587d043fefbaad050ab48696b30f13d89 -7499,0x926b1148dafe298ff7fdc2d01ae1bc3fa3b4fae4 -7023,0x9306303a8f77da1858e966f08aa0ffdf42bab71c -7393,0x9363c080ca0b16ead12fd33aac65c8d0214e9d6d -7020,0x937c9e1d18beb4f8e1bcb0dd7a612ca6012517a3 -7311,0x939313420a85ab8f21b8c2fe15b60528f34e0d63 -7244,0x939d077817dfb5a6f87c89c4792842ce91c7e5a0 -7024,0x9462268aa06a787c885b7c412b3f348b034a1468 -7650,0x948105487eb1e72a36b080783d5bddc8c0b921cd -7731,0x949404d0ac66430842145204fb83c1ab9c21f35b -6667,0x94a1d572bae06a31c029d4c26e1fa705f54286ef -7249,0x94cf2bb95bdc525f90311ddb2edcd1c481e81a9d -6870,0x95366ac32995d4c31f72c2d0029211e19bf6eb7d -7266,0x95597eac456983645d4ad0cc83c5356cc245ea0d -7705,0x9576b1104c0fa29f76b3559b77e0fd0a6b450213 -6496,0x9597494f1675f1d62edb03dee40d84828ef5b295 -7567,0x959a4309f7d82e3d34c353a405200d8277032115 -7678,0x95cab224896972e69ad626bc75d29116ce7d4521 -6661,0x9602af2eb7efc3ca2625064d0f7d6446e9380564 -6646,0x9615b6bfff240c44d3e33d0cd9a11f563a2e8d8b -7688,0x9651de67fd8c3003a56b25dc24c73c317f00251d -7291,0x96690aae7cb7c4a9b5be5695e94d72827decc33f -7060,0x967b6d2d754839d34000a74f0d086ac9d3afa877 -7729,0x96f2842007021a4c5f06bcc72961701d66ff8465 -6784,0x973de36bb8022942e2658d5d129cbddcf105a470 -7069,0x9779d71945310e75761240e8b9fd5752c6d63155 -7042,0x978043f67fa1b8198fa965907e31ab435bff4b69 -7477,0x978d4b5438d3e4edf4f03682e5a53b48e56604c5 -6481,0x9821cc43096b3f35744423c9b029854064dfe9ab -7551,0x982bb9880295ecbc34a56772fef81e964aee4a9f -6995,0x989d359dbf9c531ae6c305c37ac37220b8dd99eb -7416,0x98ccbc721cc05e28a125943d69039b39be6a21e9 -7485,0x98d601e04527a0acbb603bad845d9b7b8840de1c -7035,0x9956c5019a24fbd5b506ad070b771577bac5c343 -6665,0x995f710838a070d43d94d4420194bb09cfc332e6 -7435,0x99925c021424cc144a7420599d6e9f70f725b5cc -6907,0x99ae21feb485a89252b9bfb394e6eb8c736436bd -7685,0x99b2b4f1c6222d97e70cf7d0c7cc314465ca9dc0 -7058,0x99eaad674e8c74c26fbfc7c4bb6bff3458c880e6 -6943,0x9a05f2f00ca385ba7d4368737ec1485384ca8874 -6887,0x9a3ecffd8cb317685ef0df7b4af5c4c9d1af53ec -7141,0x9a591786c198d31693858f66d3636b1d328c794f -6909,0x9a69d610f2d970657da3820c0be9b2ad7d9f5d2b -7056,0x9a90c6fa8828bacd5b9edd513f77ba7e4528c7e8 -7075,0x9ab1f9b25b312f162e815a027b4805516401093e -6642,0x9b0071d4ee4a078dbdd5100799d8ea700a6da709 -6946,0x9b1c2055465e1f1aaea14d68fc688175cf46cdfe -6981,0x9b20476899c8fb22d96af37d2017016a80647159 -7733,0x9baedd40fae33ce9022d39a9bd71f325e626a06e -6989,0x9bb2feab8fde79e48234c2d3ea3f2568d08c42e2 -7648,0x9bd1c7dbb973e289a27b1529d8931931a26b6244 -7432,0x9bdc86302479112e61f3ba7761dc704f7044d304 -7102,0x9c1595dec4f58a106a5cda2345c7b711db412ee0 -7603,0x9c3a582cb712c8cdb067456b993f0234edb185bd -6892,0x9c79c19ff4d1a6531d9a4f91e96534f5d002df8b -7367,0x9cd7ccac32231ccb842f14368b3da3b33f20d3be -6664,0x9ceddb4fd222fff6f42ac69004e132e57de810cc -7226,0x9d6db4d562d670a987b5d9e07db71bab33eb1428 -7748,0x9d81f2898127f812751dc09c210d839a7db651aa -7222,0x9de146b5663b82f44e5052dede2aa3fd4cbcdc99 -6976,0x9e3c1f1f5868701a4d8caa58d61df2f74aefe926 -7343,0x9f1c2f0071bc3b31447aeda9fa3a68d651eb4632 -6591,0x9f231dbe53d460f359b2b8cc47574493caa5b7bf -7492,0x9f3be6be18e8d0613f87c86a0b1875b74f404a11 -6716,0x9f564ffb60945dec03fb8ddef491465dd9b4c9fb -7745,0x9f6aa1c141838df56ef82be286cabd2616c8b309 -6577,0x9f77af45f13738be8d3bb772a86310f4e452afd4 -7446,0x9f871fc8e2a9aa09b6181eb366f218aa860f6967 -6883,0xa02dff4728b8dff96c146dc5ee4e76cf2cb03c21 -6702,0xa1245e5b52555e40aa8a3ffb95c5c426c7c7ef12 -7008,0xa14849ad93999d8b184a1d61fba7b4ffbd24f96c -7489,0xa154ecfffb46380991751de890956b245142e10c -7012,0xa198d00dc35f8367beb48f27de9032c8209dc22c -6809,0xa1ace9ce6862e865937939005b1a6c5ac938a11f -7391,0xa1c26b1ff002993dd1fd43c0f662c5d93cc5b66e -6452,0xa1dea0c759eb44e9ad9697e3c50e8df8d19a402e -6949,0xa1eb81bfe607c84b3332d16f0d4ac54d95d9b7d2 -6469,0xa227c833786e46d53cf9e9a14cff906477335d91 -7357,0xa23be6f7effe9ea7da3ac3452e2740ddf26cfa20 -6748,0xa2412e0654cdd40f5677aaad1a0c572e75df246c -7624,0xa26677434188db65e42dd5606ee297366361cc2d -6830,0xa26c97a0c9788e937986ee6276f3762c20c06ef5 -7154,0xa2c0843c8cb9f29fa40fc0ffd2b4995a0f05c15c -6609,0xa31717145c27bb37868829e92a3601014768d145 -6788,0xa3a538ea5d5838dc32dde15946ccd74bdd5652ff -7231,0xa408d8e01c8e084b67559226c5b55d6f0b7074e2 -6483,0xa4861730dc861e152612889d8bbb819753c9b8f7 -6495,0xa4cd6007e102e6ecbc0d85b20c2dd6a1ce57eedd -7606,0xa4d53f6cbcf314f318f316ae61cdd400c974085d -7544,0xa552049a9f320b6a8622a003fe1108595061e223 -6528,0xa5baff7d9928ae32c5e1ff441e4575951cb01111 -7636,0xa5d9cea9d8d040efdf9f7b575a7cf4854e4645b9 -7532,0xa5e9aeb6ff1c2c0f45447b464e040936d9ad5874 -6937,0xa5ed89b0030237dcf5f667558d6c4f8098cb72bd -6638,0xa664fca1879c9ca2e6cfed49c3c855352016e4c5 -6928,0xa6808a69b3ca50cb2e4a41a51f8979ffab2d2db2 -6471,0xa6bc30d854c2647574921c4af442008db7d32ad5 -6771,0xa6da295bfda578ce0e0793bacd7c31a3997c92ed -7382,0xa71e06546f0278da6c4732e8b885378fc0781fe8 -7676,0xa7912822c220cda3596cabfe9077769576e2b46e -7646,0xa87e0bb82f725904cb77231a0f0213da8a004b75 -6477,0xa8f3e8b0efafd309a214f5ab851ab9fd14719083 -7298,0xa92c0142c3239e8e07cbb9f33e4c0dd4fc19089b -7582,0xa952150ec68cc9c75fd14f837c297b48de7df6e8 -7409,0xa95e89d93a7432a2ce9453ec4e264940a96b364b -7627,0xa9bb96d03a6ed56c14cd47a2ce75fa6a7633aec2 -6715,0xaa94c874b91ef16c8b56a1c5b2f34e39366bd484 -6612,0xab0d9e3ff817faa88f4bbc783cfa6d55e5f10f3e -6491,0xab2a00f397bb86206c991d1d5b5a76dd6ffdb4fc -7002,0xab4c5e5be3e0609f5f5490621f61edf377949370 -7230,0xab5745e6cd602742636491409e18a73e223c8aff -6619,0xaba508b0f09fb7ff9a3fe129c09bc87a00b0ddec -7018,0xac3f9a2753f185731324907e6802395d59bb62a2 -7422,0xacca6760470a0495e7dfc20fa6c2c9eba71db4cd -7589,0xacec9a35bd9dd1a0d05140ff73ec944e8ebf3e0a -7484,0xad35498d97f3b1a0b99de42da7ad81c91156ba77 -7336,0xad44873632840144ffc97b2d1de716f6e2cf0366 -6614,0xada71f37437514c428f0d3eba4633e45be101bc7 -6607,0xadad43be81e2206f6d1af4299ca2a029e16af7ab -6973,0xae3d1b55cbffc11693257fb5dc41de32f1e9ec7a -7317,0xae55f163337a2a46733aa66da9f35299f9a46e9e -6549,0xaeb1cc5fd71cffd1462808ee13b1051196bf6224 -7577,0xaeb7205c613c1e1ad34e077ebe86fdb9da16cecf -7607,0xaf2e4c337b038eafa1de23b44c163d0008e49ead -6787,0xaf476d7817105437aed79a86e802b79d4b1c473f -7673,0xaf621161755c601c1469e3487ce971f39ae507bc -6484,0xaf89069dc03d65c1b3091d770a8d3b4d02126849 -7128,0xaf918f4a72bc34e59dfaf65866fec87947f1f590 -6886,0xafdb40816c47321d6ea841d8a912757441659799 -7659,0xafff86843b3f85fe4960f1913fd62e6780183d2d -6625,0xb009ac48f600f13d580395adb841bb733032b222 -7116,0xb0a058c7781f6eca709d4b469fcc522a6fa38e60 -6572,0xb0a1d2c68bf4d0980402dc220ca6ddeb8dbfbc56 -6603,0xb0b6b79fbb09290b0663d6d767ffcee7ea742428 -7442,0xb0ccb22ffb5c5a649618b18bdb53c806711560e6 -7151,0xb11218cf850339c9e044d8c935c59e9fa4b0d239 -7510,0xb1204d3fffeaea354f2f04779bedcd0d8f3598fa -7352,0xb134dcfae521b08438e82faead92910b1c4cffb8 -6971,0xb147c69bee211f57290a6cde9d1babfd0dcf3ea3 -6580,0xb153f06077af9448c53c7102760e59836a27ed27 -7478,0xb16a8b06318c78c274f3bbc5cc5c9191b0d0c1a3 -6640,0xb16ef128b11e457afa07b09fce52a01f5b05a937 -6824,0xb17d51df8e3a134e8304f682be38e5204e4672e4 -7085,0xb1ad6dd82d086f31d2143080b0064abc44cf2b5b -7415,0xb1ca7471eff11a0371a5dc06d4c8aa6afc03a6d1 -7385,0xb1f9bcc7048ece52cb3efb2664091d87654e13b7 -7519,0xb241af12256998a0051b93e02027e73ca7e5388d -7388,0xb25529266d9677e9171beaf333a0dea506c5f99a -6951,0xb2b42b231c68cbb0b4bf2ffebf57782fd97d3da4 -6987,0xb2c864b3377063d7b56fdea5474503905b8b5732 -7434,0xb2e26d988a5557b571bf9833a8a60d25799293e1 -7749,0xb2e9642f96a1b576ab0232ec35cb0d7d07d1172f -7124,0xb30a108444e9000408421981a778b7358df2f737 -7048,0xb30f9aa978965cccfe2887d93d94519e95c2efcd -7700,0xb39a60579ccb5de0309114eb16e14e6c4f7f752e -7068,0xb3e43a85cc717fbfa7a1ba4332b112fc2c81a753 -6446,0xb4437efd22b4cce7e25b3c47a469bc719cbdb60c -7157,0xb474425297945da2f38423cdab98f63860412f14 -7341,0xb4b26c5ef40d72b8b1d0acd9e0c84d00d4a30b74 -7626,0xb4d55ae3a6b3b73633f622ef89e94e4bad05c08f -7365,0xb525dd69a58e91f115eae31de83dae95490dda6e -7657,0xb578e90f9ff992439d66e4e5349d63f80df61336 -7205,0xb589af3f2e3377a9a57da74be1b6598926479505 -6637,0xb58fc194b314467f079ff39d7bc5fd666a6475b1 -7206,0xb6059bc836f849540903724a0eeff9a620660ae1 -7067,0xb63bef5ccc5e9316961cdcd54129743ae8455bc4 -7348,0xb695da2b0d96ae1a3fbf0e51080ea592da330656 -7586,0xb6ae8f141e0f83fa8d4f28cd79d342e6175c0be7 -6852,0xb6d5ffb2df19f197021d361e020a30f5612867f6 -7395,0xb6e1bab2e6349942ab472ef5c9456019b85dd5c7 -7487,0xb7059ed9950f2d9fdc0155fc0d79e63d4441e806 -7390,0xb750e008de9139c85978956470cbd257daee3797 -7325,0xb7589b765414864288c51f885bbdc828c70a8344 -7148,0xb766e4f63da917d3d289b1d52ba5ac3829e7c679 -7242,0xb7c04aa1e49bbef0a5d7a0e7885b54c9e9309704 -7543,0xb7d7605f814da681cc299a9ac9abbfc1ea65e40a -7679,0xb815eb8d3a9da3eddd926225c0fbd3a566e8c749 -6986,0xb88d67d7af971a5b459c442f0ba2dd3ec56cb3da -7028,0xb894ce0a059f970c7d3fe47d10a2d012a6a4ccfa -7723,0xb8bc48ed3d08a3ac02d62174652369d3279705de -7502,0xb919ed11d5e73af41fabbaeb14e617516073229b -6978,0xb9202087852298e5e2ebc0c30758fa4e07faf6fb -7651,0xb93191110bcec5f50dde0ff73f15b9a0285a1ebb -6541,0xb97199da44789719fe37949459b5f8a04811c771 -7718,0xb997edbe18f7e8a9904ec7e6a945f2940de5193d -7294,0xb9abece4fcd56e2e3ee63fc9f60dc8a825379193 -7107,0xb9c62720715763fbfb941b853d39a2a60d5ae66b -7173,0xbaa74e2a1b7f3c827ab096e6d26c6c698704630f -7493,0xbaacf0f9dea6b17d8667a34b7f69f60591c8d68d -6616,0xbb1250d0d96a22cf62ee12aecc2fa684f3ca04e8 -6822,0xbb16c7b3244dfa1a6bf83fcce3ee4560837763cd -7243,0xbb3c8b897de68d645bc15238f9c272bc713d4569 -7169,0xbbdd8ecebbd692a17f9fc387bf3a9ed1d61b263e -7219,0xbc12131c93da011b2844fa76c373a8cf5b0db4b5 -7554,0xbc5b0a6dcadd4fc27665601401d6f03d97375b24 -7178,0xbcb2d435045e16b059b2130b28be70b5ca47bfe5 -6462,0xbcb72236217305fbaa97b68f87924c0afe80f707 -7010,0xbcc45c85dfd8065f0b7ea8b64a819efa1d4e4e17 -6472,0xbccac7cbc37aa2de510863647197d584f55ae46b -7073,0xbdce23979713fe8efac51273a300c8f76a563362 -6560,0xbdd0d09f73ac6f8ef59a71baab283c12dcab06fa -6982,0xbde620d3bffddccf3d0b35a36764f190b67e7e80 -7066,0xbdf46e4905e79e4728ff15ff7b8c2d1ac9a6cd0a -7605,0xbe0f35e3d0ffe514969333b4d07a279d3d66a494 -6924,0xbe897831ee9e19d83604612f205351629f2df30c -6742,0xbf0c6fe7edce3e8ccc5c5c5e32e59d37fbbfee9f -6556,0xbf1e06facb51b8a9223f266f303a88d3dfc46226 -6896,0xbf3b13f155070a61156f261b26d0eb06f629c2e6 -6442,0xbf7851da721417a1e61694c3f4bdc8dd62a0c03b -6589,0xc0204f82ce1e25d15d2ce8dc1dea3f319bb64a8f -7095,0xc02af29944301c8fba606a7df8ef446dc103238c -7320,0xc04fc6e37c1071bb5d041744d7469cc45d46b29a -7732,0xc0929a879906af158b63230bc7a60144f2e26839 -6739,0xc15fd9baa87eb0289471278ea3995da3756c4c64 -7158,0xc18f85a6dd3bcd0516a1ca08d3b1f0a4e191a2c4 -7201,0xc19d27d1da572d582723c1745650e51ac4fc877f -6793,0xc203a12f298ce73e44f7d45a4f59a43dbffe204d -7347,0xc20e63ab2f1303079a6cc31013534e3989cfc8dc -7542,0xc2231d2caddbee015aedddd3f0ee3874e3bd1d59 -6980,0xc254f4a4bc16218ed30a3d674d8fae3f25b6af5d -7065,0xc2a7111a964484bc6dcca2e6cb7f865ec518cba2 -6455,0xc2aa78b6d0d2a72557edd79845b43bee4ea4dd10 -6673,0xc2c71156d9dde42f412e83a3e169283cd70f3e9d -7196,0xc2c927483bca62fa16babdd66b07cd99379edcff -7084,0xc314cfb9c5706f316f3916543ad2f3e4c3aa2d02 -6727,0xc3304b432a16f68ae449730a121662199a868cbf -6867,0xc37c47c55d894443493c1e2e615f4f9f4b8fdea4 -6520,0xc3afca86cd661e1c7cb9865940702eaffc83a5dd -6905,0xc4055492defd5df758aa8c736928c7628cf481f5 -7644,0xc4355ff53ff1ba4a2d7105db794d6c3510b8f4b3 -7162,0xc4786c4484c06bd463f124ebef2175f67759ea1a -6663,0xc4aa1e691d6780b0374f8c1a5252050dec450322 -6918,0xc4be4583bc0307c56cf301975b2b2b1e5f95fcb2 -6910,0xc4fe91f7612ffc7b8dd15be5a3a4c2b8169e56bd -7195,0xc51298dc7b96f4ecaef7f7ba68803bb0e90ea229 -6965,0xc51aedbec3acd26650a7e85b6909e8aec4d0f19e -7187,0xc51f982f3c1866ed502b09d2e081c4fbf280ff1f -7115,0xc564040630d6929070d85df237fdf60f6bfe4b5f -6840,0xc56a0198c08c3610980340660c39f6d6c7ea765a -7156,0xc59b024cdffe353f1e2eb1a69e48fc3acef4f942 -7059,0xc5ae1eca0afc915f88c0135cdaaf270d710f03ff -7672,0xc645a757dd81c69641e010add2da894b4b7bc921 -7447,0xc66499ace3b6c6a30c784be5511e8d338d543913 -6553,0xc66a263f2c7c1af0bd70c6ca4bff5936f3d6ef9f -6623,0xc689bab7faa9d2bbec41c7f741f04fe2507c446c -7038,0xc703b8d6a2f6c9f9fd3b92bc38f3ed716461d943 -6532,0xc704c9aa89d1ca60f67b3075d05fbb92b3b00b3b -6488,0xc746bc860781dc90bbfcd381d6a058dc16357f8d -7610,0xc765dca9d0e77b01b2bb74febae2cf89e038092b -6853,0xc77976ce3c1d66b4c4afc8a4cc8d2e25727cc29d -7634,0xc7a869deeee1acab2b30b6022c246194c83ed49c -7147,0xc809eeb451a53f8036af14add3c9ca67b5d4b5dc -6568,0xc818f3a73f0f4a9afdc9835be2682e627a04a4ff -6706,0xc81e1aade8a18a1db142ec68d6efe9c65dfc255c -7575,0xc81e43b6fb257760cb655c5b3ea0b87d93cf01b5 -6468,0xc8590aeb1484e642f7321bd0586b6db3165a862c -7440,0xc8fcd6fb4d15dd7c455373297def375a08942ece -6499,0xc901ee820ebead13af91be203f97f437ab654b9c -6501,0xc98bd2c0730a3ec81e1db287bf6c6b5b5a7c2a82 -7299,0xc98e2439711edf1ab1e8bd23133145c3e1287f94 -6721,0xc9b3d8c38ce75efd4a1b39880c86f4ab71a056b6 -7529,0xc9c64cf6d1ce4b41d087f08edaa9de23262f1eda -6722,0xca3af1757f1ce97956c289ccc6b171a14acd85ca -7697,0xcad243fa79de8acb3b0336dd9793a16d8e6a3aa5 -6593,0xcadd53c5faae5111c7a0429ee1f99a695433fb17 -7652,0xcb38b655537caded39f10074e6159a4da131f77f -6842,0xcb9208c6091df778bf405f3938b6a6bef427e37e -6919,0xcbf25513c912eeb8072972347afd2be94452b5e6 -6992,0xcc02f000b0aa8a0efc2b55c9cf2305fb3531cca1 -6705,0xcc0cec53572d4e10a8fdabb468356287b170c6a9 -6794,0xcc60342649c58a9d5a5293030ccbc230b1231127 -7562,0xcce2c84c91e6c4de7e87704b3d5c4fba10626234 -7570,0xccff378f3aa9e4abb36fdcb03371a59008c28708 -6759,0xcd077eab8efbcb94aa04f2055d7a9216f23697a6 -6808,0xce16ad760dd739f9ebfa2ee6c5d47bc85c9ba3e7 -7660,0xce52ea37a14cb6da29407de69c313c452bbcb31e -6551,0xce8bcb110101d6cde21d6c76bd799261385aa950 -7547,0xcee08cc3bb744dec69f6eecf5b7b62ccd9143e99 -6916,0xceec52eae64958a7f469d2cf919102bd198092dc -7143,0xcf24d8db427325011d5cd473fef1a8b056fef8a9 -7092,0xcf2e165d2359e3c4dff1e10ec40dbb5a745223a9 -7011,0xcf33a35f0f2095abdd0c81dbde3a1cd37be0c5cc -6658,0xcf4a5f99902887d6cf5a2271cc1f54b5c2321e29 -6828,0xcf853f7f8f78b2b801095b66f8ba9c5f04db1640 -6712,0xcfdc039bdb8e4b578857b759f27d6baa2617edd3 -6766,0xcfdccff3835eb002ef0360f9514a66e6717fcc54 -6738,0xcfe154521ab1fc995a6bc68880af9888221fd27c -7632,0xd01a18c2edb9f411a8329ef9b2905f3cf7d35408 -7184,0xd0289eb86818021f3df4989380eb95a3fd2fdc1d -6891,0xd05074aa55b295ec8664290e6a4d4104d92a646b -7273,0xd056351fc32b5ec443305198fb9093057f8e988c -7437,0xd0a9dfd6e28628659977d8c330e383e885b97c79 -6984,0xd0dc005d31c2979cc0d38718e23c82d1a50004c0 -7746,0xd0de1cc5849e07c03d1d7a2589a8d56d092dfeaf -7474,0xd10cd91683301c8c15eda40f59e73d1b0bcfecdd -7150,0xd12fc7a381bffbc05c709472097a0a4e9cd8d410 -6782,0xd1599e478cc818afa42a4839a6c665d9279c3e50 -6961,0xd1b73c8251acf068ea03a42177bd1e3f610f9fb4 -7421,0xd1c8e9b404053560bc6a0302c4395ffb0ffd1c79 -7404,0xd1f7d9022da0d581986019ebc96babdbd3c72c27 -7669,0xd21257d00e06621b1946532a2410db1aba75c638 -7620,0xd21f7cc02f3a9b6d059cdad6e0c0f4db18420189 -6750,0xd2471115be883ea7a32907d78062c323a5e85593 -7100,0xd275704ee35d4fb55bcc737347546b7f603569e6 -6904,0xd2b4c82b822e48927ac8c94ecdc249be191ae8ce -7369,0xd2bfbe0ee9d7c553f00cfb44c2460be5a18438e3 -7373,0xd2c77e2a3a52bb84c192a72a763609d99d39f750 -7019,0xd2d3ace5c69b0460ec202f83dafce14766f6ef74 -6756,0xd30bdfd7e7a65fe109d5de1d4e95f3b800fb7463 -7380,0xd325b17d5c9c3f2b6853a760afcf81945b0184d3 -6836,0xd3699d43d36237fab9ddc7dce998613ebaf2bd44 -7224,0xd3739a5f06747e148e716dcb7147b9ba15b70fcc -7314,0xd3cabfd534f611b3f0ca27ba24b4fa44f8d38b18 -6746,0xd3d59d4b8b504ea6e49c5788fe1bc7505c6f61ea -6996,0xd3f1bddc7f25eafdb939d6b4f62cb9d5b19d346f -7323,0xd442dc2ac1f3ca1c86c8329246e47ca0c91d0471 -7534,0xd4b10c896d82b3158a9a9fcb8f6fbc5a8d833c04 -6707,0xd4db55cf39c37beaa3a47f2555d57b4ea2d9ff39 -7545,0xd53c50b644aa4e29fe2b633e97187e2aa3cbd6fc -6461,0xd580843212378f018eb6022cb2790654fc2c8931 -6726,0xd5aeef4a0ec3da6b2326194b775dbd5bb068afc0 -7328,0xd5bface61e64aa4f3326f365dc6ab3e78a28357f -7129,0xd5ea02e6324693bacbde3e63a3c72e5a227a4ddb -7708,0xd5faaa459e5b3c118fd85fc0fd67f56310b1618d -6988,0xd5fbf7136b86021ef9d0be5d798f948dce9c0dea -7473,0xd5fccd43205cef11fbaf9b38df15adbe1b186869 -7683,0xd60e490fbf42a43e67f1e8d74debd7bcb5240f80 -7361,0xd62adef80341e0271031711c14a8e7263d196c1f -7438,0xd681cf2419bb3f85732412164b6542843e9a64a5 -6926,0xd6a06a8c73265e471639bd953d24832bbcd548fd -6950,0xd6fe35b896fae8b22aa6e47be2752cf87eb1fcac -7587,0xd78d6bb9fea34fe1c957b8847e2b2534cf0270f0 -7758,0xd846707eac1760871b024c335e480c5ec471b9b4 -7730,0xd856b45d4d9671482e53e705058af3ff09000a28 -7372,0xd86e7b02cbacb63ef6e91915205f7b122b8a8c3a -7536,0xd8ad261b4dea3ce25642d909d2f3cbf9d4632caf -7144,0xd8c3f087000dc990fc84a776aed78d31507f716b -7403,0xd91db82733987513286b81e7115091d96730b62a -7558,0xd9ac5ecbb704f0bdb0a96bebfa3b79be829d2bc1 -7022,0xd9e7d2cc5ef478c8e6535d1f416f0c3ad477b78e -6671,0xda3a5e9502b23eaedc8cc048998893013e09787d -6998,0xdaa88c67eba3a95715d678557a4f42e26cd01f1a -6720,0xdab70a965c0ad7ed15b8ea12d5c319e06c461240 -6811,0xdac38ee1e765712c5b9290676e44e4654a3dde2f -6562,0xdacbfd99bb915739b58ac9312c78a23acbacb6db -7625,0xdaf440cdea843762c6d4ecfa7c2f64aed832319e -6994,0xdb03876d8fc29bfa655f7c6d7d68ad243e3b5cf8 -7633,0xdb87f699ae4045c290033240f22c0cbe80d95724 -7272,0xdb89f3fc45a707dd49781495f77f8ae69bf5ca6e -7713,0xdc7a51f5c32909acd5d03d11944c4480bee1cd47 -6656,0xdcb4ec61fee47955462fd1f3fa89a6f32cc6a800 -7189,0xdcb8438c979fa030581314e5a5df42bbfed744a0 -7628,0xdccda0cfbee25b33ff4ccca64467e89512511bf6 -7209,0xdcf2d2c4949d6358bf05cfd88dae83276cf7552b -7159,0xdcf323f27c990259eb07a6abfbe9305fbe914416 -7699,0xdd1122620688c25df27dd405cf467cc9e5c0cc82 -7271,0xdd28261fc65c4ed29b9d11aac0f44079cfca4f32 -7112,0xdd87cbde3c1f8f728c7924c8c9c983af6dfcfea8 -6736,0xddeb6cb0d6050c9221d037aa099a2d11c443548a -7427,0xde48d4b3b8737446193720ce23ef24f922341155 -6764,0xde5d4221ddb1c9f69af4df34c27de91e31bde64b -7508,0xdebc936c5adfd1331e5fa4ae76db7197283342d0 -7213,0xdedb0b04aff1525bb4b6167f00e61601690c1ff2 -6710,0xdeffaa86edbee913067529807c17264378932526 -7344,0xdf1f1f0059ba70c182471467d3017511b1a122e8 -6554,0xdf89210b03e319b08ec0cc73c4b50468306e1252 -6685,0xdfa2d3a0d32f870d87f8a0d7aa6b9cdeb7bc5adb -6687,0xdfa823f131baa72334edab7c4e163a65024c6b3c -6659,0xdfaebe6a2b2bdac52ac2e172e0175988d7472c60 -7439,0xdfc58348e570739bc6a911d0426fa8072bc0237c -7180,0xdfe62e1f083927b5fbccd9d73e5b59644542d550 -7433,0xdff014d1c4707dac5c035e68abb072fc5edd1d4c -7161,0xe0196c910c9b5fd143deb91510c55817d6faad1f -7381,0xe052f0e650c3a11057037e285b6cbc8a8a144a9d -6633,0xe05637e338b11640c51766304878b08181463413 -7479,0xe082bd0ac75c78196f312150a298ade27aebecb0 -7351,0xe0c39e7b896e7fd1f9fd221079502bda23e81115 -7256,0xe0d10647d92749da9bd5c250df2eef2e4110a8c3 -7509,0xe0d1a14ebc3bc4460feeb67a45c8198063ccc7c7 -7174,0xe0d8c2b43aa7769a22d6c36f8e382b72d4f7ec20 -7569,0xe1264b2b97be89755fbce7a280fd276c55f661d1 -6574,0xe152a2dbce62e6c0bd387ffd1bb8086f44c5fd04 -6898,0xe24478ed6c1d2c33fa620a26612d4b1341ffa02d -6857,0xe25d751e29b5997878e92120ca347f4129549ad8 -6929,0xe28b785ecc2cb5fe949f5f31a5aec3989f764838 -6544,0xe2e4edae34f2c1e4e42f7c0c8ce500c56aea4326 -7394,0xe340ee33a153960b449a1a21acca8a411812688d -6666,0xe343542366a9f3af56acc6d68154cfaf23efeba6 -7584,0xe36123d7b98994e266d723b2806e0c6960364d35 -7406,0xe37858391bc66b1b8838a7459e59a802642284fa -6876,0xe43d17f87cfc6d2f27c5d99b1dbaf9151dc40920 -6464,0xe44179cb9623c3bb7a9bcf54b4b1dd5ad8c672e3 -6866,0xe46ef097d2cf6ff95ad172d5da0e65a0de9e2468 -7097,0xe4bc6fce51de6198ac13edb55e60b5ca67a4d7c9 -6968,0xe4d25c54759ecd13c10e6c64ab9127c6408f9cff -6683,0xe4e78fbf545cdaf1fa7be1c47653bfd5da01d1eb -6504,0xe504283e91ea5649486c603ebfe0cc32039843dc -6911,0xe507ad5146ab13f18f853f214e56ca41b961d7db -6573,0xe52757a0d7eb6c380b6f6ae2a30a91b67ff16b9c -7643,0xe55bb97939378587ab5b7074d21639352d84d06b -7332,0xe584a05fbe4cafe8335caf16068d60b721fa328a -7145,0xe5a3b74c9e9c21004d75fb082a5e6eaef831fc93 -6559,0xe662e0af5fcabe78aaaf4cff0a13ca69512fc481 -7139,0xe6ac817cfdd83073aec079cd9ebb9c35479b7665 -7568,0xe6bf793b3ed4b42f8c3fb883a60e49f976a1791e -7698,0xe72f5c2b7c8e8697affe886497d22ad47d832085 -7098,0xe74d262c3cba40507f6174d6dd4faa4877a0416a -6765,0xe76df4d2554c74b746c5a1df8eaa4ea8f657916d -7306,0xe7879879aa86587231d0e6142016913b0cab60ef -7691,0xe7b44e0411307b637a1b3b75af8c37d752857ae1 -6662,0xe7c25f3e803c7eb5a08d0332d0c28417241d5462 -7264,0xe7f562930b38a081516d5759aaf168c65f028f25 -6695,0xe8bf4bdee20c8d66ec61dce034fbb6ee2d4834fe -6524,0xe8c41be1a167314abaf2423b72bf8da826943ffd -7666,0xe94afded9cb9ab143e8ccc8c7439794e8c41f1a6 -7675,0xe96097de5d8d168fc46b52874df0a474e4a293d6 -6882,0xe99db61288a4e8968ee58c03cc142c6ddb500598 -6678,0xe9c7b5c2bf5504922dd2f5b47529806134b4a865 -7179,0xe9dc2c1008b1322c21c1fb45cd101f1b7a0c9f73 -6454,0xe9dcea0136fefc76c4e639ec60cce70482e2acf7 -7043,0xe9e46a7323d54af1550b931c8bd6f8615f079379 -7753,0xea0324cc8d9fd70b8000bafbac7e3ff7c15275ed -6517,0xea46a4dfa7d2767ff4bae2b76f5c6bd80057c723 -6521,0xea53a19b50c51881c0734a7169fe9c6e44a09cf9 -6977,0xea91d55df3d58236e4dd8ab14ee7cc4129b696a4 -7339,0xeaacada91015c11d43bd788f09b2d54decfdc2e8 -7736,0xead0f3bbe7f2dcc0514306b29a4f96ab694d1f65 -7621,0xeaf0191bca9dd417202cef2b18b7515abff1e196 -7702,0xeafa65b829b37277a14fe43de9fada0d9e897e4d -7076,0xeb4b5abce7310855319440d936cd3add77dfa193 -6724,0xeb66fc1bfdf3284cb0ca1de57149dcf3cefa5453 -7560,0xebbeb518d4b4e488f45913a9ef72ea57be676f1a -6983,0xebf81425e81e2a879c139db3b3891b0a635dcf7f -6969,0xec1d184a5baa9ad1362ef99cd72b482b9ddc2a14 -7681,0xecafbac3221160337a3a8dcb25a955f50618feeb -6492,0xecc8a6af92d825acc5b871993fc83d86cced5a19 -7704,0xece9613025f970f1d56b2ba749e1170976465088 -6900,0xed20040b21fdf6d68336f6e61ad05f235e3ea9f3 -7036,0xed4e7ac8a5fb70437a203678ca83fb8b26e9f6c5 -7425,0xed7947e253f84f7932278284a6b62387ee2eeedd -7601,0xedb8f5e51e5b11e73bea72600aa2de7a4a2eafa4 -7592,0xee526bb5b07cf446b4fbd93344de65a093b0ccc2 -6723,0xee8804d8ad10b0c3ad1bd57ac3737242ad24bb95 -7278,0xeeaefe31d7ce33df2b1c7682ee0152929d2f4b86 -6913,0xeed3618dd59163cc6849758f07fa9369823aa710 -6534,0xeee0879183835875ab10f9fcee09e1c4a90b25d5 -7520,0xeef768df33e01bcd395612601569f4ac5daf2363 -7136,0xef4d846beb05abd34bd60ed35b561afd4314aeed -7355,0xef7f0bc2d93caeaa824ee56592e3c2e9d5bf0c34 -7045,0xeff1896ed3f1b873ffaba8784214278c79302be0 -6615,0xf037641c69c1156530c182b970045eba6289553b -7530,0xf0671cf8a1a0b3308e84852308f9624b9ec2e28f -7101,0xf149abd352f4ab50ca3c161f4f5d0c85aefa8a9d -6494,0xf1b530f3defd144a024a958d4ae37d138615d91d -6626,0xf23c5ec62ec4398302efd84587eb8ba26f21b155 -7690,0xf23df6328a8edcfb34b9905715a32181e72964c3 -6749,0xf2425a167bfd229e64d9e28258013ca959e9991a -7535,0xf2bb38e5fe4e244ef3ab82f218383ba4d358f895 -7426,0xf2d796bdfdbebb4f02263330ff1961b03eee5aa6 -6708,0xf328d23e26d8134bd6e4c5fed7e7fa7f5736beef -6474,0xf32b995fe4ddf540c848236db9638d137aa9b6ff -7246,0xf38d41c9b2c8bfdbffcbe5d6bb19cc17f190cd5a -6954,0xf3dcf19d397f5a696a106b4287379fb53fe33005 -7220,0xf40482b4da5509d6a9fb3bed08e2356d72c31028 -6838,0xf40e46c74ca3e72d8dc490493fa9499999c6256e -7585,0xf456c7abd3059023892266b241d4912e98179eb2 -7061,0xf49c194954b6b91855ac06d6c88be316da60ed96 -6684,0xf4aa6bf149873cb965061f845407091ef9f50722 -7034,0xf4bc5588aab8cbb412badd3674094ecf808286f6 -7725,0xf4edc15ccf3be5833db3753aa91782a601f9aeda -7292,0xf4eebdd0704021ef2a6bbe993fdf93030cd784b4 -7229,0xf52df12dd62731a11180403212ee67cb5f4d6345 -6945,0xf559df60dea79d9aa137dcd98b5c42bfb9b883a2 -6768,0xf56e6052090970943cbde5566e4c9e6e231ae1c1 -6709,0xf5826f24805c162c696b79ba31ebc6fb8003d475 -6450,0xf5a6115aa582fd1beea22bc93b7dc7a785f60d03 -6473,0xf5b549c9c72220090cb694fb93cd70b350acecea -7052,0xf5d0bfbc617d3969c1ace93490a76ce80db1ed0e -6979,0xf5db68328fae1746f9bac3403ee3437fcddb35f2 -6963,0xf5f5821b1236d7624a6c009190b4dd7f54bb18d2 -7526,0xf606e99d6f6a003623ea5764da119baecb2e8c99 -7091,0xf612f3098a277cb80ad03f20cf7787ad1dc48f4a -7598,0xf6705ae21ea22c954f9f31690a3d170c5866d8b7 -7126,0xf676e375ed19bd05c85f7ef8958c69684fd1b3c6 -7399,0xf67fda142f31686523d2b52ce25ad66895f23116 -7302,0xf6af0f6e69a24bc753382a9c53d568d83515f167 -7168,0xf6b1867b51d30d6f34ff9ad603dedfb32bf6322d -7267,0xf6c747372d5ba07a3784338c1748b7b791143417 -7324,0xf6cb6699367f8f61a8bf504cbe914c639d051e19 -6459,0xf6f66794f14cca426e41c98735647849e2e4560e -7274,0xf715727ab8458c5792961a1eb944f97a67289a61 -7055,0xf71faa90e53cbc2874201b2857dad1026ca38dcb -6604,0xf73fa296df4c46b7182b14ac7862f6e08da84150 -6547,0xf747ee20492c211e09d62b123dc8ebac97c08f51 -7259,0xf75390b496922d3ef5c9819b08d4b6e0c2c8bfd0 -7515,0xf7af14838789093ccd01c67cf9bc5f602501ced0 -7194,0xf7d3d05cceeecc9d77864da3dde67ce9a0215a9d -7504,0xf7d9bd13f877171f6c7f93f71bdf8e380335dc12 -7371,0xf7df260a4f46eaf5a82589b9e9d3879e6fcee431 -7186,0xf80f787609a6ea266f48c0fd0e0f0b89cda2fabb -7591,0xf8114a2e3d50e6cf16e0c9e4e88a5ac99e7c3bb5 -6533,0xf86048dff23cf130107dfb4e6386f574231a5c65 -7333,0xf88fc87d14635d8ade4a891e4f2d0c3f9eed8986 -7665,0xf8ab6b9008f2290965426d3076bc9d2ea835575e -7138,0xf8b9dd242bdaf6242cb783f02b49d1dd9126de5c -7488,0xf8dbef33111a37879f35ee15507769ca927cf9c0 -7505,0xf963a0fc0bfc38fefe08c6062f2ad9a11affdeeb -7566,0xf9c0122a38c6b85fdaff3065f3d0822d9802e03e -6804,0xf9ccb3d8f1b0a690bf9bf36e1f344c3b3741a8c8 -7155,0xf9dd29d2fd9b38cd90e390c797f1b7e0523f43a9 -7080,0xf9e5fafa7b744d75f10ffe24fb2a7f7ff40e6033 -6485,0xf9f70f783be3ee6ebde9504ba0ac0730151b0a22 -7356,0xf9fe3607e6d19d8dc690dd976061a91d4a0db30b -7525,0xfad0835dad2985b25ddab17eace356237589e5c7 -7384,0xfaf84737dae0da0c093bc172e32fb845016df642 -6586,0xfafe2ec4999aefd162aa3c0aba16d08235662606 -6729,0xfb2c79bbbe96df046f17594cb52434ea0085eb0e -6775,0xfb5b416de789a3a5acc6ca5a0321a0313bb07051 -6644,0xfba85d793ff7faa973c1bed1c698cee692a4c306 -7701,0xfbbbfa96af2980ae4014d5d5a2ef14bd79b2a299 -7258,0xfbc4198702e81ae77c06d58f81b629bdf36f0a71 -7113,0xfc22a0d6f9a4a947d8a31a08e0e4aae079aec26e -6530,0xfc328bb84082aae68cc47d7f032b177b8da348bc -7523,0xfc6895ff4756985bca9df2aabb5f31651c591bef -7326,0xfc99d08d8ff69e31095e7372620369fa92c82960 -7261,0xfcce7f97100c0bdd63c3d53ce0ac35be1f900a5e -7282,0xfce521766201013974dbf3b71e68b0cf8fbcd05b -7118,0xfced732308759375fddd853fc680a69b2c483dfa -7027,0xfcfe26b456ac450831e31da13bd0e610fd2f08c6 -7424,0xfd1e051213aabbc10ce1751fbe995431fa7298a3 -7318,0xfd306a2b2e78e35f573f8593aeed50d07ceab6bd -6779,0xfd49c7ee330fe060ca66fee33d49206eb96f146d -7016,0xfd66b08e8190ba4997337a18451bdfe744d92155 -7214,0xfd9e252cd1fa456aad9fc592608e86fabcf40f77 -7735,0xfd9f2393634fae33099503b45719d8ebf1da7744 -7642,0xfdd85dc27b7ff3d35ef814292bc64bae43de1ee8 -6938,0xfde9d8f4d2fb18823363fdd0e1ff305c4696a19d -6839,0xfdf3be612c65464aeb4859047350a6220f304f52 -7160,0xfe00395ec846240dc693e92ab2dd720f94765aa3 -6967,0xfe0ecab5804b8686c58f0738acb3659b6263b87a -6670,0xfe33ae95a9f0da8a845af33516edc240dcd711d6 -7284,0xfe4950f0d73014039be3ce900d5dcd24ded54ccb -7013,0xfe8e48bf36ccc3254081ec8c65965d1c8b2e744d -6465,0xfe9f38453e59cc67c786c2e68d01cb7eb896f8bd -6561,0xfeaf9e0a57e626f72e1a5fff507d7a2d9a9f0ee9 -7349,0xff5c26abd36078c768c40847672202ec343ac5ad -7755,0xff5cfdb5b9640eaea8d23c1d72014346ae8174fd -6489,0xff82e4012816cc01093565c6d2ee2af83f3cb3ae -7703,0xff8bf0f6f9494b44aed91bb7868ab94b76dceaad -7078,0xffa3635f5844ea0f2fccb03cb936828f508f558b -6752,0xffa364ab1d2556d95ed129f3a6dc91281787b2a7 -7865,0x003107b3aeee133804eabe7d1df200ddfbb51dce -7818,0x061605c4ad8825e3b6731875b409d77f19fd75c9 -7837,0x121ab82b49b2bc4c7901ca46b8277962b4350204 -7845,0x1259adc9f2a0410d0db5e226563920a2d49f4454 -7838,0x169ddbb26d3e2242b8878e7cc80c37ebcbbe9c28 -7847,0x20b587484e75752adac381ae577a7562e7f358c5 -7840,0x244268b9082e05a8bcf18b3b0e83999ea4fc9fcf -7830,0x25f8fa4917180ff308883e37ea27ceaeb68c1f19 -7853,0x266557a864680a1401a3506c0eb72934bd13bf59 -7841,0x2a064e94418ada3faa396785e38fbeb290d19ba0 -7824,0x2d8ee8d6951cb4eecfe4a79eb9c2f973c02596ed -7822,0x2fe2d95bb80d912a18f869c69d537b987a0454b3 -7849,0x3192ae73315c3634ffa217f71cf6cbc30fee349a -7859,0x39274f5d1734939211bf1b1b9bcfdfb324994075 -7839,0x3b081263cd149739e1b44a354f925180d52de92a -7828,0x40c95b106455d7d2c0a8a14cbbc95ac9ea356da9 -7842,0x41e95b1f1c7849c50bb9caf92ab33302c0de945f -7832,0x432036208d2717394d2614d6697c46df3ed69540 -7825,0x470f9522ff620ee45df86c58e54e6a645fe3b4a7 -7846,0x5a5fff6f753d7c11a56a52fe47a177a87e431655 -7862,0x73783f028c60d463bc604cc53852c37c31dec5e9 -7850,0x78a83c17600add7447dbd6b8ab26330481075295 -7861,0x7e7a0e201fd38d3adaa9523da6c109a07118c96a -7854,0x7ef7560789ee2cb301ec38c3c8b91ba8a94cd1e4 -7848,0x809dc529f07651bd43a172e8db6f4a7a0d771036 -7833,0x84a420459cd31c3c34583f67e0f0fb191067d32f -7834,0x84ea83b6e88c471a446ae7007c252574e715711e -7821,0x8745773cc6e70577819bb76f51fa7640cece505f -7836,0x8c7d5f8a8e154e1b59c92d8fb71314a43f32ef7b -7860,0x9413b54f04c90ed8eb59a08323d767b72dcd278e -7843,0x94a423e5f6d205eb71e07a294f9c231b1561eb42 -7851,0x9508bf380c1e6f751d97604732ef1bae6673f299 -7855,0x991adb00ef4c4a6d1ea6036811138db4379377c2 -7864,0x9cd619c50562a38edbdc3451ade7b58caa71ab32 -7820,0xac9b0b65e7cfc1dd482ed9249a44e58c9c9086ed -7823,0xaf41a65f786339e7911f4acdad6bd49426f2dc6b -7829,0xb003e75f7e0b5365e814302192e99b4ee08c0ded -7819,0xb37a2c0dc261e5f8019809116e4722585985b07c -7856,0xbd9b39b8ce8ec403a4b4f277a88d2dc4a44baca1 -7857,0xbf180c122d85831dcb55dc673ab47c8ab9bcefb4 -7835,0xc8315587e688d1d8d08986f5d49720c2404fc438 -7827,0xcf6ff6f5aad9172b15c4a5b6153ce633e8870768 -7867,0xd359bc471554504f683fbd4f6e36848612349ddf -7826,0xdb9f78f5dd41b73b5020e841b29b5983408f5069 -7858,0xdde5bec4815e1cecf336fb973ca578e8d83606e0 -7866,0xe0fa08834465ecc36c494f2b6c87b82ab7970413 -7863,0xe37f21efaf8242d086b79a799eb0fdc3fb778704 -7852,0xe8c610fcb63a4974f02da52f0b4523937012aaa0 -7844,0xf07d1c752fab503e47fef309bf14fbdd3e867089 -7831,0xf68cd56cf9a9e1cda181fb2c44c5f0e98b5cc541 -7886,0x06ed62fce5d0b087c92da7f9965f43042792d0f4 -7880,0x38ebf16d656ca64157b503364d052313893ed5fc -7889,0x3e349ecb2c6bb2eabcd47ad0c6afcb30aeec7474 -7878,0x3ff61f4d7e1d912ca3cb342581b2e764ae24d017 -7882,0x44f91814c5c766e0762c8c23d65759f631c0abbd -7883,0x4f8ab66f046bd34c75840a71389e8e73cb4827b7 -7884,0x7d484f1047fa4343047cf95502409a267b7019e2 -7891,0x7e756de98019f69b64560dfd5c95578f93dab33f -7881,0x7ffcee844cf87c65af845f62987952aaa4653cd1 -7892,0x809f1aa348e2e35d8f69dd96e219f62b2f70901d -7888,0x81caae842b3dff6b325b54e2bd67ac9d50b87031 -7879,0x8377aa8aebc32d21cbcde7267d62cfcf305a4c5a -7887,0xa9938c13f2c446c7decd9d9ebee6db8100ca4157 -7890,0xcf942fe237016ea778d1f9a4b185b98913b35888 -7885,0xfe68fbf16999798936c9b6940761c8697f1ce1ab -7920,0x01a3c8e513b758ebb011f7afaf6c37616c9c24d9 -7921,0x1da522b35363c1eda4833bc121c8f3c67b2caa75 -7919,0x292fc50e4eb66c3f6514b9e402dbc25961824d62 -7918,0x5523985926aa12ba58dc5ad00ddca99678d7227e -7922,0x7057ab3fb2bee9c18e0cde4240de4ff7f159e365 -7929,0x81ab74a9f9d7457ff47dfd102e78a340cf72ec39 -7923,0xb8f275fbf7a959f4bce59999a2ef122a099e81a8 -7993,0x04682d6c443c48245a0eb52a0a015f9bfe45ed40 -7990,0x07d0a530cefc8af999921136dae2fc29defabf8e -8002,0x0e221e65f85336db137b22596c5c5c259a629d15 -8013,0x17b544bdddaf3ca65c100eb91c10c47e65507d30 -8008,0x25b320a7c69e66515d7f6c044ec9af49fa1588bb -8007,0x25d526ba7c9905c764aeb63bfe0b0ddf1c69bce1 -7992,0x2fdbedc4c74e5e4a067156e8afd5afb1d73da483 -7989,0x380618cb26ae9e91b65d88b4fdc6b50c1aa90c62 -7999,0x408ba8dea8b514cc24f81e72795fc3ddbca8dbb5 -8012,0x4671aca0c1fe99fe6779606698e61783d4520a41 -7994,0x5a5c0c4832828ff878ce3ab4fec44d21200b1496 -7991,0x5d659bdc0fef665cbb838ca4c77b7a756746b4e8 -8009,0x69298ffe05627268d32dd32762e364bfeb42206b -8000,0x7ace9b27c5028f193bec2fd255e665aab354a417 -8003,0x7c540d2bc549305cb2386423755a4f47925d2e00 -8010,0x7d5c4610761d922b1b298f05f39eb9e5f8698866 -8014,0x7e45ccc0cd846a20501dccd0f1a65a98800444f7 -7998,0x8a49f3aad8c1a0d34ea1cb0e695c32e0a18e95a4 -8006,0x97e792198443482385c3bd7eb16d11d0462ec10b -7995,0x9fbdf5e760394964f8c71fff4ff5cea5b325237c -7997,0xad30c5135468bcc647add3370b505b88219cf1fe -7996,0xb6124f002143bd9182319098acafbebc3b6af919 -8004,0xdcb25d45cc68dc15eeb42549ee89104afef861af -8001,0xe69d3c7b11d543d77ef71db8f985e01c24686ba4 -8005,0xeab2abfb36c28316b0b7dae80cdccbd26b2e6833 -8015,0xee0616a2deaa5331e2047bc61e0b588195a49cea -8011,0xfc33f3cac9f6d1713a4b7787df6a9a33bad244d6 -8121,0x011e52e4e40cf9498c79273329e8827b21e2e581 -8137,0x0367a647a68f304f2a6e453c25033a4249d7f2c6 -8118,0x044b75f554b886a065b9567891e45c79542d7357 -8150,0x05689fcfee31fce4a67fbc7cab13e74f80a4e288 -8135,0x0be808376ecb75a5cf9bb6d237d16cd37893d904 -8120,0x1838b053e0223f05fb768fa79aa07df3f0f27480 -8160,0x1af415a1eba07a4986a52b6f2e7de7003d82231e -8147,0x1b02da8cb0d097eb8d57a175b88c7d8b47997506 -8129,0x1be211d8da40bc0ae8719c6663307bfc987b1d6c -8132,0x1c5771e96c9d5524fb6e606f5b356d08c40eb194 -8167,0x2214a42d8e2a1d20635c2cb0664422c528b6a432 -8165,0x258f7e97149afd7d7f84fa63b10e4a3f0c38b788 -8148,0x2c8c987c4777ab740d20cb581f5d381be95a4a4a -8159,0x2f255d3f3c0a3726c6c99e74566c4b18e36e3ce6 -8142,0x2f686751b19a9d91cc3d57d90150bc767f050066 -8152,0x320a04b981c092884a9783cde907578f613ef773 -8125,0x36074bf0ad1d9fdbb3ef9f7d560c995c1c4eafbc -8151,0x3d2f8ae0344d38525d2ae96ab750b83480c0844f -8140,0x3db923fbab372ab8c796fef9bb8341cdb37cb9ec -8146,0x438a2b1c6c715fb1b051c04ca65755560d8b8d43 -8164,0x4c5d5234f232bd2d76b96aa33f5ae4fcf0e4bfab -8134,0x54ea633385c52263846b37409649b5b8fa5ed402 -8170,0x5629ce74ddcad7cc72b3ea30444da7172ad851d9 -8139,0x67468e6c4418d58b1b41bc0a795bacb824f70792 -8127,0x6b2a3ff504798886862ca5ce501e080947a506a2 -8154,0x6c5a9e667297b409b5dd9850b38889ab84110c2a -8124,0x7a4af156379f512de147ed3b96393047226d923f -8168,0x7cf167390e2526bc03f3cf6852a7af1cec3e243d -8138,0x843d0aad40295f2198ef528ad747cdf6ab9000e4 -8149,0x8b396ddf906d552b2f98a8e7d743dd58cd0d920f -8162,0x8dacffa7f69ce572992132697252e16254225d38 -8126,0x8f54301f315c56c112d492d9443047d4745dbe9e -8143,0x96e04591579f298681361c6122dc4ef405c19385 -8122,0x97a32b4f8486735075f2cbecff64208fbf2e610a -8158,0xaa26771d497814e81d305c511efbb3ced90bf5bd -8119,0xab235da7f52d35fb4551afba11bfb56e18774a65 -8155,0xb25157bf349295a7cd31d1751973f426182070d6 -8133,0xb3115b68e69886dc9dbcc674db35e73cd91e9053 -8153,0xbd87bef520f8cec45d73b0413b6cbb8b061736d9 -8128,0xbe811a0d44e2553d25d11cb8dc0d3f0d0e6430e6 -8130,0xc35dadb65012ec5796536bd9864ed8773abc74c4 -8169,0xcaabdd9cf4b61813d4a52f980d6bc1b713fe66f5 -8131,0xd045d27c1f7e7f770a807b0a85d8e3f852e0f2be -8145,0xd08b5f3e89f1e2d6b067e0a0cbdb094e6e41e77c -8163,0xd9988b4b5bbc53a794240496cfa9bf5b1f8e0523 -8144,0xe52180815c81d7711b83412e53259bed6a3ab70a -8123,0xe60e17d9df5a60dfad40006323f652725c94d814 -8156,0xe6e84929fd0269034beaeb5c601c2b6c36e7a8b5 -8157,0xeae23c766a1b25481025a02b2d82a1db3fc130ca -8166,0xf0cbce1942a68beb3d1b73f0dd86c8dcc363ef49 -8141,0xf4d73326c13a4fc5fd7a064217e12780e9bd62c3 -8161,0xf60e5f4a44a510742457d8064ffd360b12d8d9af -8269,0x015036a77ef143aa14d6d800cc15d49f143e3880 -8250,0x04515b6c0bf314efbdedece646432357deb3cbb7 -8280,0x09d7d1391f7e12a1e6add8b119248e355883d74f -8221,0x0c6d90a98426bfd572a5c5be572a7f6bd1c5ed76 -8301,0x0e8611a5c8d2591bec62c6ff50ca0324501ed62b -8220,0x109412e3c84f0539b43d39db691b08c90f58dc7c -8305,0x137d1053cb9bb6a32a90e092cbe9ed23c1e86358 -8288,0x143ea239159155b408e71cdbe836e8cfd6766732 -8240,0x15f0ca26781c3852f8166ed2ebce5d18265cceb7 -8237,0x16826ecdd359f8d3b1b3078a16fe09eac5d26d8f -8295,0x18f9e9c72c8dd094792446c2a195ed6843c5ec2e -8245,0x1eece78f76843001039f5f4de41d68ec9a80a1f7 -8266,0x259abf1e6de9a4db62632c63ff7b91d4565434d4 -8276,0x2aa431706dc48805d7c73f12210254da5eb7eebf -8290,0x2b89d2cf849ade32bff380d384476957f1becf4e -8275,0x2d2a2ab9188f7dac02686003b5282988a6693be6 -8228,0x2e15b3cd0ec91ccf925e030a1f25c3422b7b5f43 -8298,0x30a6933ca9230361972e413a15dc8114c952414e -8300,0x35aed9e37fc7b8030d4b643139bfe63f15d12ead -8306,0x36df169dbf5ce3c6f58d46f0addef58f01381232 -8242,0x36f91179551fb2516e242c527c2552c2f14b2cf8 -8315,0x385439d9063cad130e4786feab5a17ed79e648d5 -8289,0x3aa4d04111955dfdfa285e32dd9624105c6d84b4 -8273,0x3e61fa0baf0e3011ba94e9cb38a5638a652c1769 -8310,0x42b709822f18595443c308c1be5e63cbfef06481 -8271,0x463a5fdc1991242a70df90bc7afd89a8dd53586c -8270,0x4983a0f6c67304c850b4ee713a4c31cc99ed0cf3 -8230,0x4ac8bd1bdae47beef2d1c6aa62229509b962aa0d -8263,0x507c3a7c6ccc253884a2e3a3ee2a211cc7e796a6 -8314,0x5255b9a8d06af60688bc8a967b5b6049e3de3ac6 -8233,0x5487d078ca8933e83d91d5e7afbe3a7bfc3412d6 -8227,0x554a8acef6c689a74b25b1c41f58fa2e678162b5 -8258,0x554c06487bec8c890a0345eb05a5292c1b1017bd -8222,0x5735c32c38f5af0fb04a7c77c832ba4d7abffec8 -8264,0x5a9ba2a62b2c802ff22f54b01b403dd4bb4a049c -8285,0x5b3f7d29379a9569d61da92ac6a70a19faaff618 -8267,0x5bb5908dccc9bb0fc39a78cfdf9e47b4c08e9521 -8246,0x5d3230f182ae16ef5b6f6e9dae5732d3c3324f0f -8256,0x5f001c3915084186b9109fb9dc1e978c98f854b0 -8234,0x609b9d9d6ee9c3200745a79b9d3398dbd63d509f -8317,0x60f89041bf199a112980ad221971fc47c5d7be1e -8279,0x621d9ca96c2d4fd559c191ec5dbdeb180eddd4c1 -8236,0x6b0dacea6a72e759243c99eaed840dee9564c194 -8239,0x6c66e5c5d201a753ff497f2e9ec5d545631854d0 -8244,0x72e92c548fbc7066a108f641715850bc94dd4931 -8229,0x743b5f46bc86caf41be4956d9275721e0531b186 -8274,0x74b57883f8ce9f2bd330286e884cfd8bb24ac4ed -8296,0x75d30bef12650239243eed131ae6a321a81df427 -8254,0x777be25f9fdca87e8a0e06ad4be93d65429fcb9f -8294,0x7a889bb2846f79d485fb3ce5959efc29a13a0a6f -8318,0x8405c5bb7292f5e181d0870ddcb4a51efaf7e074 -8297,0x85f30c14485d1e5708c3726cb4f780590402f0ba -8248,0x882703dc8239e2ba167e06ce1fcf654e17a0bd06 -8224,0x88ab2cbd7976dbb4cbc253dabb89a5a2fe5c4a20 -8277,0x89b4518dda51077c51e7853941c7dd3ac70d10f1 -8219,0x8a63b9b37487477a8cbfa7a5a91a3720d9152daa -8287,0x8e310ce29ab7fa2878944a65bb0eaf97b1853d40 -8262,0x94a8358e313044bc5bc4a615da4e59acaf3e1101 -8223,0x95b9f4a244dba7aaadb8f7c837ee681811b9e73d -8291,0x960b54b7f585c2d2c5ccface9f69ea97137ec69f -8218,0x97efe74c88bbbc78c688107e6f88105a05d4ac37 -8293,0x992805ed822c786bcdba4cb1f07b5cada3bc3cba -8303,0x997b37fb47c489cf067421aeeaf7be0543fa5362 -8278,0x9b815f3b8a0ce0ced76bb652f4b9e9a473d1dd04 -8255,0x9df6dbb291e96597114d2cbcd856325e6a731e06 -8231,0x9e6c7fb511090e9a0de0bf6a0e5358dd90ec0432 -8251,0xa3c8502187fd7a7118ead59dc811281448946c8f -8302,0xadcf0ac9f2aa04a79fc64a1fdf0a0c9f3ae0556a -8268,0xbca593fdc031dd4ccee2edc2dfe98c50edae3a99 -8261,0xbe0ff75a59e32d1ec686e79542612f33c318f57a -8281,0xbe20bac0dcf6f01834f51ccdab2dd72707c6e9b6 -8299,0xbe49ac1eadac65dccf204d4df81d650b50122ab2 -8286,0xbf42d2f5ca96715a808801d73f34dc6554a7e62b -8257,0xbf7bcce8d60a9c3f6bfaec9346aa85b9f781a4e9 -8283,0xc5093f78c91351a6b4b6bc5f8f89bdcf88920393 -8265,0xc50a1cd958b3ca29af9ee4730f69616e47dc693f -8249,0xc7502e273599c744925c905f490c41366dc1ab50 -8309,0xc8dcf0cbb0f02475ebadabe7daebc5b8b6b81f57 -8243,0xc95346b7394009ccefaa62eca28797804b2bcf1c -8241,0xcc09225a656a08b5333bab909362bda32f8d79a2 -8259,0xcc64973238cf06a164982146ff2dcdc7f977420b -8304,0xcfa132e353cb4e398080b9700609bb008eceb125 -8260,0xd0de1486f69495d49c02d8f541b7dadf9cf5cd91 -8292,0xd19e9a80bad127ca6d26647f7c0bc27d3c690e72 -8232,0xd38ce9c40e9f95f19ad0735357ebb88c6f6c3b10 -8308,0xdaa54c18ce99b84ce718f56f7e6f70e243550c70 -8282,0xdf2f3a7e116575478b72a7a4cc80c219ece4ca5b -8247,0xdf3f4b9e4e67a21c868d043a92ae921ff0ebadbb -8253,0xe3cf7619448415e857fbc76fa806a0eccbad1274 -8225,0xe56ca65b2446f107d78f45b358697774e30068ea -8312,0xe5e2421c5b9488c17e9e58e26a17b4f4bb3f84a1 -8238,0xf04f2c525819691ed9abd3d2b7109e1633795e68 -8226,0xf06a4a23a0b1cf436cf0f1c0af8ed20380f01d0b -8284,0xf21019b8688e7730ca6d9002569ecbaf8d1a3083 -8311,0xf353978890204756fc5fa6dfbd16a91eac9e6f4d -8313,0xf4bae246462166ad35bb11fc9831953664195531 -8316,0xf759c2ee9d7107e83f9c0dbe6645fbd54ea92644 -8252,0xf8d8f02b788de5191ecd20f7bdb07d80963410b5 -8307,0xfb2b126660be2fdeba254b1f6e4348644e8482e7 -8272,0xfbce35101ed531c08604c9ba16de4500477d6f54 -8235,0xfbe332e001d6b54e1f4b63c2343b8e7746d99ece -8371,0x02f55d374d791dff5614ad2f368145a46343b08a -8381,0x063e4242cd7c2421f67e21d7297c74bbdfef7b0e -8359,0x080bb3a23098d71a4e8fc5de8f1cbb83553bbc57 -8366,0x0ed12441616ca97f5729fff519f5e8d13d8de15f -8360,0x18ca8be41d32727383bc0f98705f7662ed0b7e28 -8386,0x2006d4e76a398c78964f7e311bfd7ccb149eafe2 -8392,0x22e4cee555c44df56ac7b85033cde54b7439817c -8377,0x330b1b23dbf728841af12e6478cebb9d51ab6f90 -8399,0x36a9ed9b00ecc380c4e559b80a1857c65353ce7e -8375,0x392acebea829373a3efdc0da80a16003106d8f6e -8368,0x398105cd43115b54a0efe0b210d99c596e4571a7 -8385,0x3dd262181ba245184a903cd8b77e23417f815669 -8364,0x3f91613f0c7f1f5940c324ffef07632dd5793680 -8357,0x414958801dc53e840501f507d7a0febe55806200 -8388,0x43c9a445fcf3bc3d1483c0b90dc0346249c0d84c -8369,0x4de3ca09e803969408f83f453416b3e2d70c12fe -8361,0x4ecdc277484d71a3bd15f36c858aec2c56803869 -8390,0x53958191c3077ede3ca90eb840283df063fc1be3 -8379,0x55d4d311cd9b2dd5693fb51f06dbe50b9da84d13 -8393,0x5ab3e51608cea26090445ca89bc91628c8bb99f9 -8383,0x63059cc2533344b65372983d4b6258b2cbbbf0da -8391,0x645325494a37d35cf6bafc82c3e6bce4473f2685 -8380,0x6d3af85e27686fff7686b2fae174b0a7d8c95e16 -8398,0x6e4c6e76b3c1d834c0e3c4c2baec8d58b8421a99 -8384,0x7bbe5f9c95e2994c420b3af063e74e5f87b2a3b5 -8365,0x7e7eb65a93441a2d2bf0941216b4c1116b554d85 -8370,0x8061c24823094e51e57a4a5cf8bed3ccf09d316f -8394,0x916411367fc2f0dc828790ea03cf317ec74e24e4 -8389,0x9319710c25cdaddd1766f0bde40f1a4034c17c7e -8396,0x983efca0fd5f9b03f75bbbd41f4bed3ec20c96d8 -8363,0x98ee526edf6c9c3cfa1369a5d24bc2c6c278bb19 -8358,0xa0cb87300ab07d00468704cd8f016f8de47d8e0a -8367,0xaa4c55a8dd5b0e923056676d544fc20bb5d5e3a3 -8382,0xaaf5abf888d6633cab2bb04e46ebb2fd3ba98b14 -8378,0xac0b1652388ea425884e6b60e2ed30155f43d50b -8395,0xae39820d9f9f0ce9331eae6827a9d922ca5287b9 -8374,0xb3f0c5e4012af22359c9ab233dabd80cd81f5ec5 -8373,0xd4feaf1023cd6998053a1eb02460000980cc908f -8397,0xe7aa0939f0cff45162a22751cbe0009c689ea256 -8372,0xeb54c366512c4d59a222a251ea7316568859e08c -8356,0xf22f17b1d2354b4f4f52e4d164e4eb5e1f0a6ba6 -8376,0xf8c44fdb83bc89fe3db2feae98e2732fda469699 -8387,0xfaf9d0b7b92e8b281caf10b42970179b45ca6412 -8362,0xfdff46ff5752ce2a4cabaaf5a2cff3744e1d09de -8453,0x09eb271b444d1db7163c8555919de85b20be7d1d -8462,0x0c5c5beb833fd382b04e039f151942dc3d9a60ce -8449,0x116dba5dce9ccda828218b7eb46406810632014c -8466,0x279228aafd13bb4642abb772a248c78555dc39c4 -8470,0x27c6b12cc14a498e0e252bd7f3e7af060ebab048 -8447,0x2822ee30383eabcba817ab4a7a592f4a194e14b5 -8461,0x36c796bca1e8e4031d3fdfa1ff0fa5ef429e0ed2 -8452,0x38a07152ee64c7067feeba904d72abc9a9da0c77 -8468,0x5d8249e3f5f702e1fd720167b40424fc2dadcd1e -8456,0x64f9a121ccbb8956249ed52c4f63d0f759da53ce -8451,0x7c31a819a36e29939058b61fdaea28bc3cfb66b8 -8464,0x7c464a0ab1f5ebf3e2dcccfec7ef41d02ed7a2f4 -8459,0x806c2240793b3738000fcb62c66bf462764b903f -8450,0x8fb2cd40046b08f155d06c3a31b69c1bd2c971e2 -8471,0x925c4454820b2b88c2f70f2621e6890b451266dd -8457,0x9f5b097ad23e2cf4f34e502a3e41d941678877dc -8448,0xa1dd21fbd9e1f0bf28d41f18bdc22326e50c02e9 -8469,0xaa4448aef5f5967693751bfb67197946bb6d1dc0 -8458,0xab36d30c1a1c683037bd7aac67f29b2e3ecc6576 -8467,0xb27a61faf33d2f6ad7af8cc2512c3b58901f779b -8445,0xb944789b5c962f1096ce8674456e68fb0f2e3bb7 -8463,0xd5532a58e4abb6485ff4e7bcf5bed278e32a516e -8455,0xd90c8970708ffdfc403bdb56636621e3e9cce921 -8465,0xd964811233b8a0185d3c93664df82136c80d1bb2 -8454,0xdaeed7da964b212963ce207660633758fc6ecfec -8460,0xe240b9a2936f6fb8860219bc059349e50f03492e -8444,0xe6c78a5bb134f00a3111f34b5ee863c82041292f -8446,0xf4de4180829272ae5f69acacf820f4f7d547513b -8558,0x06d538690af257da524f25d0cd52fd85b1c2173e -8553,0x16419058f15a86795933f78dc624b384d09e3a4e -8549,0x18ae6d4becae6cb6d2536ecce89871f27a8c1bb0 -8538,0x224d8fd7ab6ad4c6eb4611ce56ef35dec2277f03 -8528,0x296f55f8fb28e498b858d0bcda06d955b2cb3f97 -8542,0x2f6f07cdcf3588944bf4c42ac74ff24bf56e7590 -8546,0x343fa3559f99ebb0dc1f5221d0654d7bd7bc011f -8548,0x43d2761ed16c89a2c4342e2b16a3c61ccf88f05b -8551,0x45a01e4e04f14f7a4a6702c74187c5f6222033cd -8552,0x45f1a95a4d3f3836523f5c83673c797f4d4d263b -8537,0x46bc16f76b0ae14abb820d3410843ba54d8ef6f0 -8563,0x4839ff304dd3e38da9bc73f58e2999390e76b80d -8533,0x4a364f8c717caad9a442737eb7b8a55cc6cf18d8 -8535,0x4dea9e918c6289a52cd469cac652727b7b412cd2 -8529,0x505ecdf2f14cd4f1f413d04624b009a449d38d7e -8536,0x53678e62af4dcdfc3bb5b72c9826ca9b60f28f80 -8550,0x5c1a97c144a97e9b370f833a06c70ca8f2f30de5 -8541,0x6694340fc020c5e6b96567843da2df01b2ce1eb6 -8540,0x693604e757ac7e2c4a8263594a18d69c35562341 -8545,0x6fd8dd2d7ac59e41c3907f6ae4717b08d5b407cf -8547,0x701a95707a0290ac8b90b3719e8ee5b210360883 -8557,0x712a326b7c0dd4d1c319dedaf0122330de25dd44 -8532,0x7a045193ade1e7013d8d19f3edd3f6cb8ae61275 -8543,0x8731d54e9d02c286767d56ac03e8037c07e01e98 -8562,0x959d0037a71e8d1a1a369a417910dd05dfe22366 -8560,0x9d1b1669c73b033dfe47ae5a0164ab96df25b944 -8559,0xa6681e7863410491efb2b8d6ea7f740114614e49 -8539,0xa96b576f3f117158176e396467d591a1f5dba9df -8561,0xae8d00e43adb49d14fa07c93b27cdb3ee94c4675 -8555,0xaf5191b0de278c7286d6c7cc6ab6bb8a73ba2cd6 -8531,0xaf54be5b6eec24d6bfacf1cce4eaf680a8239398 -8534,0xb0d502e938ed5f4df2e681fe6e419ff29631d62b -8530,0xb49c4e680174e331cb0a7ff3ab58afc9738d5f8b -8554,0xb69c8cbcd90a39d8d3d3ccf0a3e968511c3856a0 -8544,0xcbffca36cdc13c646f8391db55e4c13a7ce05f0a -8556,0xd47711f0aa5f34ad0a17fea6599f45664dd201b5 -8579,0xfe7dbcab8aaee4eb67943c1e6be95b1d065985c6 -8657,0x12a1458b9162dbf50983e72e78e41e37f5bf1a80 -8662,0x1a36729fc276c3649da2cf1eb986d7dabd5d5d99 -8661,0x2a3a42c653e427f4d44ea47007a99ee6bbae65af -8658,0x385e9f438a8b5ca54bc87e2b826fb7adfdc3fd7f -8654,0x403471cbcab399896004bc0af2b4674d4ab3b53b -8660,0x46f81107e99adf80b8ea49ab7281b984f826fbd0 -8656,0x65d21a94c78a5f9ef68a2be141d0c91afc51a7d3 -8659,0xdc2fe45974c30c2c7cd16149dd5fc00b3d31ebde -8655,0xf6cbe9226e58f2700b4df42a35cb14078c679076 -8695,0x036d9cb816f382d947dfbc26a13d2ac8cdb13d34 -8679,0x05e532c94cc6210d66b3fb0683d58943735ad243 -8710,0x066c98e48238e8d77006a5fa14ec3b080fd2848d -8758,0x06f98cb1090afb2a923f21ab37ca9e1a7cf8532b -8763,0x0c791a2377981149ece2596e73b173164dd19c27 -8727,0x168901193a72e82c00110c799c9c22f3ae6fd311 -8738,0x17063ad4e83b0aba4ca0f3fc3a9794e807a00ed7 -8707,0x1812aac123b6a3881688adaf3954c832bf5a743f -8734,0x1c327ecfe87ab0428d74720ee1f33238c10b5834 -8708,0x1db2466d9f5e10d7090e7152b68d62703a2245f0 -8701,0x1e9a8a4fbf43d9e73ac2106acd1c32c4976ec978 -8736,0x20b488ddaf6fea0e9e645fd277cb897f6475c603 -8743,0x236ed9cce4c2423572b9453da3abb5b72e2eb91b -8715,0x24149e2d0d3f79ebb7fc464b09e3628de395b39d -8699,0x26aab17f27cd1c8d06a0ad8e4a1af8b1032171d5 -8759,0x2778d99a0897a8a43d8d0f30f9f61bfec1b0a7ea -8688,0x285cb1faca9184132c7ee99964a15549e6cc9c4c -8739,0x2b1d11f82fc70e2693ab19c4c3da23da75ae2597 -8677,0x2b5fd19124359b7e72c08d44d3ced472d181beed -8750,0x2ba86032e601e001aeb0069efff824bcf699ffc5 -8751,0x2c2c8fd6ff697302809a39619dfae4f4f76d325d -8714,0x30f433dc05eb80ae3ff678102de12a5758ee2f0b -8709,0x30fe862f1d4c2c7e1b006d9325256130fbb55e46 -8703,0x32cb367378d0b1ddbc81f259a8445bb88ddd9a0a -8746,0x33865e09a572d4f1cc4d75afc9abcc5d3d4d867d -8674,0x37ff10390f22fabdc2137e428a6e6965960d60b6 -8740,0x3b8075483e51ab78d81caa5300f7f20a5386d04c -8754,0x3dc9fa7759ca2769982bf7ebd36362e52d54f3bb -8684,0x3f6fb832279ac7db0b4f92b79cbb8df03702631e -8732,0x3fb2ef203a051a5bf190ffbb2fa510e78a5bb103 -8697,0x41279e29586eb20f9a4f65e031af09fced171166 -8722,0x4804357ace69330524ceb18f2a647c3c162e1f95 -8718,0x4b245547590d2274bb3ffb67ce448369167b353d -8757,0x4b3ee1628f0d5e19337b104fb7d776c5ce5238cf -8755,0x4c988e12c38fea0a4018605aaa5349d3495ef341 -8702,0x4e0e206a3e10ca372ab7afae840993ec02d6c815 -8756,0x5479de16d06526e6792a722242f2bcb589c49582 -8752,0x5569b83de187375d43fbd747598bfe64fc8f6436 -8693,0x58333b7d0644b52e0e56cc3803ca94af9e0b52c3 -8769,0x5b22bd2fc485afe2deaf1ac9e2fad316dde163b0 -8700,0x5b89789fb230d17c4b0ae1461f26828554f81e5f -8762,0x5da3ea9167e159de0d3003c21c86db1334c3f5c2 -8672,0x5ff29e4470799b982408130efaabdeeae7f66a10 -8747,0x60cf091cd3f50420d50fd7f707414d0df4751c58 -8681,0x656ae279bcfac02d8e5a51306b72090b3aeeec6a -8682,0x6b38c561366a939ea8711a7311c8e5aab0a71df9 -8765,0x6baa135acd661b93401c726f4c9fd2cc0be374ea -8728,0x6ee38828bdabfed9ddf16c5819446774354c5634 -8721,0x7320bd5fa56f8a7ea959a425f0c0b8cac56f741e -8696,0x7d69692389cda1ed99fd04d3081d7ac9368d6778 -8676,0x8cd6b19a07d754bf36adeee79edf4f2134a8f571 -8690,0x8d0db2bd9111e35554b8152e172451c80dff22b7 -8678,0x8d5d5bfd51df03a8f65ae20cdcff20cd9db8346d -8711,0x8e10362334a4549640481d330a0020238b37ad10 -8673,0x90c28b6ecfb7312d361756711055665598a3f125 -8704,0x91579f47f7826471c08b0008ee9c778aab2989fd -8720,0x92e17fd2da50775fbd423702e4717ccd7fb2a6bb -8685,0x938ed674a5580c9217612de99da8b5d476dcf13f -8768,0x93d9e5f11182e6025a839af2b792e2200d3b7469 -8731,0x958e575d01f84211b63315395b16555602ac4d46 -8706,0x9eff321ddf8ab509c8c65f7e57289258e9c169b1 -8729,0x9f543b3f1eb37b2540749df537e83d29309ecc02 -8713,0xa714aa2c155edb64dad72cd568a43784137a24d8 -8753,0xad23cfb8b5d2a662f2acafdcbbfcd0795e60b04e -8749,0xafdf91f120dec93c65fd63dbd5ec372e5dca5f82 -8770,0xb112e4cc83f0df154fe3fb088109be2ac5bfb110 -8719,0xb19bd9fc8fa8f00599a04115193b915e1929bc5f -8723,0xb4bf17210844418f9f2d3b90036e11aa40517971 -8689,0xb68d9ad0a7f2e870fc19c71a4fde4146dab00368 -8725,0xb740075761914fd8c4e43dfd5d85a2e5558c8a20 -8742,0xb82d03271f4bae1f931123a7c27818bde59e4b4a -8717,0xbbbd75383f6a61d5eb5b43e94e6372df6f7f13c6 -8694,0xbe4db588a6d60b29acd6b9da5c8502bff8da2d67 -8744,0xbf4a907ac15c60c070fbbc7221f9fc21e02e9890 -8683,0xc0a2b72f912af5a0831b8acf87b69ed7070e021c -8761,0xc5ba10b609e8500c04884e1bcfc935b2c22654cd -8737,0xc793f56f74b0e3e78fb3a6d55b75e2ca636e5340 -8705,0xce3c96fa5cc543936a7f1f8e405516ca2159f0c6 -8698,0xce81ce09f495204ad72bdd08cc555afa221aeec7 -8712,0xcfdae153097b3ad134595e70fd128a2f5f8fb023 -8735,0xd14451e0fa44b18f08aeb1e4a4d092b823caca68 -8726,0xd1974664d191a9ca1cfcf483066f9e2b942d5ed9 -8764,0xd257c933b2e55ddd796fbe3e29260c952e1763e9 -8741,0xd50c9ec0cb2bc848fa3df8205e6d093e68e041c1 -8691,0xd7daabd899d1fabbc3a9ac162568939cec0393cc -8686,0xdb0c52f1f3892e179a69b19aa25da2aece5006ac -8730,0xdc05d85069dc4aba65954008ff99f2d73ff12618 -8766,0xe2cfad8763bf901cc05e9f498b7813ff2054e1fb -8767,0xe39ccd6ce1af169f9e4311cfe7788f152d9370a3 -8733,0xe7de932d50efc9ea0a7a409fc015b4f71443528e -8716,0xec4b282dc8b1732890338785fb75bcfab9838f84 -8745,0xec8fea79026ffed168ccf5c627c7f486d77b765f -8760,0xedb12d0b9e4ee75b2bc28d86bc291e7810de0299 -8680,0xefc0495da3e48c5a55f73706b249fd49d711a502 -8692,0xf6cba3c440eb70489d4fa0bd9dcf0cdf799f9f3c -8724,0xf7b5965f5c117eb1b5450187c9dcfccc3c317e8e -8687,0xfa2ecffc6dc11e1e6466bda293ff4a5f6d5a6dff -8675,0xfc69af1700e35ac67807652fb1957239805ee772 -8748,0xfc9a6ce944f14517447bcccd89cdba33e4b43435 -8993,0x029057df831cce19fc7adbb8d7283afcbb9d88d0 -8982,0x03709c48dec486b64ddedacfbed43acea5ababe8 -9078,0x04196c83ce9438f287ae0b2774abab367283c2a6 -9060,0x045c906779cec459e897449af2967be52a71450f -9085,0x052b82b3a096a592f3f28d4736c4796445bb98ef -8909,0x0531789edca5f6e641b8d3ae48ef9fda4660c76d -8972,0x0645095dc03b9546dfdb2fc8e7a52959ec37efa1 -8998,0x08cea8e6b3f8cf1c04f095edebbcc1df6f08121c -8962,0x09b471b83b91bdb1aa361f05770cfceff7526b65 -8884,0x09bbe4dacbf631994f2f1b70c1b18d62c19466e6 -9109,0x0a1040c23bc8d04669a118a27433d5b379c2d381 -9015,0x0a3451e8546458dfd60d81f90d2e495398e58f87 -8916,0x0a523d8d8e2ad6f6623b3318a59743546cae6163 -8950,0x0c0858290b6b268a93fb557af06390a3460c5db6 -9076,0x0ccfb68fc0e5c6cb84225508b5cdd286a18979b1 -9075,0x0d50d4336df62f86771b2ef5db67be8c1896ace1 -8904,0x0f933f6a58d885586d356d6677dc87995bdc1b51 -9058,0x0fd9f11b9c54a9f100234eeef9ee7873d73c2204 -8912,0x10567782c459bc8bf90b89f7da189c754203bfec -9002,0x10bcd35f03c9c9b68305875f52b406188f5755de -8986,0x11695ff9ee5e600b90bcbd651d1758a9b240a2d8 -9112,0x1812ff6bd726934f18159164e2927b34949b16a8 -8866,0x18358f1b750a8c38f041384c1f4d8867820b3a5f -9007,0x18b2379f0b359e2a5962aac884d34f29e1a72a98 -8990,0x18e36589ebfbb0410babd72e9ecdbb11ca764ace -9048,0x1b74ac5daca01d82a3d2e7688ea30e50fe6ff35e -9051,0x1bea4f9f93f84fd5a68f321e6003a6b16b313740 -9101,0x1c2ec03cf08c77d7a4668c045415a8809d0bd6b7 -8915,0x1cab023a0cb8178163fb47de7c63c3192767cc26 -8864,0x1cc477a4b1ab759d21aba3fdd886adc2eec28243 -9042,0x1d43076909ca139bfac4ebb7194518be3638fc76 -8938,0x1eb392aba52a2d933e58f7e86ca96b9a3e2d8166 -8935,0x1f83e8a02f3869fb7ff043c89f3f31a48efa9195 -8896,0x20ebdc614abc74386e5eb67a1516edc6ebd32540 -9077,0x2344621d5aa6e784e8c6f4c54b0b29dd9c3ad4b6 -8899,0x2369bbf24e64dbae6090d043663b7c1b20edcca3 -9103,0x23d5c2f9e0a2954128340587e2232924084386cd -9120,0x2438db4d24256371cc29405b12d646d370988d75 -8895,0x259dda264f78d4edd3c9abbf93fcf2174eddfefd -8964,0x25aaef700c735db1c7e8f77eb6e4c20878c76262 -8942,0x27d6c81b2688f28d8fff55940cdbb8b5b3a0ed0f -9071,0x28ac26b4f6a4483d11ad209f08f7b4dbb7364fd9 -8957,0x29829a795d2a592edd87378e5346f9fc17dd6033 -8930,0x29fd3a2ea3fd84aad21afdd7863e00a1efbae6bd -9100,0x2b351e9f1a1970115f5859107af33f1be386fb7b -9090,0x2c76dff7f3b5ef99c59c764f97aec71221920b7e -8968,0x2d0eeb574cc98f6d57c72ffe730d5c8a8f2eac37 -9030,0x2d7f2b4cee097f08ed8d30d928a40eb1379071fe -9026,0x2ddf16ba6d0180e5357d5e170ef1917a01b41fc0 -8868,0x2dff1c101cdae89d838b980d891e316c09b1f4cf -8959,0x2eb0e1a4194204c6881ef13cdfe6e539b53564c9 -9091,0x2f6b39ecfe379053475041c2fc6989d64011538e -9055,0x300e530cbd5314860dd8a0e873c8c083d2331ae1 -9094,0x301bd265f0b3c16a58cbdb886ad87842e3a1c0a4 -8856,0x30f73cb88f965eca6e4c87c05ab97d0b23359ae6 -8973,0x321767762d66a69a8ccfe07d1b9805d314707b59 -8869,0x326bbbaca7340be280f997739e4ac9791415b77f -9059,0x331d17d86c0989e533516751c1af2a896cdbf46f -9044,0x3379f120917fb67728d6db6065d9fdbbd1507a7b -9113,0x33ac3447b7707a31888d5f003234ccabb54339e5 -8927,0x341903595b4805b2f6d751083c4dd1dd71863459 -8995,0x35479b023e508ee9a7b533dbb5b516bb6875f937 -8858,0x36c4b7a6d8fb2db65c5806f4a81a8723160a8bb7 -9108,0x37cc674582049b579571e2ffd890a4d99355f6ba -8975,0x387bcee789a50b42eda6aebc55b7505183ae56f0 -8867,0x3c751688261f90369c28b16f3797e623667f69d4 -9050,0x3dd7419889ff387a58d3dad1f8b49ea1064290fb -9115,0x3e76e8674fffd1e6495ec719b82a86cb6a31eff7 -9093,0x3e89c061bf570b0678c7792bbffc6baa9d580dc5 -8989,0x3ec389511af4af02c9f3723c0fdeb105d1cc5a9c -9104,0x404e933ffd7ea2b1044a40c0b3c6883cefadf43f -9118,0x44532990d085c099e22f480ca15d8f0ac49cfe5d -8981,0x450aa5c9a0809e5e704254f79cf3452031c01ede -8960,0x4513f50921a0333f2067ebdb9369daa81bcfc24c -8885,0x4793c35f775a4abf2d503278cfeffc04963076b8 -8917,0x47e136cf4a96e1afa72e19022f9699bbaa1be60e -8865,0x47e983e30e97e4957c805244c574b74fb3d2bf47 -9086,0x48261a0ea65cb1427e24b3119cbc94cba1b752c1 -8988,0x48b4f0692eaa84f1961b64342ae746d40d9ac2f2 -9041,0x49c79ed56df2301ec851fad0b2a566e4fe38fa82 -9035,0x49fd4671dc7f836be12cedef798d094437c8dc00 -8859,0x4aa89efdd4050dd40c1a3863294b6b148fd297db -9047,0x4b4d63e5044894184e35ae7854d0e0c749e418ae -8871,0x4bf6e44a27e37f195c15b3075cbac63c46848375 -9069,0x4c0623ec2cb2b1f1a5a19e18ab9a2df897e1f746 -8939,0x4d46d8c1ac1c80538af5002bc64b6412e4ac1023 -8932,0x4e7f21d92b70fcbbcb6afcb8bf59420622c158b3 -9032,0x4f04f8ae9fc3a9539d00e5e69c3b262b31220d00 -9040,0x4f2b1fbb51bdfe1471b3de6e88c909e77c042b61 -8924,0x4fe1addcb348bf191164fb10605c8beec8d42abd -9053,0x5035d9b44c29b503ebcc1e7d6223ae78a37032d6 -9025,0x50f7e7eef0fd9173f6b33ce4eafedec606673bce -9068,0x52a71b10ce5ecf54b6a583a8e1a2a6adf8c72daf -8947,0x5333982087c2db4f85d0ed2cde22b22abc0fdae3 -8945,0x565810cbfa3cf1390963e5afa2fb953795686339 -9073,0x5800249621da520adfdca16da20d8a5fc0f814d8 -8914,0x589236f61b971919f49014f4633484df356dd2fb -9049,0x5a21d4f5c1cf7fe5fae9eff8bfda519cd10893f2 -8857,0x5afa7ddbce8ee8862fdf5fd8c546bf32615d2d9b -9046,0x5c1ea29930e052f218634d807667a1387eb4c44f -9039,0x5df06caa38ed2c60a49174176fc745c532426fb4 -9038,0x5dfdff7a95a9a8472e6439afc6b760545692ca1c -8889,0x5eb0f5958131b627063750a5d4efae37b258c5d3 -8890,0x5ee146ea004afbb339d9c4164ca04c89f00a021f -8948,0x60e04bab3d82a16a7963bcd44b494bd9bbb5f8fb -8923,0x60f0cc35b10b8f7754fe7078bad8bf19204739d1 -9008,0x61dff4a2a75523767b52b0f6043a7aa96ea80846 -9022,0x64b3149a51795e4bd85505087c2aab3d9b50d957 -8958,0x653a03b511e6fe9e80eecb2bbbd36dcaf93cf147 -8944,0x657a9bdd0d2e89c1fb1f0a4c364dfd7d1a3702bc -8926,0x665288be03e0397d5bd9838732fa1ec3189288a7 -9020,0x66a1274fff5506c3a69dd35dc5c9a60e63fb3107 -8892,0x6712ce4f5ad375df6301d5010c1039f393904498 -9043,0x6a8be071c1f37e0a5abbbc7700769548f5bc1e46 -9024,0x6c7a28f6ae2d245b0130520b112fa3544a06d9ae -8996,0x6cf8d2bf45fe99e369db145faf6fb606a50b27f3 -9031,0x6d4f89284e06554eeeef79215a823b6f9d7b48d8 -8880,0x6ec499b869716bb479a843cebe048a90ec6d343c -8872,0x6ed4c3ea9b25cd8bdef9b3101e3bf5cceda01402 -8970,0x6f0df1d93dc1673c933a81eb122112c511d119a1 -9011,0x6fedde12105f47eacde493ee294d207bfe68dce5 -8900,0x717facaa298fa151c6ffad47b5e9c51c41bc2199 -9119,0x727a2f78669f765dd3a721a8d6a2467b68ba7e8e -8931,0x72c948de38c6c8211211b00a685ecb1e9ef88dfe -9096,0x7398da0704ee25025d300a96197788378dcb1470 -9027,0x73f4c90674a91d8e49286ee511bc4bb9c7e0f5a5 -8929,0x7438c178ba5b6c014f984478de175951b39a2e24 -8999,0x749f09be3ca34b89f970ff3f594567d072a98e5a -8862,0x74af6d7b484729384342dd68c60d73a3bf7081df -9009,0x767ce21d9f9dc90f524b0c67ebdf50ed5ab26041 -9000,0x769874c359e23356a9fecf11d710f05513f033f8 -8956,0x792b49617491b936cbd61592f2c7d2e07d17e90d -8952,0x7a369b3d9cba81d338b98e25422335bfd5d821bc -8928,0x7ab75ae7f84a5c5d696b619d866e05a27b1f4d7b -8934,0x7b2f652b2da7e0b1077b02d5e629966383d11161 -9037,0x7c6c373190421988fa31e64f369c45205676c1f0 -8955,0x7cce8183aee0d8e2f3f946fa142faf58727dfaff -9061,0x7e54f4c0acafec318af1f5d1dcd373cdacc32622 -8891,0x7e6da87fe69306caaed675ffe4e7dc0ffe3bfe4d -8977,0x80589802c34858fa9ae0c526bf8972d5ffc4d9df -8983,0x80b51629e095918bc15c206f1aff5a590cc729f8 -8992,0x80e51327a393e6d6777c85c9ac16bad45086ca5c -9087,0x835531a17d01df9ece37fd197e6b232fcda981b3 -8979,0x85197bc6a4753829889cf31193639f2429a80a46 -8883,0x8537307810fc40f4073a12a38554d4ff78efff41 -8893,0x85e9f93cdcc3b617b0790ac351f35b14b6bb9363 -8940,0x86a0a4a05b73fc03bec0ef7cfe1cab8cc406ce6b -9114,0x86bd913ac784e6efccc2296053c4aceffac127a3 -8902,0x87afc932a440bac07a50aed721eb04f4859a63a7 -9019,0x89800a17cea97db4a8f0705d7f8da770032397f5 -9057,0x898aefdf7d1ccb3dc78179bbbf52acede878d7f2 -8897,0x8ac0a7a1f26c6e074c3c063ba5aa5b736db6724b -9033,0x8b57c3285a2acc9af1b813fa21ff99264943ff00 -9018,0x8e304e89dc574dc3296bbbf94b3f81ae256eb0de -9013,0x8e9e34fd67634f33b2e2e49d083fbfd8c064fe9a -8954,0x8f4ee8ab18ed25f787bc81a8d5e7b1d0deae4322 -8874,0x8f5e377b2b07d1440f7f7d079f402a53c97159d1 -8971,0x8fb9ecd91723fe037012133ef8c4c767cf0d4514 -8901,0x90b4d987981ad1ded5bbcf9ee2928e811f81e8cc -9084,0x91ce463148bd7695d4db41f4aa36088e502428f7 -8855,0x91e4c4ce0b693058b7f5e05fafd6cfc1af75c76b -8887,0x91f48c638ab0457fc807ca40300f3acf1688a4d8 -8943,0x929625acce321770c747c38cc989b689ee823d90 -8976,0x92c7a51bd507736ac0dda48b5f35a4aad0c2bb4d -9066,0x956693ac35471196bcda10e6e60d573becf51180 -9074,0x9627d4f1241c41a61992873cf4c78066168141ce -9054,0x96424dcbe8767a5ddd8e0e40291f6240ef78abdb -9004,0x976973618488e452027f4017b088ea8374486fa7 -9062,0x97c78339c953233c98f016fe5f93918154a12765 -8881,0x97fad2597a51bd04c53f107de3580dfd545139a4 -9105,0x9879a2b15722374243843dddb79e5e6872aa74b8 -8905,0x992e0ba66012858538ca20c621f9ccf09fc40424 -8920,0x9a956704888a805c954b2c68d4faeeaf1eb45115 -8877,0x9b76a24aab4d578440fdd5139f5e9d41025b6fcd -8922,0x9ba3ff23c5373a9c7f0ceb8b6b5f4c98cec7d922 -9099,0x9cf7443685827419b0067fb2471c24969eaa716c -8994,0x9d245fdef1164a4d4014c2570fd88855ea9545d0 -9036,0x9e51cdbd0dc54e314b6b17c69ed34a98b8259a16 -8949,0x9ea1c30a61f33c82762df9bce404b19735302b88 -8913,0xa3f9a7a13055f37479ebc28e57c005f5c9a31f68 -9052,0xa7f7e4fe8e4cddcd9969bd3fbcff67000cd7de47 -9067,0xa8b549b46cab71ee91bfcf1861ec052c5e76ef3b -9097,0xa989d213dd33fe2a3fa93f502054c4e1df9a7220 -8888,0xa9b203773c68b4edc17d4bd1151f6b2bbeb36bc8 -9082,0xaa2f2b6cd33eaabb795c6db60aaec599c8450f35 -8870,0xaa3d9fa3ab930ae635b001d00c612aa5b14d750e -9056,0xaa95ed59fc4a4e0acdee31a8981cd8f6f2ae6445 -8918,0xadbc2c4ffbd5de29e2ef566e4db67f8a839dd66a -9065,0xadf21508f72cc5b28f70cb22171995f817d5282a -8860,0xae7ab947dc04b2e4965a9d99c109c65b4f0ee139 -9117,0xae9482ce7f2a472800d5e7f7f45ed12f17c90a8e -8997,0xafbe4bc31b3e5636e1446a987a42d8ad5b4eccf2 -8906,0xb02113a6b679a20ac5f6f7c05025f4b16395aeb7 -9098,0xb113d72896d4874111af00c9499b5a64e9f1e3f4 -9083,0xb11c04afc9b61ef83658df6062e5f62fe2e0c8af -8978,0xb2dc051ba9d471dc441755871a5096b291cc364d -9014,0xb442582f235cf3d039a81f067b02f5e66f34a57c -8882,0xb48b743b85bf955c4c039599a3ecdd6175b6787f -8961,0xb568433fe45ca1ea2a7ff98ad0746cea7300c12b -9034,0xb584d4be1a5470ca1a8778e9b86c81e165204599 -8987,0xb5beec0c58db0ba0bad09e5ea6af01ed0d58b735 -9089,0xb6c103b76500b262244b9879fe7f7e1c43fcf8c2 -9064,0xb7a6dba1d9035cc7fe20a2e40e5b2fb2e8f5ddf9 -9029,0xb81de21eb8811968c03c8ca96b2c5901ee849000 -8933,0xb8c32878c69f4cc4705d0bd99bca082c4eefd76d -9092,0xbb1f202095be99000038d8d207c7e6f0f85a3925 -8936,0xbb7287aaf5fd6e4cd245e88d000ae41d79af50a2 -9107,0xbce4cd69fefedb6eb076cf67e55bae858567136a -8985,0xbd57fa1470145289407992279c684aa42c8c88dd -9070,0xbdf50eae568ecef74796ed6022a0d453e8432410 -8946,0xbec1741a51e2b06617bbfb145492109a630e05cd -8876,0xbf47f1b6aec8c1ec47ceba21f3b868567738c8b2 -9003,0xc457f777402bd8449dfdd2a3481c4affbef56747 -8967,0xc6962fa49e9221aa5c1ca90b2c1b721678c176a4 -8908,0xc87dbb87cfdd14c5b96a90c65c30bfb195e06e39 -8873,0xc893bd7361ac00a2879d526016043fffde5cec55 -9006,0xc94bf98cfe43d8c891d4ee43a47435be361e5d15 -8886,0xc972ab9a6cb41b4bc437326af7d2b45cd59d5256 -9088,0xcb473d87a56b4609a695753711f727e5c4335ccf -9045,0xccd5a9bf2250070289a719c153390cb001316c6e -9106,0xcda819182c2bbe351644ed5d429280ab98004b4e -8911,0xd075c9fa148161e4828977a84a4054eb048172dd -8984,0xd10cb28719302698d7fa685d73dccd330b6bf573 -8878,0xd23678c7d554cb09ca7892d6521d40bfb02a05c8 -8991,0xd3f39174ee7937381e19d682ffea40968266fa3e -8965,0xd4021383fc5135a7082e6eeb473ae7abf109d024 -8969,0xd5087ca5f76dc6785ba03dea23adcf41fe228df5 -8951,0xd550ce2bb4cc954542efcf23ea1147143bd8f77c -8941,0xd5aa63075160b2a00edfa3d7bb51acdb81af7678 -9110,0xd648a4f9db4aa872985b35b9abb9be642c0d7f2e -8963,0xd6ff172fc2caf9878768f768e3176e9ef2ed7b22 -8894,0xd8cead962b7d12139ee62d38fe9bca8e04ae7fa6 -8875,0xd9a317288843d8ae4c80bc78eb9af4ee21aeedf3 -8907,0xdcef080bb245a3314945436b41c08206405b2cc9 -8861,0xdd975f8d5b2f20d574e6777001e236a05f4ae3c4 -9012,0xddc3a2bc1d6252d09a82814269d602d84ca3e7ae -8953,0xde007c8a9a7fa8676f9a751b7bc1ef38af76757f -9028,0xdf4ff02e2dde3a08590829d7398cc31b0255bab5 -8925,0xdfd12c02b718e2a9ec97852a5b22a8f9ae194cd0 -8974,0xe00d67f732e6ed1158553fdbe9c6a151d06bed6c -8879,0xe0134ca3d7ad384070580542baee00511105befa -9017,0xe186a05fb020324a7ca61d80138bb1ea66dddbdf -8898,0xe72edec20d6c65a046ae96c3ddecfb93f5cfa52c -8903,0xeb03440702bb6de23c63ad1c5a24598433f59f86 -9102,0xece91f5954f65f4116fd9e854c6b8dd17ef9150b -9021,0xf004c4c51b6c026247b5910706ee78134299eabd -8937,0xf096fd1b47dfc14d2320571c5f7ecdb5cad219f0 -8980,0xf19516273aff391293ff79822c08e66932b77aa6 -9116,0xf1b6f0c071ce884f53b789720e3d8b523ec895bb -9010,0xf50ed30a7a6951d830bc4e6498b5abc7132a4f6c -8919,0xf5b588306867992201fc78de045bce01be6ea2fd -9016,0xf5cadaa4769aa6c061224602b687affb5119d5e8 -9063,0xf68eaa807c14b74e3fed68ed91a6ac7063f8570b -8921,0xf848907549f27cc5424ee0b0f8fe5238113632af -8910,0xf873a063d74ae5f27aac1ef6317095a383032775 -9072,0xfaafb26c06a45962e97452e5d4310df2586623a2 -8966,0xfb0eab1261bb71254d4a8cc6dbf151b53d34781c -8863,0xfc42bcaa16a54e7e48d0c39e4713dc5923bd551d -9023,0xfcebb7f5f3827f2fb485d24d1342977630e6ac6c -9095,0xfd1256602f99a8cb2b43114e41af4ca0b4c7e47f -9079,0xfdba28d084e157938cfca7e7091c17bc4d6cd2f0 -9001,0xffb09850fab52765b5e48eac1aa3ffad620c8fce -9005,0xffcbd42167550edb20d64f99278537075723b4c8 -9111,0xfff4a34925301d231ddf42b871c3b199c1e80584 -9288,0x0909256fd0f4f450fb2cb770ed4bec23d36c163c -9247,0x0b820a3f915beec49423229fb32968f69a359516 -9276,0x0ed2ab0efca649fc0dab6251580884c10f474245 -9282,0x1105f513257c0ced9f4adc099d9b96ea8b78f50c -9249,0x161c7bfef0eb6c0618881249871d1363330242cf -9256,0x20ea267fea6502cedf46020486443ac416276bac -9253,0x2739ee562861696c2c015510b57a476d5a4811f6 -9258,0x2c0677636cec1852f992a4cc3ff5e4691253ba77 -9244,0x2efbd59c43cb1716f08ebcd132314d32a74a5d95 -9259,0x307c3487e0165a6cfc384165d2d914a034ac8c90 -9285,0x3169cda6574f291726206f442f253b85b41fd9c3 -9286,0x357754fa82b746d91087e5d49c522c82541af837 -9240,0x362a26172962bba6c659eae820f21f17bfa41619 -9275,0x395edee07cb8c0d76fd6fb29cca4dbe046bb724a -9238,0x3dea6da7cdad789e6d947c3e983ab4f996a7bbc1 -9265,0x407da3e66095e28852774d5b88a575d75fdc6af4 -9269,0x4209a22670281c50ff062222da8469478b5b59d9 -9246,0x48457693b5e3f2d307eefda50a79f2e88eef229f -9245,0x4ec6bdd8ee95be2e16852702023e6735f2856a44 -9273,0x5206795726a31ea0b0c04de6dc302cfc5f49b215 -9291,0x56cce1ebe7b640fc7bd56710cc0c453d282ff927 -9243,0x58df0e225033227c0a376d45e4e950fcf3bc9519 -9287,0x59b344fdb56887b91ac2e6c3cd820f26cfb6d128 -9270,0x6053cbeb363a0b4a07b1ebcc7c8c3863ff546f7c -9257,0x6beba35f93eb604395826589427d228b81a110bb -9261,0x6e01749dd294b60d67389c6899368d28d682a265 -9236,0x71c9ac784513fd58ee174ee851e993afeda1a0b1 -9250,0x831d6c0a82c3c734a55e80029b1b4e2a04ed3fd0 -9251,0x8a1d036be71c9c4a6c3d951cc2a3ee028d12d3fa -9274,0x8a71704f0f8891f92417faf840568ecee0be0918 -9254,0x8f12491f3f067b8570cfb75f95f8c79b62bea438 -9266,0x92a4cbfe0013153e70d2b1f9c5a07beb8aaad097 -9272,0x93a753891d6d32c56c6557a2015de238a3830556 -9281,0x965a25cfbbe8185246f4d96b80cc4dc860546f1a -9241,0x970bec30e2c5a1e435761332bd3659ad6745d839 -9290,0x9739e02b9c9872498a3c7324ce48673e83d9188c -9283,0xa049a043441de9d52c190fe792f5efb4d475c6dd -9262,0xa0a3bc64eade631a7a6a8f277b001019a58ea9da -9255,0xa22b348ffd337f1e46fb054ad48f2feeaf55e75c -9260,0xa23b0ff393801d07cf3ff0d87c3e60b10a39c5da -9239,0xa3c5ed39b8665854212ee0b508a806105e249935 -9277,0xa46fd59672434d1917972f1469565baeb57ed204 -9289,0xab954db48b9f348247e10d4d3d1f185e7424b3fa -9284,0xad5b3ebb7e4aff721676e1731a10ccefd60e1589 -9252,0xbddbc5d14e977b1a4f65ecaa977f2c192e3df903 -9268,0xbf1e8e287579afad68cc82fc801c3c07b8c5ae59 -9267,0xc25bf8d11750f378e7ec1ae079d2ce18e50e4afc -9263,0xcee6efc855dd81bc679ab9da89188e9820004591 -9271,0xdeb139f64e0af3d1531ff4ddde3cc39f4e32f5aa -9237,0xe31fe9ee0828f255ec3179b45bbdc5f6076e550d -9248,0xee732752d86f2c8b6ac57be43a4ce0b636ff35b3 -9242,0xf5cef9011c259574797f381caceffc0178355daa -9264,0xf63c350217f83ef8cd93e09d0e4a41c5dbca3f25 -9292,0xfd555512d1e5eceb74335521dd696555ea81ea93 -9356,0x154b08ca083bab868f8794e8b517ea0fdd92d6e5 -9350,0x5130f6ce257b8f9bf7fac0a0b519bd588120ed40 -9352,0x8e7903ca4305d2864b8c360da137f900d315e867 -9351,0x93bab043d534fbfdd13b405241be9267d393b827 -9355,0xade63d643564aaa8c2a86f2244f43b5eb00ed5e6 -9349,0xdbd4ffc32b34f630dd8ac18d37162ec8462db7db -9357,0xf33141bc4e9d1d92a2adba2fa27a09c2da2af3eb -9353,0xf870ef6dfe61e9bec5497874ba05cbeb94699147 -9354,0xfafc12d8eb19a206ad58fecc791ff7330699e752 -9729,0x01ecc782531adcfb8c5a58a2c7fd544ada946b81 -9711,0x02318dd7821a8b9395d616071c1d3573c5886c02 -9727,0x13c5a1444cc7cf0bcae366ed5bde13d46dba3311 -9745,0x14099863f0b6490759f9d8bc5653cf52b7ef38eb -9797,0x14f0321be5e581abf9d5bc76260bf015dc04c53d -9728,0x19405e9d4594cbfac18caa9fb43fe8c4af893ac5 -9803,0x1950e8e31107d0acc5c4b44c34db7a5935f98278 -9783,0x1c0c05a2aa31692e5dc9511b04f651db9e4d8320 -9759,0x1db929398958082d2080aa1b501e460503f60467 -9777,0x1eed080b8743ce5015a57bb1c57f1c05016e8245 -9705,0x1f6ee9ce38e6beeb968bb91f755998548d3165e0 -9770,0x24663e2b5199d5e250095734756909ddf2682e26 -9814,0x2a2e60f6354599608dd87ffe65fc850f42490977 -9725,0x2ab4ae1eae439bf2a3320ee337d72fc067b326e5 -9710,0x2b67d4f9407f772374cae8b010db36a770c2c3ae -9717,0x2c229ee3ad3fdc0e581d51baa6b6f45cc9a6ca39 -9730,0x2de291709980dd2640c33e614e97d4e0ab4f1a27 -9790,0x2f8ff0546a478df380f975ca035b95df82377721 -9726,0x3138293423c06d81cc9ae78fbcd3f7db6323b83a -9708,0x32c993a42a654c63d296eef15c5acd3ffd272165 -9768,0x33a73e76657ffcf34fba88c85c6881448fbb0d79 -9721,0x3700414bb6716fcd8b14344fb10ddd91fdea59ec -9723,0x38e5462bbe6a72f79606c1a0007468aa4334a92b -9819,0x3b37a1612f36b72ea5c621c2db82cb979f008f78 -9798,0x3d0ac6837de9acb4d69e7cc1686d2c151b55359d -9699,0x3d444c30d056f28e6d7c5656225a0600f242f0ae -9794,0x3ebee70505d799ddc6a92af32a83925eec4fa8b6 -9732,0x40c39c462264ff8e0c372d3f18b6f0444d048e43 -9818,0x41facbbd06078d590baf2e606f0d76082ac16aab -9731,0x42562a9253094de372a7595ac4e85667135b84ac -9780,0x44279c2cbfaa14d354144895b0c7771c597d6944 -9734,0x46a79cd173abc6060eccc26076b15cf6d9cfb49f -9782,0x4c4c649455c6433dc48ff1571c9e50ac58f0cefa -9791,0x4e05073560b9377e5561b559c9cadbbe7112e38f -9820,0x4e67414a21d6a06332e17f24f51a8d18d1a7d5cf -9697,0x4f60f85b512367acbc23e28a31d1d47a73941d82 -9760,0x4f70287526ea9ba7e799d616ea86635cdaf0de4f -9735,0x5018e94a46d0991893e278548c79b2142c761e73 -9771,0x5059edf37ff5a84fe3a14662ba25a3234824d0d6 -9779,0x51f4e1546a8ea32cc1335d63ce1fad64ba76cdbe -9744,0x5501cf014e6d72701e6d1447e12adb16af37af26 -9719,0x5507db57a67c029a33f0cc89b641c1963f4c9a4c -9713,0x5956f75aa43dd0263b8d417546737d0f43f80254 -9789,0x5a736e0706066b4c3f91dbd1599a2c83b1efe6f7 -9772,0x5e68918bd08a33234257f3ba9004b158e87c5854 -9808,0x5fdbeb52adfc6fba26ef0e4b5e9464c63a15467b -9751,0x61a307cbfa9b88f2be019915bd597c99d9690d96 -9785,0x62eb6e22bfcfc0a76bb8ac61dafe36de2da2564e -9755,0x6417bd2e6ae85ea6491de96d1b3b43ec2aa75302 -9776,0x64e39114ee9f12c15f565ce39a8fc60061e1ed70 -9806,0x659641f71525b02bfe7203f3e5113c48e6736540 -9737,0x6671ee8ff31fc5cb66c5708d7055b1d608304b2e -9701,0x6858051d5bdf9c297ef5245148da8f1dd4dbc255 -9743,0x6a7ae5124677314dc32c5ba3004cbfc9c7febff0 -9781,0x6e5d83f12c5bd47d691180e1377fbe1e433c8fe3 -9752,0x6e9c790fe4329e012bbd7df93abca42276ace374 -9706,0x719e5b865de407bf38647c1625d193e0ce42111d -9796,0x71ceb3f645dcbd7549caaf85a224db987ae92630 -9802,0x7215f38011c3e4058ca3cf7d2b99033016eefbd8 -9767,0x76210bcebb5df31d0365b38b7dcadb01ce2beca9 -9724,0x797136fb4dd214fef0a510b2946ce58cab390218 -9707,0x80ffca5591d271ad19cc2e25eb45cfaeea26efae -9698,0x820b6d50e1afa711d9d1276afab4d747662a9b69 -9813,0x84292b0b26139d6ab6c73ed06194d41c70ff6866 -9703,0x84d5657347cc2bed0a4d6a82c0a6f3be1a021cc6 -9778,0x8ebe03a1963901f6e1680a6234643cc80de2222f -9714,0x8f14b50d5e56422b2759c4fdd8d148bb28a6599d -9739,0x90872c7b3b19600437d4e9b8e9df127730104618 -9746,0x95e24048ab1a9086b2f06107df7ec5e61961951a -9754,0x9a7671db5d28dd85f05d67ac18ba38f4b3862904 -9750,0x9aa8769d207ca65dd9e578a566e40e02722bdba8 -9769,0x9bbfca4d9ecdc53f56290442d85c8e55131daf00 -9718,0x9d34288bd76ce6470e897ec1e088af4ec7bbb065 -9757,0x9ec0f9187e579b0142bd0ed2968dc3a720e3f9f2 -9709,0x9ff3ee78368a8c57a37889b4adc4bde707cfb109 -9716,0xa1e6c990af715d819352f2dc42ba8ec31a5f76da -9722,0xa33b9b89262d1f2e2c86c3306c1f20e845fb69bb -9742,0xa450f4023199cd03248a5579e9bb0f5aa7baccd6 -9773,0xa506cb45cc949a909f3ecd3f7a1cdcba8860ce2f -9774,0xa5351d5f6fe586a7a2ee0a5b7f797f5fb686082b -9815,0xa5523e1092f57f292af90cabf92957e09bf9cc5d -9793,0xa56df50d6ec63c5e6a3f2e03d665d07338a71fea -9738,0xad1e25f40b3dc72728fbd9093f0ec1b8a0c39306 -9712,0xae710dfcee96781ab56fbcbe2753e098a5ea3a0a -9753,0xb0051e20a2ed91af525a2edc54aed6735e7fe892 -9809,0xb1daf02cf0d82555f3b3ebf4c396177698d2993f -9700,0xb3046d2447006bc7e9eac3f39f4a291e0799c6f7 -9784,0xb476c5976006c188a84d40dac2f815d43d61c534 -9715,0xb56deb6abc9ab381aca33ddb7baa9fcb4c2db8f5 -9763,0xb618cb0da4586246f61d1418e684218068a89937 -9765,0xbb05bdafff5b75a7235ca814095a4997ace0cb32 -9733,0xbc587e41ad8f218e49874d5ca62e5debde59aab5 -9795,0xbdae9e6897e08f7ceec538c4cb0b7f413eec3497 -9801,0xc07d09f0237519b66986c2f38c473053d58afefa -9766,0xc1ae2079d44fc5271a4ae23fb9d4f53bfb43328c -9807,0xc210e31fb7210c39b12511a3733dc0855a8db833 -9702,0xc3f96dc9f8a6c543d4339d4f699c847ec3e8d928 -9792,0xc8b8567facb677b5c36fca64e8a2b544f0bc8c97 -9787,0xcaeb4089409d10dec2d69f8762c61bf6952d2a45 -9775,0xd04aabaded11e92fefcd92eedbbc81b184cdac82 -9756,0xd0ed74704c9c5268478e7ae30b8f580e79c0b0a7 -9741,0xd0fd4ade27add51b392e018a0c12d1d51314f9c5 -9812,0xd59d7cccdcaacb4c283ba6fec950edf7d087dad5 -9736,0xd86d16feccf908466a3065d504bfb12c19336060 -9740,0xda6d2da01b7141ba3232025dc45f192eae5569da -9764,0xdbbd0295ef049515a5ed1803995ba7296b866565 -9758,0xdd6bb8614360019f0890d8d12f040608b538a5ea -9804,0xdd73867c509e06f5a01f4f923811060ef3f2130e -9805,0xdda86c5ab04f8d671e3ec0e79f396b3f745ba2ae -9747,0xe163259cd7c52f447a1b93fac89d01cf8395e9c7 -9696,0xe1b7e8d3b385a19173ecd7deaedd1368f8706263 -9800,0xe3ebf3788fab3de919da7332e7b72899414d3e80 -9762,0xe64fa0b6590c23a8d4c53d2a04d253b320e2d2af -9749,0xe99447abbd5a7730b26d2d16fccb2086319e4bc3 -9799,0xea1bd0ae3ec45e32a6090edb445dacfc083a8db6 -9720,0xecdcc6181f17418efe422d5218d4cc63ad8c5c73 -9817,0xf04ff1487bb27fa6a83f6276a55ae17eb8b3c581 -9748,0xf12d64c674a5a8ae89d9f5823a8e1b98e879632d -9786,0xf25770eb8bf44c7d4bf43acb72766eb4e97ef852 -9811,0xf3ceed05921a8b901775b993562902776afd1806 -9761,0xf640aec9141c14d3fef5f0514712d23e1436e033 -9816,0xf860f90e1f55e3528682e18850612cbb45bbf1bc -9704,0xf964477d99dd8b593794d035644104b2e27aee4a -9810,0xfea09b732138f0b907356240437ccb852b9a1c09 -9788,0xffa0b13283b76e1278fa5072b2da04cb9534b937 -9942,0x00ca56477620b13c7b1d4d221aecd960ab697715 -9929,0x0517ca29c3948779cd61849a51dca85449fe3128 -9953,0x0bab493632e27325a3f6c02de14c04ba006abe3e -9961,0x112f30e9c369af29c27b855542b34549b35125d0 -9979,0x141639034301d5e66dfe6961e8fe173d4d48ef3b -9932,0x155820a84c4ffac30f23e46129a968370fc4dac8 -9933,0x1bfb4c5d84aa3e6564491659706b09a25a6819e4 -9963,0x219717ef71e81c06f82aa2443d5e6c0375883c1c -9974,0x2663a6f2768c66a8483d0bba4b9dba849686e50e -9926,0x2c7bba60bf6a8b6b042a3b43c9349345da7ad078 -9927,0x339d70eae3a7ca14c515f5924dea4a2862d75a54 -9912,0x34e125352967e55df11cef1077d89e19e83fcf60 -9914,0x36eeae644b247cb285f4c299c72e6a56bb50f2d6 -9967,0x39d1d72cf5a46176b0cf135720d5b56d0ccf402d -9952,0x402b16adae502e5b5a49f4593face220ab0b6dfb -9906,0x424ea8a23f9c45184799b75bf0b13ab2d0f8c414 -9970,0x42ed2bd88bda5c7b03f454cbc9303e35a0c3c57d -9915,0x4735d87ecb17e6e0bbfb5e2afbef541643a7968c -9959,0x4b076884091b24db4d80df0bc4f546b5be8a45df -9964,0x4b2891a7ac11d2b52560d4b78daa9bbbfda13e6f -9973,0x4c424155997dd42435d137fdecfc64ea619a5978 -9909,0x5791ca4d6c4b1eaafdb024ab3b0726d3baeae82b -9934,0x591005085116df73d5b05589e664762285740f05 -9923,0x5917f58db34fd30c21f1730fd894063fe0d14187 -9946,0x5c6079193fa38868f51eac367e622dada53cf17d -9971,0x651a2d5a89c585aa12f2b7bc5fe0e7e8b4825a9d -9911,0x65e63475f1d006c3c2e628f66c1cb6f270124fdc -9917,0x67944bd9dba951b1221d6b838109fb581c47095e -9919,0x68a329ef5f43f92662690b6593722eceef622b27 -9950,0x70e9d7dbdb6e276e5e298488f7da913509239edb -9955,0x7115f16c3db03056f67963ca48e227c3cde59363 -9956,0x728ddda91e57078794fc7be6acb84d7cc51d760a -9905,0x748fae3ca04627c866a009315f004274461a9455 -9977,0x75079acaeb581e28040a049ca780e543f722ebdf -9947,0x79fe2ec964e8a361188f4020d3def3bdd023d152 -9960,0x7abbed988145058d8fc9a0e782af01955ac337bc -9930,0x7c425f7f4c1dd42c0bc8f59e8e3604ab5a0ecfe2 -9980,0x7d3137bf099fc4d1f45739211749be9f61e75372 -9957,0x8235e1d9f0477e20cb76f456cc3b21c9958db018 -9921,0x84ac58149abdc777f732f6a464c1538ae2841903 -9937,0x84b6a7f1f47056eb90ac641e76fb7c8d3cb46b4d -9910,0x8544b4c89f2d90adb1184df4a3bc3e9d67118867 -9972,0x8aeb224e73c8bb7dd3b1b9c46ebdd2359b5dc50f -9940,0x8da9cd7232611fef7b1f05ab80ea9bb977f52a79 -9965,0x8e3ffe1febd4034bdbb5b233cecf7981849c583e -9925,0x92dbea6fa85a1cd839b604d1f2852abfb7fa2897 -9968,0x96b6a3e2048db602ffd71e5a2c7351673a77e1f6 -9935,0x97b4d169014c15e7a5890123992fa543086e6b51 -9958,0x97bbdd714043ef5df47c96f21bbf968ca283baf5 -9976,0x9c5458761cbaa84ee575f090e804e2874dd1e784 -9920,0x9cede6415a956205a9c21c88d34f1d2a4e87e8c0 -9948,0x9e7ca89eab560c58f738f92e70f2922d20ecd01e -9918,0xa12d1a1ed7fa7e7201274badf4dfbfb577816c14 -9913,0xa51721114da446e9de56c9d3f2b8985f57c12763 -9962,0xa7484899eabb8fdab150b48bdb60cfb73b9d1517 -9978,0xaa40216ce9f6b9baab726fbc83a184944a993b2a -9951,0xaa47c9422fd5e12f1a364e659b593e1a10fc5436 -9931,0xabd485558304d923a42cb4076a045cbc145c10fa -9922,0xaccfd4db8ad6964fd5a6a52d6649a7484ee1a79f -9924,0xb3e829d2ae0944a147549330a65614cd095f34c9 -9904,0xb93968f5da93ad1065abb614c93d5e2836950e6e -9928,0xbec463c31b463eace35aae233c5d0a386721a192 -9936,0xc0d1bee2836528ad62c29753b900c39b278b609a -9969,0xc86faf9e55696f0079cd6b679292d0462f6798de -9949,0xcb2e195d92e9da36ea1f186b8df09aade31a5dda -9975,0xcc2e880fcddc4fbde66fb573ef04c44fd04f130b -9945,0xdb613730c823f260a1a8ac2dcdd6b8b82b491919 -9966,0xdbaf794ac3f2d5e90ff453332067f9f1a647c62a -9954,0xdbd42bcbe8f81d2da8d46fd158829c931e08cdd2 -9939,0xe17bc4de8e5d437bdda8e8c4874bf928127d441d -9938,0xe4251afa6796a94647b44bd53299d6ebf939ad01 -9981,0xe5dc0b65139a38fcb4db4ac8e1375f22a250784b -9908,0xe669f5f83616639d3442b03108233e0054afbef8 -9907,0xe79928db3a1884a825d002af9afe713e9da8a774 -9941,0xf38f968f3d54576ae67150f0f81d447462e12030 -9943,0xf90df76f9f576a7f8eea734f465cb36d2785cf73 -9944,0xfd6f41d11e7e256a4157b1e44bcfa5f013ee1a38 -9916,0xff44d03b6cf6a40bd1b6261150f3272243d569c9 -10152,0xfb1bffC9d739B8D520DaF37dF666da4C687191EA -10207,0x08f8611b996bf2f0fd7523b54e69bfb92dd43a81 -10204,0x0e510c9b20a5d136e75f7fd2a5f344bd98f9d875 -10192,0x149bbb210051851016f57a2824c0444f642833a6 -10209,0x19a5ec09ee74f64573ac53f48a48616ce943c047 -10199,0x220d6beedea6a6317dae19d39cd62eb7bb0ae5e4 -10201,0x293ded6b7d8d37bfe1e66aadbdec9f87fedbd37f -10191,0x2b7a5a5923eca5c00c6572cf3e8e08384f563f93 -10197,0x3a0c2a793a8db779e0293699d0ce77c77617fe0f -10206,0x3f1d224557afa4365155ea77ce4bc32d5dae2174 -10203,0x4e1484607760118ebe2ab07c0c71f1b4d9671e01 -10190,0x4f6a43ad7cba042606decaca730d4ce0a57ac62e -10205,0x5224e52878ab1c6076402e5c98f018d109131351 -10189,0x88cc4aa0dd6cf126b00c012dda9f6f4fd9388b17 -10193,0xb10ac31a6e613c6fcb5522c19f4bdbcffa94f89d -10194,0xbea9f78090bdb9e662d8cb301a00ad09a5b756e9 -10195,0xcf70e2f3567ba396f3dd04822f78ecd70ba46894 -10196,0xdf815ea6b066ac9f3107d8863a6c19aa2a5d24d3 -10198,0xeedb4e89e8032d04dfb3a33fd89344dddbe5ebde -10252,0x6c5927c0679e6d857e87367bb635decbcb20f31c -10253,0xadb944b478818d95659067e70d2e5fc43fa3ede9 -10305,0x0299d5f49dcccdd56b80f9fc2730ac3b75d3ffac -10333,0x02d902f9e4d29f6143e79f82cca68a1fb8ab4566 -10365,0x03660e5261c8effd8ec62971ae64e18c4d878a57 -10404,0x04fdd1602c0a2f6a39fe64bb0f315080386ada1b -10432,0x05cee65cc4d2c59cb4b927ad25b2d6326af7401e -10394,0x0daaf796d2b87cad097908399a0b7e8a1b8b52f5 -10338,0x0fc9b5f4aeaf77d4aab26e0a3dd27710415db839 -10430,0x1370ea7dc27c34d72a1a04207b967cdb817121bf -10362,0x1b1da1817b4a637cf81bcebd3357e3ca59ee5bde -10415,0x1be63db5051c126f54ad0807e1b471ad9aa0befd -10424,0x1c034c49aef924691ed6c6c3c28821ca2965d445 -10405,0x1ea6f492abac1f1b22259ab77e2f4b12ad6ab1c5 -10313,0x2181f152d02712cb51072c4cbc1ef8ec2efd6ba3 -10303,0x224cea1e3f8c8bbe35cb88bfcc364b3c658e78b0 -10339,0x25d6c94ce4d8822efaf62238c97f79d59dd05551 -10353,0x29da5213c75a1976452a27c8054e4c65ab0a3c53 -10369,0x2c79c0099ce6a9044ee6e05d5cf59389b387d22a -10336,0x2fd610e57b0239635de4ebd56c178d0d33e4b8fa -10345,0x30f5fe161da1cb92ac09e10b734de07d5c120fdd -10381,0x3a19b96b9599f63883c28a09f653b1b12b116670 -10385,0x3a6c2b9833abbf7299fc24c51751951e904f02dd -10352,0x3cefa85be7f532fdcd54adf2e4ca38eebb5576b8 -10346,0x431f7276f5998156285d2019b9128c93a6ab9bd4 -10376,0x44591ed28ace30a583e6f327716fb9310b15ce2c -10300,0x448130d049044d53ec22bc9750f5945ccd69db7c -10299,0x4d5094edc4d2c95ef9f040c2bb6ea888ad2c9297 -10316,0x4d986c946f610b5c9978871b00581c9047dffc30 -10427,0x4f06912118dd95da06b97eed80ba593b9e0329f3 -10372,0x505fb5d94c3cf68e13b5ba2ca1868f2b580007cc -10327,0x5364a429cc1647e4adcac157168711bd8185fd83 -10420,0x55824c11de8bae9a82215c51617ffe3a5f6a3744 -10436,0x55ca6826225dd775ad829c414ac040330f37f9f2 -10301,0x58063154a642ffaf2cf563b19358ac8ed12a4c61 -10380,0x585a164b9c5cb115499ca2df0bcccbd798234b75 -10363,0x5c8c6d48e771935a6cb14e5acc7020c026ec4844 -10423,0x5fafd12ead4234270db300352104632187ed763a -10361,0x60b30c1a7633e55248fcba53ab8f24f5c54984c3 -10388,0x6128660b9e4a570971759008824d082b6d282057 -10315,0x612d4eaf6879c4424e44d031d97aa3010e15499a -10312,0x62a16c6899e8fd8bec52307b0584dc92268eff8d -10344,0x63614adc11e3bd10bdd502710c7e9fb4870f476c -10392,0x6b04a55f47325fb095b866e38ca1e281f8f9d2db -10383,0x6d6c6dfd8a4f952f5c015eaaf4263779029b4cda -10325,0x711a5c8ba2bfe94e6a9d37d5999e5bcab8ce8cf1 -10359,0x74449d5b13839a7fdbaf00811da67240f6eda8ef -10320,0x7478d71889cddb6ebdc8b9295d83fc554525e885 -10433,0x75760bdbf7b71d9e68146684ef0a0c06701e6309 -10425,0x7a665e4191d0c8a02dbfb0feb550ae25f02c0dbf -10335,0x7af14adc8aea70f063c7ea3b2c1ad0d7a59c4bff -10334,0x7f9c2cb8393a5a5b1e0a1f38789da394912eda0d -10413,0x810cf5404f20270eb2249f07b5b1a2ed9deae16f -10322,0x88aa0ddbdd26495ece39d0190a282eedf29e1c78 -10340,0x91f4c95369b7d6224bcd16317d5de12257dd0da9 -10295,0x92c5da0bfdbfa9731741084bf41c12fa972f2641 -10309,0x951a84d192b143dd421bb2fb7a4cc866bb51d62e -10400,0x956f1787864b139597566a7e0198633c68bb6787 -10408,0x966572de3e5d5f83ce9b1d0b12d36038be6ec01f -10390,0x9715b7ac3e4f8c3a1c57ceca5ae4870638ed058f -10349,0x98e51d3893171882e0af0c5c9b88bd17591bc6f6 -10323,0x98fe79124697774f96433690bab80c3b5044efb4 -10331,0x9a62a11025351f4279e4af89228d16f69fa24dd3 -10431,0x9c6e912e1ce3171b5bc44f3acb23792ede9ce646 -10342,0x9d274f053ed5c2aff203f337eda8169d7f29ab86 -10360,0x9d809021d92ed6b6ceeb5053b1081fbc32ec6729 -10409,0x9e40c3f8ff0878d569a38ff328480cc601cc6f08 -10384,0xa4d20e7afa8f3cf1f089144f710d5bc28b0daee8 -10343,0xa567d65965b9dfb6389092ccefe6bd9c376f2f6b -10401,0xaaa001881225f33122ee1538949fd991098c4269 -10414,0xab0022fde9e19ebcff178bbc9cd621960ed7ffb5 -10435,0xb0af6f4e8f0eb91ee2d058ba0ddbf472e76aabc6 -10314,0xb28e765a5548c0bc8935e4390673c0d1b5073b7d -10379,0xb77dae11ca7364e0e6bdcaaa60c9dda5646c2d4f -10341,0xbb40ff27fd79ff1d275b4f955be16814d4b425ec -10306,0xbb66acb8b62c75400b7fa06ff49d8649ab8f2a78 -10358,0xbde95d32c1a9ee4f41f64507869679118008421e -10428,0xbe244dc919c90bd0229ae8a7dc35f1fd5f09894c -10311,0xc0e065fbf0c3432847c80bc72fd5dbddcafd8c58 -10399,0xc69372f4f919ca09bceea5acba2501c18f7310c5 -10374,0xc8a85072206aa8e4796de840b00efc331abd040d -10395,0xcc7dc8e2908c5d09fef768a74b82dd1c3197529d -10297,0xcf36d3ce95c16aa44aead1be3e1457a4731d3dec -10393,0xd26c0fc29d12b115cc86c5a4218c9acc04308853 -10438,0xd4dcb4e92b0f434f1297d5239a3aad875e25f675 -10373,0xd528e1c99b0bdf1caf14f968f31adab81c59dcc8 -10355,0xd5d62cd4a027d8cf65af86de31bb464825ad02e6 -10426,0xd99c7c5f9f3a1c2773570dd958fd7e48eb45b06b -10402,0xd9aaa6c2b57b1871919da39be3efa24d280ce9a8 -10302,0xdf703d8ab675b7373701986e1d34f3ac32c1ca39 -10386,0xdffdbb54b9968fee543a8d2bd3ce7a80d66cd49f -10370,0xe16d1f031aa363d211535d82b05024fbc3d79ad9 -10351,0xe27375343dd181fec634f69c46ba3827a6843a25 -10350,0xe66b612153daf1d7c5121172d54ea53f18b4497f -10329,0xe7043e97fdc28f507b9137c94df2e2a23a85acd2 -10407,0xe80624fbf823057f3453d405dfcb01195472cf93 -10293,0xe872b20f755451c66f415316fba2e6bbbc6ffeb3 -10375,0xe8f323f7eaa070c0e45e40744e3ddb656a6be19f -10321,0xe9c95e87d2728ac8d122da483926c1f4360f44a0 -10410,0xebc9ea5560bd549ec1d5066a6ed7454d91735485 -10412,0xed33a81153fbf6bf18d24029bc596d329e935a4b -10417,0xf10638b8a3cd4820ae1aefeb27c40354fbf68672 -10434,0xf12734d297232c566e1db790f5a1237132a68877 -10377,0xf3dab8691aed8d1ea640fec94df0e0adaaa2c24d -10416,0xf633516025678d72e35843bdcf90ac1fe726061a -10398,0xf665e9e3ab592207a8b6bf3421b2da79ddf6b8e1 -10298,0xf7cad960fe1f621ce6d05cb1b3240aed135aba25 -10406,0xf8780e00ce8ed2e79aec10908a169900ed1d4afe -10317,0xf882defd9d5d988d05c6bca9061fc6f817f491c0 -10328,0xf8cc321bb516770bc3ce9f2f1dcef32d612cd0f2 -10367,0xfd6fd41bea9fd489ffdf05cd8118a69bf98caa5d -10387,0xfdea4da674d280be218399f10452922c1404889f -10347,0xfee5ee79d3b4cf5dd309bbe4ba6520a399e77998 -10537,0x00b8f3e184e5daaadb8fda995bbabbcc1b35ae93 -10522,0x03fda24f00020c7b1713290ac114692c1db217e1 -10541,0x0427eaec63450789200a4702e23e22155428fc63 -10498,0x05bd0695af629185fd2dcb13a99a3f0e7ef73943 -10546,0x065e424d9b4136ff04ff5f1e0cb3013255a130fb -10552,0x07ec822ed7a30c55467ccbbbac0838567f629bbf -10560,0x0c31c3649bbdb866809a28f6f4146a0c4c043fe9 -10499,0x0f1952b985188ace80006794e0a7eb710e72fbce -10575,0x0fb17b077d3b76fe4c23842ea2921dde47141044 -10565,0x11f7124e71fa927457d7c3dcbf6ec8b058682890 -10570,0x151914f4681e2de71525a7056dc79b832a6c13d7 -10521,0x1680fe3450bd737c80e202003a9f0091c389db53 -10536,0x1896450330872d9492834c30d40c450ef0b529f2 -10548,0x1f6475d1a9c4cd87400417b447e686c93a781bd1 -10504,0x229ed380a02f65086d492fc5bcc0e83c9515473f -10550,0x22ef6e79e1a8448c82b56160424d67cdafbc18db -10525,0x2397787a17e6044ae7beb2fbc8865b63532047ae -10491,0x2741986f69be3d3c92b23dc2f201517939678ba2 -10501,0x2a9fed1edccb6b303e5abb8c4db169f9d303ac0b -10551,0x2b6b20ceac439042edc6efd3d41b11af66013e58 -10517,0x2d7d5f298067e81e8827eb686428881f4c988dc1 -10543,0x33d9291cc2eaabebd011d76765526f0c4c7ffe86 -10580,0x47f0af4bf073eda632a1257b2093ad32dec27177 -10492,0x4c29d3d9a594dc680a3dd922c77aefd5832e6009 -10516,0x4d68a3480e22c21f3b2c2a32d4ed2a2f4f36f680 -10557,0x4f0a416851634c2da17978837e86c5c9cb27fe15 -10520,0x555d9bca6a6993fda91694b753b64c21fe09c647 -10514,0x56b2fc450bfe527e19772688ab203d13060505c7 -10494,0x5cab1b7ddba3a59d307e6f4b55b7329e5abfb549 -10518,0x5dbfa614f6a0574333ee0efa577c388e7f2473cf -10497,0x606285b3fe057fef83b23a56132b37d66f8740a2 -10553,0x6378693ae65c151ca8aa78149deabf316e58792c -10500,0x65d1ab38a3318935f21ac43c7654d8255949bc9c -10527,0x67ce7f7bc81a074c1a425eb5c7d175f193438edc -10566,0x68079af32e370282908f07f3e46e928e8ac82e07 -10574,0x692777684e0a276f1096f56c5bafc0c7691f17cb -10507,0x6b0b101ca45c0a0add3ca1bd0a491156e56ab498 -10495,0x6c12c88a82a54953c22e201b89ba92f173b1c8d8 -10506,0x6c27468b42c10cf376fdb3345cb7954fd5c30486 -10549,0x7383fbab142f4e81a754e49498ceb501e2020c5c -10581,0x73e26e955d0b7007d65c2fea4737b764554d5324 -10510,0x7511e4932363d428cc09e59379beef2c345fa203 -10512,0x76870375c2c4b1965595551fc33e1e3ef0d87701 -10534,0x77263ac8e34f462ff09b711d38aec99e5a67f9cc -10505,0x7765bf5987cd6a2362d47d75f7d9768fa25893ff -10493,0x78d7fa35a0c18075b000f1acdd4f8ca23b3c5277 -10526,0x8413041a7702603d9d991f2c4add29e4e8a241f8 -10515,0x853c02c68c1fab2226ce2fa5eedacb05afaf6777 -10569,0x8643548aa389215575cd3639c681826b8cc0d29e -10544,0x870dd9dce39d9da54971d52a8558ee599b1c6b9a -10571,0x8820a3ef36a35d297cdf6ca7033ac5a3b284048c -10524,0x8e4bc58ec417cfb732b78433e543c106d774cad2 -10576,0x9535b6873bbcd4b765673d690001c65af8310ade -10563,0x9700104ac74c0d52e9441f3324adc97059ee429b -10555,0x972d38ca00c2e137d2bfb5db1d9b62ca2e9be890 -10559,0x9d7a498bbb4bdeeae409d965fbf2c7b61e3fdf3c -10513,0xa63f3147c6bae70a53a30e30421b1a992e60aac2 -10532,0xa77c7f3ba954f4a420d8c51e33de23b36daabbac -10542,0xab107e8497c254879aa76d07f6114286d68811d9 -10502,0xacb3ee994f0e87caf09e586fed6651e417347451 -10538,0xae854724f31f41e47031ca7c47252e09fb517853 -10496,0xaf397edfdf3a37af5036ad6910ad529bf7e3d3a8 -10564,0xafc0c2ac31b30440c507f3f74bdfc4c9ef470297 -10545,0xafd00d2176bc435c101ef425fa57d0cd83a243d1 -10533,0xb344973982d22a1dddc830b4877fb3eed5d54b65 -10539,0xb41d9b7e27757c1081092c57880641c3d4a276cc -10523,0xb5eb0528c6890f02d579c5cf8caf3d41caa7190f -10490,0xb9f7fe50dca579a65d4f872167767bc968a822b9 -10535,0xbad3c121641159242ce9f30984f490e4cc74bb1c -10547,0xbbad817c0acda0cdbf50fc6d40dec2f6a1e49575 -10554,0xbc565b8988f6d3963c35ca062d00b8bd5db8a68a -10556,0xc7a26bb3d7524b4f4fd199beb33669d504547e8d -10530,0xd3262d349c8b160efe487366f0f04dddebe4d561 -10509,0xd701a0411efbf80dfdbeffdc3272152960749ee8 -10529,0xd753ebf3041a084d902eb5a035c4c289ebfe0216 -10562,0xdacc998ec5193745b4eb6d9a9abea52d71448721 -10511,0xdc3375957cdf6e57837099f51da0cd56ead1c140 -10519,0xdfda34d21665485acda4c03da9a5a35dc4ffb0a0 -10508,0xe069adb7125fdbc577271441188d9b1b74146f76 -10561,0xe78d85a1a0356e02292606f2709e0c8ca737d764 -10577,0xea55fc8e7e0f97fea8e53d676ae162e09c5b593b -10579,0xef3da260582490370dad046ac3570647ece80ecd -10503,0xefd52ce11898af9e4e0da8b85e69a39ef3253ea0 -10540,0xf0941fc0a3521cd46973fbbbe06fcdd4ba7d7388 -10531,0xf1684d1deb902864f2eb0849f50409599322ce16 -10567,0xf18acc02628009231d7baaf9a7a24c0860dda6cb -10558,0xf247b4f1f97077e25c67cb8cb7e0d8718ba9be6b -10578,0xf3b4222b5b9708ae4008d3d220f70fab47433e73 -10572,0xf44ff799ea2bbfec96f9a50498209aac3c2b3b8b -10528,0xf5266be1d9ffe69f57c750aaf04d6d4cda0eb490 -10568,0xf6a101810698f5b399bab6ad95f78ee2744bab84 -10573,0xfbcc03cc87e67a50c8393cfb2e15475bd3ff01b0 -10707,0x014bb189673c18c921b29f32dd78c1ac3987153b -10702,0x053ba886857dbfae1fb0ee91ea1a17555ecfbaa6 -10691,0x072d88b9525803056fc3a461b310ec1947eccdaa -10711,0x09448876068907827ec15f49a8f1a58c70b04d45 -10706,0x14a49ac7bb98b1564e95901fe5857b991c8ddad2 -10701,0x1d02a8ae79e60f50b43d7a6d594251a2dc5a20ad -10708,0x2950b8792beec6b0d5fc11725b2e1664eea60b0d -10692,0x4f1437a43500b7863c614528e6a15b220904010b -10715,0x5aed117fc68208b1b98a49d235922084217ba0db -10700,0x5bde4e21bf5487becbe98881e977fdc220010b4a -10713,0x6a78bff76813c5833c80c55e1d97d24a56eb4127 -10696,0x6ca558bd3eab53da1b25ab97916dd14bf6cfee4e -10698,0x73814dfbe55528e9e91a3962e1414cda9b604b25 -10712,0x7ad867c08b668af40e4ea30c18158dcb8a2baa4d -10704,0x8c89e0347be4e511de93e16d3cb086ff7eab2949 -10714,0x95f73fc89f62b42410fc4a4a60edefc51de0b7b0 -10703,0x9f6cf3c50e031607bf4531a02f6987a64eeb6175 -10694,0xa5e6a847f79ba19aaf41b8e1b2e6c4741234c6b7 -10695,0xa60b1b4b4518b2eb719742e66d64821a5bd07e18 -10693,0xb23d99339265d776f582e40c850f43105020a12c -10705,0xb445a73edf9bcf0f8d927a8375d79d15716857af -10699,0xc36475bcf230c80f08c02565d5a3d9376ceed323 -10697,0xdba1eb2c3abc62dfce544cfb17fcc4661b794778 -10710,0xeeeadc382b44e610c8e3b9d76a3269b4f5f94f42 -10709,0xf5e49b0a960459799f1e9b3f313dfa81d2ce553c -10772,0x41df4284a34e752386d2067cb086c1591461d1c0 -10810,0x167829543510c13c792bd8f6a414f74d51e3a6d4 -10809,0x2a017f2fb369f4ca061b8d8a922bb05100e8f8c3 -10807,0x35ba8c41ceeea24f7c826015844f2b58af3058a6 -10812,0x3c0fd0b42aa46f03f6cf5e305ed9b2ce402847ef -10815,0x40d577c05311c8cd4b335afb4a133ed8328c7ba6 -10804,0x41c84fe367bb1b517fea383e66c4bf304f30eb47 -10803,0x5411894842e610c4d0f6ed4c232da689400f94a1 -10816,0x6d55aa15e84bd5847b13bab3245c06b05917d3da -10814,0x6ee80279c057f17a3c7d5a9aa3e52ac304861e71 -10806,0x86690b9dbb979850ae4622347af81232baa3c967 -10805,0x9310f89a8d910d33c2a4d9a73a441be3705a63f2 -10811,0xae8999da4d81cb81b42288b12176fe20d7ead578 -10813,0xb2ce2d1d4cb5c3b3d3adcad757faf4222ac542f5 -10808,0xbdd1d443118554feb151406622a3b586992b49d3 -11339,0x00b91a8c620a9aebe2ea8aa99de1ec34997e81f6 -11359,0x014b152fcacf6069803f67d9379a693a219869f6 -11189,0x01a2dd38f688bc11f09d1cd6ab3318a292a9ef9f -11166,0x01ba05560c5bc1ae3d90987950aa54a122435166 -11036,0x01dd9c3054303c26c576cb27bf75e1b16c505f3a -11293,0x01e4d996240f677a057b19db300060bd20a8f7a9 -11380,0x01eafb9d744a652e71f554cd8946bfbcd38f5b96 -11367,0x02d21061ae1df3b4ec8ec246e790418259cf1443 -11081,0x02e3efed80972ea6b4c53c742e10488d1efc0fe2 -11254,0x0358aa3dc3d7101a601d66974e8b84d449c1c4bd -11175,0x0371ad7dd404d3ae05cd10d237a03ffef9ceafd8 -11323,0x03726d5537e29061a93e2ed537f8f53afd37523c -11180,0x03e8686efe195f036845498aeb2125c626ad949b -11415,0x048d6eb8a0fd322a1b0519fe772ae534f773c478 -11116,0x049769cf5803c57ab1da4d192dee34baaf4668c9 -11043,0x04b8a4a5cfaba7e213d77e496d4ee39f47f4b289 -11271,0x054a3584601b973f472d98828fae682623f5b4a0 -11255,0x055730b5f83f9a16ea9fdc3ac97c9cfbe5cdb071 -11278,0x06016071419f005ced89bba0ed18ce9e45dfe0da -11322,0x0609b39b3efc80ec350e8787defabc357fad0bdc -11075,0x06739e7b7e7f4e950925f153365a73836b29867b -11126,0x071a922d81d604617ad5276479146bf9d7105efc -11226,0x072658a465292629bcae578b2a3d89acc91e6215 -11294,0x0766aed42e9b48aa8f3e6bcae925c6cf82b517ef -11141,0x0903c7cd8bf1e6f46d23a476202bf1400c5c271f -11246,0x0976ed800c997b965d48e47bee3f8aa10b5e0b60 -11431,0x099913e22a0db0e3e6d8a67506e46dc168fa0174 -11070,0x0a2e33f6bc06bc833175f6d329d30bc18f79d4d7 -11258,0x0bfbd3718ed63d6f37b0f9e0ceab66e92b1ff4ed -11401,0x0c13a59a95da229562ef8fa47c9b117b2f3c51bb -11088,0x0c57117111a703f6cc8d7f6c69d3f244e8478860 -11301,0x0d109f5618e8e85091b1eb57b192ba68782a028c -11333,0x0df543a62d8cc750433567011e9a374b9063ea4b -11119,0x0fa0427cf07cd59f50d93d32a84a3ebf4be60d6f -11111,0x1047afc683abc40314b29dfd686bb178ebeb4f44 -11243,0x10eac0a3579a482b1b4fb9eedd16a7e0a19dcb07 -11241,0x111a9b77f95b1e024df162b42dec0a2b1c51a00e -11410,0x1147d12648af6b60c831aa8564ad5bbab17749a9 -11346,0x119ffdf012c4aab8a419b4beebd50f24666a3a84 -11058,0x12d15e0b690c7e198b318b3bf7c00526e38c9676 -11060,0x132f3f42a55f037680f557a1441c4e8e42a41a41 -11035,0x1337709aab907f678dd22cf0b74f786f8052dc4e -11264,0x139c6a31c852d11b22470c7ee3f6f0bea04411a8 -11129,0x14ba0a21e4917748e7159efcd7c739039542e568 -11092,0x15fbf2f74faa5400c1ccb9cfb0ac8294be3a0272 -11176,0x16d082a1b028914b8c7301549f7fe4398111db8a -11066,0x17d099fc623bd06cfe4861d874704af184773c75 -11416,0x17df10e908e3847038a3392c85d5376b91248079 -11347,0x1820f9d0ba5cc038b9983660885ed09e59231ad6 -11090,0x1849754be236a199d5d81f077bf4c3f8e13ed16b -11147,0x1891a76d191d5a24bcd06dea4acadf4b8ae4b583 -11082,0x18fa37b837ecf3949234c4aabeee5695163ee3bf -11150,0x19b9bf8f80267dfcbd16f404ecfef659231db140 -11105,0x19ca00d242e96a30a1cad12f08c375caa989628f -11276,0x1a3b57e95ecec8ba072cf4fa4c50479dd4d7c36b -11396,0x1a3c1bb1b18d33a06c3457beca828e59904d5aad -11065,0x1b4fd39128b9cadfdfe62fb8c519061d5227d4b9 -11104,0x1b68fb87bd4af2b7fc022ba3a2a3dc82ed025c50 -11313,0x1bad45e92dce078cf68c2141cd34f54a02c92806 -11400,0x1c43fdcb1437b1210cb18c3c1025705dfd3320bd -11206,0x1c84c7e891fed487e51b1d761e35a095c199ca02 -11110,0x1cf49e880fc64c0b98bee0ecac89aa79d29335ea -11417,0x1e19f9b2f87ff30ffb061d07c77a57d59d267f97 -11233,0x1e1bf73db9b278a95c9fe9205759956edea8b6ae -11216,0x1e877ce35640225a95ef327f85b502d204605894 -11015,0x1eddac6dfe661f09dc9f3256f9cb9ea39f2bc902 -11097,0x1f45afddb111e8b118464051ac555cd108353c87 -11196,0x1ffa0af1fa5bdfca491a21bd4eab55304c623ab8 -11220,0x20b2bcf35d6a42da6991883b78f01d6eb2c4a476 -11297,0x21f95cff4aa2e1dd8f43c6b581f246e5aa67fc9c -11224,0x229ecbb1d76463e761535dd0e591c34317396131 -11267,0x22d3316c21c512f9b4f22b9fa09dac0ad39c0314 -11158,0x23394cc9847e0ee02f23d9802524b793c48d4667 -11350,0x234210b2d3a734df46958f5156c6054d57cfd414 -11361,0x23552129c697877301ecbcf91d652be900dc9c61 -11010,0x2356e1565e39e6b688e1aa2f367b3c152d8a7ec9 -11390,0x235de7506f65b4668f20f4ea7a952f6ae6330885 -11079,0x259e6198dd81293928df0d63ee81cf511f9595cd -11077,0x273dc91515ac5da9d9eaa7cff645d573770837ca -11312,0x279868346825022a110918fbe03bdec8bbba6508 -11225,0x27b1f546426f95ff7305c9ad5316642bf5b039ee -11253,0x27d2741ecaab604d5aa2c903935faca82df0c161 -11338,0x283d88d97ba922c8f8717dc97eed6c3976b7bc7c -11096,0x29060f43b53865e6eb1279573c99f9da22bb2f97 -11421,0x2a607c05b91eab1bc861762a13a602a74bb7e0f0 -11331,0x2b2ce9ea2a8428ce4c4dcd0c19a931968d2f1e7b -11173,0x2b33fac8c11619eb15bbe193ec2675e505e2829e -11240,0x2b394b228908fb7dacaff5f340f1b442a39b056c -11215,0x2b4919a0028250734595afbeafd808667742c9ce -11368,0x2bcd57c497d5a6dbffd98f0fad67cd9440ae0620 -11209,0x2c761ed6886867aa3c4e258253982987326cb929 -11409,0x2ce34d0e81a5a7c7306d7259bbe1e29fcf62594b -11140,0x2dd8bfaffb58dfe9d0d5d8948af0c2ee8054e495 -11282,0x2eb422e4503eae23b3aafd16ed6796a54dd39b66 -11408,0x2f8a04fe85f9a479ba8911d6ec59597b87606e7d -11248,0x2fb997a312df6edc99268858f66a3bef226012b8 -11185,0x323c6f5eb0bca20e197425e266311bf9f3ff46f8 -11199,0x32b4c887a4025f565891667f085662f67dcd944e -11153,0x330a132dc55d77bc349520697e5ecf892d8ec63a -11039,0x333ff948b0b2d2c26a2ff779915abf1dda0a7cdf -11296,0x34020412914efe1ca69a363e6298f06545c263bb -11207,0x343d5191a9f2610c0f16a00f13af91677317aa16 -11441,0x344fdc08f133d854a22f04393f390536139eb61a -11008,0x3573de618ae4a740fb24215d93f4483436fbb2b6 -11134,0x35bf7a0886b0cdd625a3418c6b35e85cacafff35 -11260,0x36395bf7d30de794bc94f9e581cefc2d0257136c -11198,0x37f35dfd4aa433e34b76d7fd34cf4750249a9207 -11422,0x38f06a17cdc51ab8a15be3eaa35bf53509648815 -11329,0x3929a552b469c59a563f65aa5a78979f3dd94abf -11137,0x396be32f26c98dd6c6cc813eaaf59c6c3e1101d5 -11171,0x39ce849440f77947aa4f29058977238d3d09d95e -11337,0x3aa179c2f70d1d022afbc1f779177b8739cc45d1 -11145,0x3aa7e08e3e0cb20a9afe03c04f65b55113d98cce -11107,0x3b54188c35074610c3cea64577b2cb88bf77dbe0 -11197,0x3b9058d450f214796b581a7a011bf5db7ea31724 -11120,0x3be8564de160875453c53f6407b2e822da9a64c3 -11427,0x3cade4676898109cc09434c51f9d5ca21bb32a15 -11363,0x3ce5a4cd0a9fb49686b26237cd2be2b2fa8dc04f -11394,0x3ee6107d9c93955acbb3f39871d32b02f82b78ab -11319,0x4033a2736bc9e0aac3ee6b16febcd6af326d46f0 -11050,0x415648279259927553a531790e1fe913a20eb5bc -11328,0x41d4af9f8faf16b6d183b4bdb993b37456ea18e6 -11393,0x424532a7216cf1bf35faf2ab32eede07c3b77598 -11335,0x42ab3ef2e6f8d625f4baf7724f35946c49a06b4f -11419,0x42cbc398f63855f89afe225f5f90151b00d6c73c -11112,0x43c36ff1955140674d3cb7222655ab2dc4422e5f -11275,0x43cb769d5647cc56f5c1e8df72ab9097dab59cce -11311,0x444eb0bcb6ee013860e7ca5a13bf67d46a992414 -11303,0x458ef40fd32298a32d65f13a361e599715c06542 -11013,0x462062fdeed276b803b7c348cc70501a8f9c3c8b -11236,0x4854c82b12a389051a4bb55ab24b1da3ce8a2b5b -11108,0x4926b7eb0ea979f944b5957d6a01e88da0fbc807 -11315,0x4a6cdeccc4eb5c384a4f935a77a7578969ad5b31 -11155,0x4b15602550a65869ce9a771a43230215a8cd3d1c -11053,0x4b93a031e44ad6aeee4b5308e3e85b7cf180dd45 -11332,0x4bb48255c72bebb45351f042a38430554f486ade -11360,0x4d3356c65e20a2b9313d0b49bffd2af5bbc96ec7 -11351,0x4d3b0b33a287361b1ad9df9dc54c3b3e0523e2cb -11021,0x4dd7b43d1b9920d78e8016a74dadca8f472573ad -11291,0x4f086a048c33f3bf9011dd2265861ce812624f2c -11095,0x4fb4437df127c75790afdfbd3f0b7b67fd5c65ec -11295,0x5048a2001c9d4e21530a57320b826ef2d1daf065 -11012,0x508734b52ba7e04ba068a2d4f67720ac1f63df47 -11326,0x50d1666f8048f88bae6b23cc0d09fcc259065441 -11086,0x51868bb8b71fb423b87129908fa039b880c8612d -11428,0x521a34639d8a6c23e257fcbbb3a748c1d353dfda -11160,0x52c223e9efb7a305ecf9b5cc9f8752e81451a1a4 -11348,0x52e0163ca9abeea75e2437f2c9095f3fd807f5c4 -11068,0x53b788691d66ee50c8f36d153921b37f85432fac -11381,0x53d718444da5a4bfad5074a061a12bf39c64b426 -11238,0x5534a31217b883e849b6f96b0ae21ad6b71c0112 -11033,0x553f3e880a5ac95968b501fadbea20d4a8778650 -11011,0x557b10781dfae44ad008ea1c7a281c230f4e4c1d -11266,0x55b6345219ce250ba7b582ff41ace886f4d596a9 -11284,0x566c68cd2f1e8b6d780c342b207b60c9c4f32767 -11109,0x56756c847b027a27703aad58c732c041f4e5f033 -11139,0x56fd6d268334741c7d56464dd5b01f6132972818 -11433,0x5979ea105222faf98364366c7e8304166cedcc31 -11202,0x5a06cab77beb2e99247cd23504363f1bfb7c1bdf -11434,0x5a8d1919647c4de929664bcb442afbf94279b913 -11080,0x5ab3d7a18a210ac599817db2ff7ab3ee786f3a00 -11375,0x5b4fd5e0a742e2f5d9ec2bceacc3d4fb8466155b -11023,0x5c8765f08aec9f117b58b83834ca45c948a59ab1 -11018,0x5d89a4c927d509ef4a636488ee291d396e406d1f -11061,0x5e7e72bd6b06816fafc3939e04edab6e4d0cf6cf -11366,0x5ebc6063f478b16d514f43e53d2ad576993240f8 -11234,0x5ef8c33953de22871f413fe470650fcd17d79362 -11364,0x5faaa355a1d1c25ccd16333e14c11dc78c573f24 -11316,0x6045e787688c7550bcc3dec551c54c57f13e6204 -11219,0x60ec371c264df9d091c2784953c16f2e23c952f3 -11114,0x61b9353e2ae30e327f360d18b65972e3ab8198ed -11229,0x61cbcb4278d737471ee54dc689de50e4455978d8 -11369,0x6297941f7ca4610e736edde16fb0a28db5e95275 -11252,0x63c900b6e9cceedaa37ff6af892e6ac54db3008f -11398,0x63df09710a65e46288747f611d56cc0116a3cf93 -11320,0x64da5e89b347ad033615afcc5a585db3f38a1ae2 -11014,0x6536dffd07c1cd3773f896f0e46962df7c14a833 -11354,0x65635977e77c3da6028300d87406836018203c6d -11405,0x65e45d2f3f43b613416614c73f18fdd3aa2b8391 -11124,0x67076d9fb5193d7810d713b129b7b948cb1cae2f -11218,0x670aed4270ff1c284535e59c69340fa0be7425e6 -11379,0x67d53210dc19c76b2f64b190260d3d9cc8ba686d -11302,0x68e53d3e39c7ee5795404c70e51b991ac121c30e -11144,0x69685c20db3cf157da4e87ea25c16d76d041a5c9 -11269,0x6ad51a172ec3b172e44d1d79e56225b323f76ccb -11214,0x6bef9aca384837784b7c5422a6deffd6da22ebd4 -11163,0x6ca9ea8fb6f48c496db2a5dcb7ab4f0ad6d57451 -11372,0x6cb0cf0518bc8f87b751f178ef264b248d1a2128 -11064,0x6cd2852371fb10bb606c1c65930926c47a62f8cd -11336,0x6f5c7be5ea924a1579e85e6bc74ac0504183e0da -11342,0x6f8da831931721b53412a5303ed563663561a38d -11397,0x6fed42d8bf5010e5710927fe0de15f91f916204d -11054,0x711c071f1a0c0cc85cafd4120daa7b252533b57e -11169,0x72d0f2d6fca0bc2f099f377613b1e815b22744ec -11135,0x72f920e27788b7b91654f8aa39a839796864934d -11052,0x738380d1cf77d87b17b3da26ea2c9df8f0d3a948 -11221,0x73e51b0368ef8bd0070b12dd992c54aa53bcb5f4 -11208,0x73ed7b73b4afced89bb7d490cc9956d27347747e -11412,0x75441c125890612f95b5fbf3f73db0c25f5573cd -11045,0x75c082e8188e55fff0f110edf3d877d655bb78b2 -11138,0x75f29a89107ff590f3b65759e8e6f9943149c27a -11101,0x766da60cc688e45b5948f05cb947d3b8df7274f5 -11425,0x782e6dd4fe454aa3534fe92050014cc6eab7bd20 -11009,0x78649ecde1dd50bbb80455a0c7492efb1aecb843 -11100,0x790b5b11cbea99e2a6dc259263960b383235784d -11345,0x79dee4f059e9e1c3fe2b7d26ab0b9454871b64c5 -11307,0x7abef6a3e7b4d5e7e33584bbb7cb60ecbe0f0581 -11085,0x7b3a5df48819e3160b3227c506354da076dff666 -11406,0x7d3063f7693d8de76e4ed0b615eb3a36ca1a6c25 -11025,0x7e22507141e92dbe0db8e865888ee35acdb6c5fe -11268,0x7e66050192e5d74f311def92471394a2232a90f9 -11113,0x7ecc9d0ee071c7b86d0ae2101231a3615564009e -11250,0x7f6f82f8ad3b393a815a642f99909817db649e61 -11133,0x7f905c459772a805116fec52530ccf1665cddc36 -11162,0x80a79d0fe8c658bc2ae7bd37fc48a74d59803a5f -11122,0x8147cf285996d2887c87d1671b8a11bffd05144b -11365,0x8177dd406e12f70feccf71753a4bcf7589965486 -11184,0x8180e67bb7809378196e67ba67af667e6534c999 -11270,0x81974842d074554539cb685474b3e6437f5cebcc -11094,0x829292879a993529549422bf2d292d46f7fb01b0 -11235,0x83cf5e8b98ff3bbf2237fe7411e6b03c57c7a0ec -11152,0x850ab9bcb1a38808526f682466683947000bfde3 -11321,0x859cee22130d4c974c12c6774d323105348edcbd -11228,0x85b355bca05cf4f833a0c0cfd0e57ace32b3a6b4 -11357,0x85c99235387c6168235db821470fe60f821a3212 -11237,0x8702c9a26d400ae1f996569fa508063fb26db0c0 -11257,0x874933d799c7c2d5a156d634d286cd01f9523caa -11187,0x875456b73cbc58aa1be98dfe3b0459e0c0bf7b0e -11247,0x88dbe71408dc20c82ce44d919d7f521717352636 -11193,0x89ac9cefc4e69b484fb46602964b38380fd19fb5 -11178,0x89e8e196ea5c6d325164a40b4b690e141ea9641e -11385,0x8a8d0053392678b17b3a7d7749f5d26a5c843905 -11423,0x8ad2f87edd0260d68036634da697fb248fbd0818 -11300,0x8b6e31f3286b4d0b611ff326e9682651a893ad70 -11414,0x8b80a8701a339620d45e47762b1568b9c3ac3a17 -11149,0x8bb2945cbdd5ec18d50a34c761e1d225f800b624 -11084,0x8bbb9dd9ae557f20802685659a87a15d3cd0c266 -11362,0x8d979cdf20286714026f74237421ada942de4606 -11191,0x8e061886dc6859f7bf4e73ae122273e08eec0e32 -11078,0x8e36d3d9a5bfabd127fee7014a3aa843a10111f8 -11273,0x8f567291d6c6f2129d4ce283a7a5cbfca0909333 -11019,0x8f813bc36261669e8f75e1ebc341b6845b0d7824 -11230,0x8fa82f61153e7746112cb4e9de1033504c3294ee -11318,0x904cd8dd61482853d22e0a3cabb94fe889ee8380 -11131,0x906ee3cc53f2a59c0e338e8357c4490b1534896d -11055,0x90d87ee39e27ac5130165c4afd113ef1caf7890d -11030,0x91155c72ea13bcbf6066dd161beced3eb7c35e35 -11435,0x93098ec06593c5ca17fa7e843609b8bbd37ec997 -11432,0x932b30b2bc3f00b77affce8d0ff70b536f658462 -11309,0x94204607208f763b8dcc22232f9fd2a51f8fc14b -11142,0x9576d00fc572aef537f30d3daa40f1ab309fc55c -11154,0x963b4cf30583d39b08125335c9b5d671eef7c60e -11443,0x9676635530c052a81e87e032037d6a3a62bc37a1 -11310,0x9b27db3cc52da7f6ca16740a977a349aa09547ef -11174,0x9b280e4623ce52781f0200a3a17f0760b844f751 -11164,0x9b7bd49e37195ea029bddcbef14e4eb2349dde0e -11200,0x9c954f8104b39826f7210d66e50303fec89b8853 -11232,0x9cf36ffc181fc70882ec8c05ebfeb4bd45fb4b67 -11057,0x9de1d18e23f052685ce6d2b17806753a49dcf15a -11231,0x9df45d2beaf86a35781bf2b918999e7fc6dccd06 -11251,0x9e59cf395d28f4efee6e7a6a504efb0c908ca136 -11292,0x9e7176b445956559e228c82abf03687a017651e5 -11378,0x9faf71d8d68b1b5e2d376d65441add8765ac277f -11395,0x9fb1db0252d9153f426fc585135b7696f8a37d96 -11117,0x9fc9443af565e1d2d23871b608ef31ebcd32b700 -11192,0xa0afda43fad7dc1a31f294da33bc23b1c0ef0c4f -11179,0xa15e86a4b596978410163c5a3f26d132bf03e333 -11262,0xa1b1d50b7074b8767011bca918cd266850645511 -11194,0xa1be037c26e08d0d1763263a4ba0b2a8d7a80472 -11186,0xa26fff7821fed25eb37af785c04c743649ce6edb -11407,0xa4226e6833e5c6f83628c25922a383495c3d2259 -11042,0xa524e0525f7779493756c13163956b2d9e6e554c -11034,0xa6d727caf1f99943331a58e471a88156db90577f -11436,0xa726bf227be6c3a6945218fff4b218ba0d0ae5b9 -11059,0xa75a9af9626bb6eb2684fc5b5a2348cebb89a1da -11420,0xa7a0fac77c7c711b2ae00f0f483321bf022d4d24 -11130,0xa7ca3ff9213e489e35a276d6fe7dd0c2dd54f8b0 -11083,0xa8c41ab18133e566b8cd0349eca19e3707aaad01 -11387,0xa927aeb88f739e522f3fdd573fbfd5f4fc7c527f -11373,0xa9baa5df418e8273d5a61bd0f6b6cd24ff85f1a0 -11136,0xa9f38af839fa0627254cf59e5e7f16dcadd8f06b -11290,0xaa27053c78cefe2bb96e7aca312062e67650ffe5 -11204,0xaa3b2f7c6ffad072ab65d144b349ed44558f1d80 -11106,0xaa8e22ecc2c0534ad49af3fbe6145fd5e4ec9e28 -11399,0xab3009aa9aa054325ef95bbdb250582aff2e33c8 -11265,0xabc61f92c372fc440d6b39513afde6f081749f6d -11340,0xac557425fbd38361196ee5b5641d551e07bd88dc -11212,0xac642240e8b5c21bec36e62960c8ac5df1b3deb4 -11355,0xaebc1a45f35fdc1066165256ae64d793ff21bffd -11223,0xafb98c29ca654eac2c327b3de227b30172f67857 -11103,0xb100682b0ae5d2632ce9ac4966e005a61a85a6a3 -11374,0xb10e50ea8993107e8861fdb79764f5643fbd1e1e -11274,0xb116706aaf354f793374d1525d4b23b4beaab9d4 -11404,0xb19f4d65882f6c103c332f0bc012354548e9ce0e -11123,0xb1ab0187bad475fa193961b759fd24b7800435c6 -11190,0xb1fae939ea3a0da8acf35c8c0ba48059430f4e57 -11146,0xb25429714c0cad172cb16a85a231c3e51713aa1e -11041,0xb344dbe14698bc415fef5511cd861080845b5767 -11308,0xb54a8709a96d045454e735328437cc0b0a884077 -11102,0xb62100d94436f53a87516f1aa3cf42f8a96ae049 -11128,0xb76086387b8d710fb5a9179ec474b3c6e39ad7c6 -11327,0xb7641c958f9b939c5152e15f1fa4508f1a240f96 -11288,0xb7cec81d391e745d7c0679be95f862a29b3d59db -11132,0xb7e3ac46877c58e94380cb11f71867cb3e54df56 -11217,0xb99e8cf514a07b60c1fcab8f1ecba5894f083fc7 -11299,0xbaaa327bafd7e2a2dfafcb09fd335246f3c6fb0b -11287,0xbbaefef873c512f55f9c4df00d6850cb5b808a76 -11283,0xbc0e513def7604463456106ed9c6dc66808aa643 -11115,0xbc29eb55b452bd28276a2119cb2465ce5996b2f8 -11358,0xbce1a9c503d2167ca387ad49c4aae0e30f8cc79f -11098,0xbdc014040b64df06439d5ebccbb139a6bf12032a -11213,0xbdf94b9d813ae4b54d9b221c6fd003af2e1b8432 -11069,0xbe40005be569d1f9d836a4b95e7a828b0f1b80c8 -11343,0xbe6fbe1a434684fee00616e04998ca42734da6ce -11007,0xbebc1bd86268cebd4f4e0c6f0066dea7262f876b -11286,0xbf12fd5e22fcdb0ffb5c62005fec4f9b90339580 -11371,0xbfac229d495c057c9f5ee0f0fd294319fc2223e5 -11211,0xbfe5f4fa601efd3074e2f92475ee5278a6293f07 -11038,0xc073f6b833e71d03e20a80a1c0e6b8566233da1a -11376,0xc0f4441a1b059ec16a290bba0158389ccba07ebe -11157,0xc0f5da4fb484ce6d8a6832819299f7cd0d15726e -11182,0xc1ca7ca02ea9e730b62464ae503930781e1c2300 -11256,0xc2bc6edaa1d002abc74c6197b3e0f5b850269dbf -11298,0xc2f81876ed0075a6ebd99e0facecf5e90c210a0f -11037,0xc3908989814ec7e1e0cbc261bb613b864f5db6e3 -11334,0xc450f8f521d2d7e94e5f71c54ef7d72dc19f5824 -11020,0xc5666f7c50dbb9bfafde29d5ed190a31ffca8370 -11289,0xc66b447be01ae5feadbd6dc76d228c5143af9a3c -11272,0xc6ed1d3e02d78377e74e98a97b4433ca54bb7f20 -11049,0xc72c4437824866ef48a0e8455831c21022a12592 -11031,0xc7670686529791d9c62eaa4d3b4745bb84a3a1ce -11227,0xc77a6814e1ee8d5d8ce925cfb78425f565fb45b1 -11203,0xc7c84d12e350cc9cd81eab405aae2d600308c711 -11181,0xc83c23dba4a008d7df46cd50ad144ae856b6b1be -11222,0xc8696440176f3adcff3ee3d107b737e00a938140 -11071,0xc99c96e761afeb6454f3bf3163668d599110305a -11317,0xca55757854222d8232a19ec8aae336594ee3b5e5 -11429,0xca77bd3147b68e869d9bdc05b7fde789be905afc -11188,0xcad929915d9b8d7655c17e94d0c5cdf2793e7eaa -11172,0xcb5f95e3860d8edf1bbaf942fe782cee0d710708 -11087,0xcc60867f8d448b35ab1f5f6058dbdc06377a7f9a -11183,0xccfd57aa70610e66d8ffc09ee1489d473f7f8192 -11249,0xcdb61c99e46b6111f9e8493f09fe548e6bcf85b1 -11377,0xce1a9cf9266b03404c43c8b0da1ae833a6f922c7 -11242,0xce1cf9dbde650bc0dbcee83e5783f9845048a605 -11063,0xcecd29559a84e4d4f6467b36bbd4b9c3e6b89771 -11170,0xcf14ef7c69166847c71913dc449c3958f55998d7 -11099,0xcf32c043aa2441e8948be14eec49e9f280cb5781 -11074,0xd14e734fb1c5d60ec1f775c3e0dee536174d46d6 -11388,0xd22ada46b2fa2bf253141218f6965be2bf22d27a -11392,0xd24687aee61bc25f4ea71c0979e97dc3c2ea9c16 -11210,0xd268887b2171c4b7595deebd0cb589c560682629 -11389,0xd2b66d92459d24cac2cba9687564a713ec978912 -11118,0xd2ba06b50aeed06be5bcd85cc8718213f8902f3e -11281,0xd30738e3b7ff47d619b8364e6029c11de175cc08 -11426,0xd3455c05cb1c0f15596dce5b43680d4908f64a9c -11386,0xd3467a3e160029295b78dc5007924f4f561d79a5 -11205,0xd3b8519e16cfad624253bf5ff9c658a3502cfa75 -11159,0xd4544a58bdb7a71a295402b3cbb13d3704083d4d -11062,0xd4f64a36d0e9f00e499c35a5f8b90183d8ab3305 -11370,0xd50e831c62e91cafde34666598a99e4f79683706 -11352,0xd63bd47fba5416b32e649f6cf3066de82b6bcce3 -11022,0xd7a5e904837d1f1a17c5dbc1575f99677dec6570 -11073,0xd83944a44c4cf0bf36b405cb2b189c03b3288c55 -11040,0xd84d315f22565399abfcb2b9c836955401c01a47 -11245,0xda713512042bbbc599c438fe41c0c14b2e1dea3e -11261,0xdaf8a28890fc0cbe3952aeaa353af868e9a9f33c -11353,0xdb04b0b66e9cf3722d1c337c43452a311ede88a2 -11279,0xdc015935dd936450b9d116c3fa66ca3ad3afc109 -11382,0xdc233910a2f71d2734a8cad1ca2d936df805bb62 -11167,0xdc7c36282eb0f42dd739968f3e3d5bf075bda6cc -11016,0xdd86b3f0b61977993279a2490be0283672a7292e -11067,0xdd957fbbdb549b957a1db92b88bba5297d0bbe99 -11263,0xde261a231af98b9a9fd01462fe84eb53c11aebbd -11325,0xde3ffa5595226c6587c9b5ec7aa33216c064f96c -11384,0xde4182059a022c8af088acce0a2803294d40c758 -11125,0xdedeb5254af8d99bb07216c35d4bd1673706af9d -11028,0xdf2d2c477078d2cd563648abbb913da3db247c00 -11024,0xdf552ea8b8110ceeee65a4166f03575771fb2370 -11072,0xdf6fb75b468860aa3343d69b19a2dc004a416dec -11047,0xdfad8b746671480f72bb47e2a724a113fe01f096 -11056,0xe095a5b61b6cc27ed755dae84884914133c7cb8b -11306,0xe0d89392ecb1e26e48e9d91738bf0bf31192b5f1 -11165,0xe1139e5d6db7e99de116603258e62153d883b901 -11438,0xe1ac0249d7d88a2e19a556d78185ea39ee53219f -11244,0xe1de6b2bd69131b31ac4afbfe917a5e376e60afe -11051,0xe223b22b8de0dfae31bc5be5f9ca4d3149cdd401 -11437,0xe31eb1d710dee27264d0088bc877c6f808bc6faf -11089,0xe3d115671cd4100dbd018aee78c19cfa18055476 -11076,0xe4317f279d28648d1a4023dac3d9fe3017116d09 -11151,0xe5bd15ef9c2f232d342aa9b422e67a96e0601002 -11413,0xe633e60a301428f202d999fc9a91f9229085cb23 -11344,0xe726586f11bfb7856d4c52c77cdc5ff333953e15 -11177,0xe73b9c66487f76e4fb28d4029786575424673dc5 -11195,0xe7632aaf1096c694f0e6b7e118d0532d12f89ca2 -11349,0xe8cacef21859a94aa410c8dc0dda9538985a826a -11201,0xe93e70df9ff34b6175720252c9c6eb2d68694fc3 -11161,0xe9a1283ff0465bdc15076d8c07dba1cd06699d76 -11156,0xea28051eef9ba3a23f5f5cc2708481a392d7c0c4 -11239,0xec7c00ce4d63f06d4c2bb7d63e032911996e70ef -11026,0xed1dee9e641b5b3cd6691ab337ac1aaa5515a07f -11277,0xed2000b7ffd60a18c7014a4860213093992f1353 -11305,0xed6e02fba69ce941dc51c19458cde643e3542cf1 -11168,0xed9416054a155a92099f35e8ef23f9740bb98974 -11285,0xef9dc7ddadcd84bb97b9ceefef3338ed72a0170f -11391,0xefcbf2bd622ce716d3344c09e77e7a74071e6ce2 -11091,0xefd98ec23b80f2e85aa3107f439946e336ec9034 -11330,0xf0f2fc60d2dc9fff81568c7bf6724cb7f2f2271d -11418,0xf11a79b1df84f36f9fa88c535dc4a3e7a58d03c2 -11027,0xf37cded599f52f4f3ff2e86444cc8ae5a9e8d46a -11046,0xf38cf4c1644098d819df230b7cc7b9fb13b4f323 -11143,0xf3e54c02d2279418cd36addfc935b4d8b032fdf7 -11044,0xf47543ee13afb212a90fd206006f2b6a406fb91a -11032,0xf5312b8a4d66160f9365d3c4a4adff18b9617a30 -11093,0xf56d65a3f92a0d48e2f75f827e892e6b5e3bd1f6 -11280,0xf58e3844fff84300b118b735043ace1aa35862ba -11259,0xf645557308c489aaaf0595b3270c2dde29f317e4 -11430,0xf6533b6fcb3f42d2fc91da7c379858ae6ebc7448 -11314,0xf7fac3af7bdbf717a6dbf2ad9477c1f1c942d70a -11424,0xf81024184da7fcbb1394248bf508c41e9866e5cd -11121,0xf84387c783b0763890a9235d0124ce79005b42cf -11383,0xf917d20bf8ada622bc36f027544a6600826de5a9 -11048,0xf9d0771d83856be7ab6209d28055f76c5df16bd7 -11439,0xfa097bd1e57d720c2f884c3dff0b5fce23a2b09e -11402,0xfa43948c857a201386a99cbc07a099c56fe04580 -11017,0xfa48340039037f6a6b099efde699a6eab3802a03 -11341,0xfa72b38f6c6e6661db9c7b3585b4c9b823fb979c -11356,0xfb05bffb5714210f070c9678f9f460b4d4265957 -11029,0xfb0f98739c8437d38f7c1926787707766257e390 -11304,0xfb2bc53e7fb981f44f7f9c542681a095b20da463 -11148,0xfb86e03bf6f73dab782b36b62ee3ef235b2be4d0 -11440,0xfbd08a6869d3e4ec8a21895c1e269f4b980813f0 -11324,0xfc550bad3c14160cba7bc05ee263b3f060149aff -11442,0xfcdf934e768596b3050a41915dc618efa21106a3 -11403,0xfe0b8ce2fbe738756768e72ccb42a371c01593a9 -11411,0xfe24e5c6bd0721b5b69e10da687796ba63f3bf81 -11127,0xff4eef59a89e953926ce5a61c6f68c504d47c7b6 -11555,0x0e3eb2eab0e524b69c79e24910f4318db46baa9c -11550,0x17c33cd997d8a3eb3f7945486b47c456b1f02d87 -11556,0x49ca35468148a3ca4737c397d918834b790c1aef -11549,0x688a62d79aab9628783383b9fabb46c2dc6c2faf -11557,0x6e47adf8a0bb4e3f4252f87beefc373752869b17 -11552,0x72aa25d1c70ab1423a6d65bf2cc6e4270a6e0224 -11544,0x75c9d9a49c62a31e307794d5b9959daac22aab0b -11551,0x97163345c51db2f3508cafb7483cb639706e15db -11546,0xa4bb709b56901e8e1d7c51605a46f75983fb39e1 -11554,0xa8abc049b7e0ee79014e12111ecb28727fcddd2d -11543,0xb22841ef2ec2c820c0d0fc5370f765791668f50c -11553,0xc4300be7878f42b39bdfb6a57d0f88eb87b842c3 -11545,0xddefaf643fe8b3b1ed7dee0f751408cea5d5bdac -11547,0xe48f23956bd88f68399a72f996364d9b50bbdd18 -11548,0xe569d45803c76a6651f466f6b16da4c8705d4257 -11620,0x1c8cfde3ba6efc4ff8dd5c93044b9a690b6cff36 -11619,0x400a9f1bb1db80643c33710c2232a0d74ef5cff1 -11662,0x038ee16bc19e362cc5ba1915f32a3a6717047287 -11675,0x0404084e0e63eb98766a4540a0ac6b0fbf17d155 -11637,0x05d4d700e04dccb0bef8b8fcb54a6cdd5c9518bc -11663,0x0a8ab428fd0a5f3dd849f9e9ef38c2b2894cea10 -11647,0x0cb48f50e2edcd4023a1bd015326c4dfa1de509c -11653,0x13111a7150614af6809c2b42849701d9b84bdb31 -11669,0x153f0e24bc761f456cc121d443f2af761ad9d2dd -11645,0x1556e2d8ff76dfbc5946ae18260e78a6ed745fed -11644,0x1b429f416dc733d766ec2bcbd4a37c0d23602e19 -11667,0x1cb72b46350f21e98995293bedf1b6af3b2656ab -11656,0x32961831ec907b5dfa784d691f587fc8fbb93b00 -11634,0x40f294615a264ef53062805d60eff3d93763650c -11641,0x48a57756b14ab02549f6e4cc50ab72e8f7aad7a5 -11651,0x55a5334d1a402383c5a8c622301ea00cc8cd1681 -11635,0x57b963fbb8e4bfb6d9047ac6d5ed183fbe6e7397 -11646,0x6458df5d764284346c19d88a104fd3d692471499 -11672,0x66e8617d1df7ab523a316a6c01d16aa5bed93681 -11655,0x66f31345cb9477b427a1036d43f923a557c432a4 -11652,0x68797130d8e63745761c524c33121fdd7290cb72 -11657,0x73159bb46befdec16a94a2ee056dd5b0a2c2a58f -11642,0x754b8955a07bdcda1476018fd84c608eda124aca -11660,0x760eb950b718f51efe6405c6c18bf979e32287e2 -11673,0x822be0563b923dabffcd1080c762722336bcdfd8 -11654,0x853dab28a0985aa55e14baaf499cf16e7250ba07 -11680,0x99bfdee8d6abba65acc70d1b96b457b38a78affd -11666,0x9ad274e20a153451775ff29d546949a254c4a1bc -11670,0x9bd0342fc5f66ea8f4e7257d6d9d0cfaddd5467d -11643,0xa4acaf871759bc0f6f1ca0bd3819e7fafe8b7acb -11639,0xa5f634c44f4a8a187ee03df1a8a39e4928cadb37 -11679,0xb045a5010089b53ecf6d08ef85eac77897864ae3 -11648,0xb9028590e4fcc0015c1907c4e11708e5911d54eb -11677,0xc0bb819b0acdeb933a58b32a4bff43af4cac1aea -11638,0xc3abffcca5a9870510d3869f86340145effd7d89 -11678,0xc69d44efd880e274da90dedc4c4a9b8adf37fe78 -11661,0xc724ec30b202d885f1349aa740be3132fdd61a83 -11665,0xcc6b9eed17aff79e86b5fedabfd46bcce572ba97 -11664,0xcfce125584a1d0c77cea78a267a521d8aa9af525 -11674,0xd0a8822202ad830dd1a6c8afe1e343151714c5b6 -11671,0xd3046bcdfca6f114d8c0b46fdb049f7b49261cdf -11649,0xde1d0ad1aa256834d849b6e954be15e0e7d46767 -11676,0xe4de4b87345815c71aa843ea4841bcdc682637bb -11658,0xf3c85aa8d48ba8bcaa4424ce8f121f8a62d59002 -11668,0xf6fefabe5034b4ac95e9ed4e99bfc1a688278a4e -11659,0xf790ff41ffc2b9a4fe203485fd9b431b13ee7a1e -11636,0xf899e3909b4492859d44260e1de41a9e663e70f5 -11640,0xf965579c83ea3f18284723a4c7e0788bd9a375fc -11650,0xff3ae4d3f1dd772d295614c82ed79958748153e5 -11742,0x0483e8b3a7d005b6c6e1e9581fda0742299b882f -11774,0x057319be3e73ccebba3935c4bdfdfc0e853c2268 -11759,0x090e4125781f6fe88a08908975546d01ec9b03e7 -11744,0x0cb52fbdf21d1cf32c48b0a4143b47a852125ac8 -11765,0x10cb0a95dce1d5e0f8796f80c8f08c31a9af9b5d -11709,0x116886bd236e65d1ca1ea896743aeac7cabc34b7 -11780,0x148d20b7767548082d671cf03e39cadc44232a64 -11752,0x1ee88aa687051e7de94d3bf68c410c61619bba6a -11725,0x209db142306a5be44fc6e1ea65aab49c258ae2f6 -11764,0x21239e2a5e70f4c7ced03566e841e6ed6f16b616 -11718,0x24e215a6dbb81de5903eb1e7cb890546727f75be -11751,0x269f993c0920d91f54395157e5f8393e733ffc85 -11736,0x2a4a4f6330b30f257146bb0bc6e57019b8ddbe78 -11734,0x2c7de1f2cb771b5fa5d128a8d197e492dbe76920 -11723,0x497cf78c31151228ce7e5929dc9dd146e0cc8a64 -11756,0x49fbb218d6a85ef1683cc8b45f1c8ac6c19a263a -11747,0x4c72a641858e9fd0782a409908d2a3905cc28e5f -11761,0x53431b13e9d353676658e6da81186301fee31526 -11745,0x53f1e2cfbbd04368b270c179fcc8468479b8c945 -11738,0x54e986cbe464e243700083ba4f2da64f6648343f -11735,0x563b244a67aec9eb2cef5509ae633a22448cd02f -11719,0x594d2e3101aad23e9d27be3b8d9127c753309162 -11720,0x5e0af2ded0fd10b0990c0b57af0d7f2af877285b -11753,0x623edfe9c2975f7cb4cd8a84b992b34414331d48 -11781,0x67b208dbb6bdcacf196568083012d170be3e6f0c -11783,0x6e816b778361824278dd5871999014bca4d3213b -11754,0x72e5ef6f2ec88f826e694efb8cd8831c565b2ab6 -11773,0x74a002d13f5f8af7f9a971f006b9a46c9b31dabd -11716,0x76829bd875a06ddbe41aaf5ddbf5087dd17ddbed -11777,0x7744f3a3c01ffb1f1c46b7dee2145378c69b69a7 -11711,0x77c015719603aacc2a617fa861fb2c1a894099b4 -11739,0x77ff820dda15107879d8118cf801be05838d4625 -11748,0x7c7d496250a4de2c423a7462b71276658d6b483b -11766,0x7ceeed1a89cac2cd3521484723353778eeb29c2c -11763,0x7fbc1453e2b5905cdf62d1d1e2b1cf2c4937c07a -11771,0x80e13ffe9be0a9b79923f5ad9f29c72cc2f2d9fa -11722,0x85ee14294d67c40a9911a63d4dd445433c219389 -11724,0x866724e06f787f45028f4f10eb0de195a75a99fd -11715,0x86ee25f6f0b4a568dd134b78207c6c7b196c5eea -11737,0x89865676cbc34a530393ed2eca6619f75856edc8 -11717,0x8a9c590f89a0807bfa93cb1cbdd491295a097ee6 -11767,0x8d8c1461b86dfea1f98fbf149c7f42c9217eb66b -11728,0x8e95c2d5a4aa296c9d905d38362394cf7f91c165 -11710,0x92d1ed7eeed154a01196ed32f56ec8336be1e25d -11768,0x94c1ff1951c38fc3df189c8f4edf01e3bf4d1e5b -11770,0x9571927ed09c3591b834a80b889ab2d277ee22dc -11732,0xa232cf1ac685eb46eb3aef2f2c2db30313472c15 -11746,0xa5ff63489c195b4e44727c36df7e0f61c2d457c3 -11726,0xa6d7e5c7f7799bdae196f8bbefa4846ebc98061f -11727,0xa881276209fc2b52a2d5637f218a03c5476c452d -11708,0xabc2f529a40a1a337e0c1da12c004b61bcbe2189 -11769,0xad34f39893896fb4925e1364178805aece2d43e5 -11749,0xb039c73a193931fcd9ab1929de43bc96889f2cc0 -11779,0xb231a7faa1e58ee7cfef23f1dbd4e59fe641b50d -11762,0xb7b4e96dcfdf51d3c35aa22c18092530daed5038 -11731,0xb8830f6f23dfca3a3b2f29f5a1e5449abd3dde80 -11729,0xb98b90d76fca614f31f8c677beb36e92c8f2e886 -11778,0xbb6f8affdca29aa1a282e6a21b192b6513a57f9a -11733,0xc1594af4c8e13a11f92ce3c887ff417be72fe6d3 -11714,0xc504cf3b7730774515b2c2ce8228a38e20ca062f -11721,0xc9a42690912f6bd134dbc4e2493158b3d72cad21 -11760,0xc9c8fabc17cf597530ff3a2b7c1be1761ac9e123 -11712,0xd01716fd8fd62749cde6761e64d36f76d03d52d9 -11730,0xd574fae75dedbbb6697077bb39186b12ba384895 -11740,0xd70e8b2c85c82882e0594ee076c9d6c57f9c8371 -11757,0xd730aef2379e8e0e5ad44a1d2e836bf7ec5e33b9 -11713,0xd8de990f679878f2ac3bba2d35e905a641b0bf0a -11758,0xd8e8d3ab0ec8c42b850694a3f7f80ba909956313 -11741,0xdb48afcb76242e63b8ac7c048973ae4272add973 -11776,0xdc159584554060f2e372cec24fa3b2c3e4b615ea -11750,0xee8369a567af599e7ae75cf19dd815c7f3350a54 -11775,0xf022df302ef9c605a8b0c9335135dacba3c35053 -11755,0xf23f40ed993c03d44d5b8817390cb7804e502695 -11743,0xf5a70eb5c9455a17f2a4622ef35b0067bcb5b802 -11782,0xfedb46925b7c98e2ddcd233d92eda74d43e26cc2 -11843,0x0110bb5739a6f82eafc748418e572fc67d854a0f -11823,0x065e8a87b8f11aed6facf9447abe5e8c5d7502b6 -11821,0x10b7f03e68e2a5f88f43a032ab3acff2d152858d -11814,0x11c083ce07e5d5202d3d8ba0aae2744dda7c6233 -11816,0x1bbf00cf5ad097236a05e91f2745c8221b54e256 -11804,0x20975da6eb930d592b9d78f451a9156db5e4c77b -11813,0x2607adea2dedccb3bfaf4c8cb762eaa88326db6f -11831,0x3768c34646e27a91dce6fbc870580b7aadfce56f -11832,0x3845f099ac03923d441ab98b5d804b9deed572b9 -11799,0x38c263f062f3b595639473fedf9073ad462c510f -11838,0x39c8e4b591fb63f0b6fcf3b43a2ae45fd4fbc3d8 -11796,0x3dadc74b465034276be0fa55240e1a67d7e3a266 -11812,0x3f9da045b0f77d707ea4061110339c4ea8ecfa70 -11833,0x40a863955742dd6cfc873a7d69dc85113fa8085f -11805,0x422658f19af1623ace117c25973618bdd8808259 -11809,0x445e27a25606dad4321c7b97ef8350a9585a5c78 -11801,0x45c964947f6cf495d5878f28410fd0c3e95d70b1 -11825,0x4bd99cea23d7d93bc22344b2fcbc8759daeaa1f7 -11815,0x522149e80bcef74de8592a48a4698e195c38ad37 -11842,0x5c9d55b78febcc2061715ba4f57ecf8ea2711f2c -11806,0x5d9102d6a0734fc6731a958a685de18101d98357 -11826,0x5ee0abe060957a6d364d6b9a0533419d18981de0 -11811,0x6138b40551452b7584ad96f8207a74f002d82fa9 -11807,0x6290498c50e9871064bc1e271a93d662a302848d -11803,0x6df964f72a15b29e1ad59e24e745c50918015b96 -11828,0x6f301e65e0ede390e24e847aa16925ee01ffe745 -11827,0x700ca78cfc794c57f2b5b6a86dd0d166cd867f8e -11798,0x7305a4d8c9d01aed3c0cba9a9f0f359e92160833 -11830,0x784116d2abed09647c129a9b4e92676d75fca180 -11810,0x79c825cb035d600671b286cb2a8e767e020558e1 -11824,0x7f3f57c0fec124a542d0212c202bbfba16c505fb -11802,0x829b1c7b9d024a3915215b8abf5244a4bfc28db4 -11808,0x8c0ef71b6ffb7786db4b4b00ed2503c8896e6038 -11820,0x8dd335977020b67a2b730554447825d2042359f0 -11837,0x952b7f34986e9c4f7b82a715670784e7b6064e2a -11822,0x9c2bcd81d612cd8aff5ff9a87461adddb7599e20 -11836,0xa7078e74d1abcc57db40309cf2c680832d7cd1fc -11800,0xa921c8799ba111f2ba545a44353207a219d40ffb -11839,0xc086a19470114ade2535317502727268bee352ae -11817,0xc78a09d6a4badecc7614a339fd264b7290361ef1 -11829,0xc8369642f6ec99da81072388b2e6d0ecdb148620 -11818,0xc96f6c795a281da306567e79e6e85fe636e8113f -11819,0xd3faab5acba8979d68dab32f3edc93b0abca0411 -11797,0xe2648e1654fc8349b3c13c8eac7b77a22930ff8c -11834,0xe5c7b4865d7f2b08faadf3f6d392e6d6fa7b903c -11835,0xf14a0ca754d1ba530dedc77a578712c461757427 -11936,0x03518ad0aaad97142262c57f0a07a1220e99805c -11935,0x1b46a4b50c7ee6b50e5c57458db3736511e9e48f -11940,0x36b0017aa6d08f8d663223d56aac9148a3268a6b -11930,0x4cd9ccf47cec9bfdef25e0aab2b680c50e28b337 -11928,0x69318316a6a6b8c62d92736e67819d1fc037c135 -11939,0x6c3f41c2d712dc1701009561af5580efed574c92 -11934,0x79946eac000c85c83b6ba3adfa9ed7f4e2314e84 -11929,0x8aab3bdee6bbbef06293bcb7c48d22015d2f86dc -11932,0xa5d1e186f22d7e289d4afc55d74c1fe06dd6548b -11938,0xad134801fc0dd62b3a954bce6066172cfb40631a -11937,0xb68842952cc6a329798001a383c48cc1bbaf38b5 -11933,0xc8e0c477b06abcab3e4546a814ac157cad6e3b58 -11931,0xdf5043e0008dea21b6407da5d026108a467cdf63 -12013,0x027493c97a8c3e7f40930e9cb1404ec41c2d5626 -11964,0x062016cd29fabb26c52bab646878987fc9b0bc55 -11979,0x0930665cb598e8868a9492e66453bb0ca18cf411 -12009,0x0a53ab9005b495398e9e4aef29ab32e34a777af0 -12042,0x10c614d6e24d570dea899e557bd42e043007a23f -12015,0x14bf452df289086edd1aa03dfcef73a6ae0b09df -12000,0x1d864edca89b99580c46cec4091103d7fb85adcf -12025,0x22f39d6535df5767f8f57fee3b2f941410773ec4 -11987,0x283bcea12ac20fb9d48bdfad1fa856dd2922ce23 -12008,0x29f21ff936ffebf4441111fe479e95c99f904096 -11988,0x2bdcf5e0aa88583d5a8c5d7993fd790eb26d58f9 -12049,0x2d4b368168ce5fd44113d9e71073ded0abd98af9 -12007,0x2f3dc43dc83e01485f11a90f3cb6bd6ee00453f6 -12019,0x39f8750b320b7a9562db638eea924eb3131243dc -12016,0x3be088fc83ab85934e3ac75be7181d50c30ef783 -12030,0x3c82a9514327a93928108e9f00d89877f4beb6e3 -11992,0x3e50bc3afa396f71b8a1f2ee546091b629996de9 -11961,0x3f56e0c36d275367b8c502090edf38289b3dea0d -11967,0x43ef5c19e24e45935e6f44b3bfb6b9bbd3199df0 -12004,0x47e720d1f4f82f3a18959c5c909cfb4f8f0f42dd -12022,0x480798fac621add14113ecc82638305c260ceaf1 -12035,0x4f31874b2be6af6039461a64db403650b8ec4f0f -12023,0x5057effde2979fb5c7038a362a9dd46eabf30b05 -12020,0x57bb323e9e14575b9a1f95f80cc4b1b5299644c4 -11996,0x5a6325c3e3c88dbcd52a8d55a31b342d09fa7982 -11990,0x5d7bf1dd3efc9639ca3d44e45dda988142d0dcf0 -11978,0x60d133c666919b54a3254e0d3f14332cb783b733 -11976,0x615b25500403eb688be49221b303084d9cf0e5b4 -12021,0x6186a0d86bf9a81ddf2d61376122c2fcd86f0d36 -12017,0x66be0cc7230767755583f33c525cfedc4b3b951c -12014,0x6928e88007efe020a6b2d38eb68eb4e3ac9288aa -12002,0x6c59438a61edf9200b6f3f7f5eb94e9a44bc3ae9 -11983,0x70b2ec409745d8f3cf001ab9f5d87fa6f8860405 -12027,0x7198ff382b5798dab7dc72a23c1fec9dc091893b -11993,0x77965b3282dfdeb258b7ad77e833ad7ee508b878 -12012,0x7afb2409d5464b252659b8f3e6cba7517e54069e -11981,0x80762c8b0dbf57324dcbeb867d59c4fc6ffdd960 -12044,0x80ff0aa765e49d451ff7c7d046f7e8ba732d8bb5 -11968,0x812dc8e1dc490862493010eeab0f0592e4cbb5d8 -12039,0x81f914f374daa0a2845e1263e4f5c3e559d6c026 -11995,0x82e90eb7034c1df646bd06afb9e67281aab5ed28 -12033,0x86f78d3cbca0636817ad9e27a44996c738ec4932 -12001,0x881dace37c6fa4a5364bf4806d0e9f8dad8098e8 -12005,0x8d3d25f3c27e00fdcd1d19b840def02a7090ba32 -12043,0x903bd2805099f3265d50363cc930d25271f79312 -12036,0x9190697ec28c3954bc61ee21ab0e7ebd52545119 -12031,0x926b92b15385981416a5e0dcb4f8b31733d598cf -12003,0x929596c08815cf9d97e3c8280017dc74be81c12c -12045,0x9433b2017410fe925e52eb3d19642910b503302c -12047,0x954ac12c339c60eafbb32213b15af3f7c7a0dec2 -12034,0x95c21c556124c9e90051d6fdc88c6e833318ae7c -11970,0x983e54c2f5c8d83b73b80ddee2446dd616de0067 -11972,0xa3c98af4598cc64e13e327fba34f1529979c1e63 -12024,0xa478e708a27853848c6bc979668fe6225fee46fa -11984,0xab91c51b55f7dd7b34f2fd7217506fd5b632b2b9 -12006,0xaec4d8ad3fcbbadac26b87ca1696ebd5b6f6a996 -11999,0xb0aed7923f7fbeaf5bb2caa4a049a51d638be2c9 -12032,0xb0b2fbeacdc6fea615a3c0ab32948a241dba7ccc -11966,0xb7675b47d7a386a770e5bd17e29720d07221dead -11975,0xb89c1b3d9f335b9d8bb16016f3d60160ae71041f -11962,0xb9c8f0d3254007ee4b98970b94544e473cd610ec -12048,0xbdef6dad6841aa60caf462baaee0aa912eef817a -11969,0xbf1aea8670d2528e08334083616dd9c5f3b087ae -11973,0xc09c73f7b32573d178138e76c0e286ba21085c20 -11977,0xc09fa75b57d23e500fa5e6c35e0a4c54c467a457 -11997,0xc6dda364c8becb3322c2b867e19f433a7dcc08da -12040,0xc733418b56591f3b2599ddc08c52287645c4ac90 -12046,0xc88c8ada95d92c149377aa660837460775dcc6d9 -12037,0xcace31408a06327984a86438481f0873f0c8d06b -11985,0xcf38a5706cf52726f563b35c8a69d1658b6871d1 -11974,0xcfde0e91d6097413a26161d39154a68313c72378 -11998,0xd13ed4879dcf81c181da82c46f4d0689b0734f23 -12010,0xd7429ff08dd10008f8906180a7f4a6dc75f0224c -11986,0xd9cc12ee8edb37b8484984c2d80dffe8d53beadd -11994,0xdb5d7086c5198e8a4da5bd2972c8584309c3759e -11971,0xdc8cd2973a57476cc0cd493e5374434ed4be25de -12011,0xdd288f6f4d7efde6bc1f17d1e85a14d2071a71b6 -11960,0xdfa46478f9e5ea86d57387849598dbfb2e964b02 -12029,0xe4a809f6699f6c282a09d556572e87bc653152c0 -12028,0xe8f6b8a4068c95c018dd7cbe0d0a5811848eb921 -12038,0xe9d954a9a6a1a61bc1120970f84cdd76562c4a0c -11980,0xeaa460b5493fa24f7b4f42eb37c2ef44e8a9ced8 -11989,0xec744346b11d8759890ee43143cdd0b9a8929709 -12018,0xf241dac3cc01f1cc9fcbc1ada8c1c800c0a1fde1 -11965,0xf4fa968578723580935a00d1e12fe96bc6401947 -11991,0xf5942411137a1be0501204ed9dd5b4bc5b714e8e -12026,0xf9973b6bf045755c09b36aa4a21f11e5c5f78caf -11982,0xf9ce2522027bd40d3b1aee4abe969831fe3beaf5 -11963,0xfbeb75c41a606849dac686fb87afc50e5dae64df -12041,0xfc8070e692c1c3c9705656809a76183ccfa709f7 -12081,0x1c6cd107fb71768fbc46f8b6180eec155c03eeb5 -12087,0x2880ab155794e7179c9ee2e38200202908c17b43 -12085,0x47c409845b57ba4004c26a5841e59dc15bb39e7b -12089,0x87047526937246727e4869c5f76a347160e08672 -12082,0xb2eeb93d778c364e7e2274d6299e2aa0c2bea090 -12084,0xbf2c77dd0627a1963991b1c7a04438cde94f96ac -12088,0xe9d69cdd6fe41e7b621b4a688c5d1a68cb5c8adc -12086,0xf5bbe9558f4bf37f1eb82fb2cedb1c775fa56832 -12083,0xff1a0f4744e8582df1ae09d5611b887b6a12925c -12153,0x1c7ccf4057c04fd4fea4b1a8b0484770569bbbe4 -12137,0x2b7b735857ec9badcbfa37581a76a2f1d4ac7fd0 -12145,0x3281a7ad63436c1253f8033b800eb7727cca226a -12146,0x373bd1e154fda2a43a8696b1434a793576460593 -12150,0x3c3f2c8c9c3b3199e9a1c3915754209218155050 -12147,0x6724c95af33e396d85d2fc93609c60d23490878b -12138,0x6e58a098add16e083261803340863d215ff7d352 -12143,0x77ae10dd2e7fc7b2819602d483765d705e9b8dda -12140,0x7b3e2ec40c241b424b08fd31937d22137793a00c -12142,0x7e01b39214080559e495294116deca84afa8e716 -12144,0x844d982fdc1f5c8ead82e95d522f2486a7cbb3ee -12135,0x86a258f330ddf62a222b492cd4bbac34f5a62e2f -12149,0xa562fd780b05b4c6d895ba78d5519ddf0a5d25d6 -12152,0xaf73b136dd1902e36e33e968be93e30e17a5b01e -12136,0xb2f76a5ac3678d11b5e09431cef6edd65b63017e -12151,0xbba4db63da448c124ee38eec636b697ca9bdf9e1 -12139,0xc17e9347ce26d7630a98ec4158bd7200e54bf4cd -12141,0xc6c17896fa051083324f2ad0ed4555dc46d96e7f -12148,0xf20fc12a4955c9d47194b8fed591fe01777d2b06 -12134,0xf7e89ed8106cba1335e04837e59a28ae1a3d580c -12332,0x0153c8aa92040c22f7c1045960f2a42cf7c3e9a3 -12309,0x0389996552f5da35fa6ddc80b083f78622df3a6f -12315,0x0668b83c505e817677ebcae72a7f9e0b009c2e92 -12293,0x089e3422f23a57fd07ae68a4ffb7268b3bd78fa2 -12324,0x0e614f2987f3afd8312c45066f3068fbbdbf2578 -12288,0x0f6e8ef18fb5bb61d545fee60f779d8aed60408f -12301,0x14ac2da11c2cf07ea4c64c83be108b8f11e48f20 -12297,0x165958225dfd1ca035e66318486968da1521d43a -12317,0x17bae0e202f6a22f2631b037c0660a88990d6023 -12328,0x1d5d4dec31e8aadfbffe167d844ad734a169c695 -12347,0x23c74cb91085c4cb2b76cea709ae50309f79dbbd -12343,0x278ddde1ea2404c82c5dd71694bc0058e8789b70 -12334,0x28f93307c8df3710ad3ecfb23c1920b58684ae31 -12316,0x2a3566dd93407f2542a6cdf421e5e444c00cabd0 -12319,0x312ccd65da10e12bab2f5b09732b22db3f047315 -12325,0x342e36c6268fb06e9fddc578ed5df013864dd0c8 -12344,0x380eb51db6fe77a8876cb0735164cb8af7f80cb5 -12322,0x38180dd6d01f6774d8060e3ab8d06475125abb56 -12292,0x3a87bb29b984d672664aa1dd2d19d2e8b24f0f2a -12313,0x4024f711b40f9fa42620123fc179102a53e6a500 -12289,0x4819a04d0783e7d1b094cd36f56000976c3db223 -12349,0x49421e40d3c4ec58bbdb13d38b410c47d4af3357 -12290,0x4f273f4efa9ecf5dd245a338fad9fe0bab63b350 -12337,0x4f7c0bbe210b3a6e124b67c5604cf50a8c695cbe -12308,0x5747a767799ca0bc1f4470e17313d4aaaf5e0bb9 -12327,0x585ec17b609daf9ae8f43d5265f1512b04e90823 -12338,0x5af7a354c9c35b58b4278ab0e1e934fab01b26ab -12302,0x657b70fe0b8d49e5af63b2f874e403a291358165 -12331,0x67fc5aa239d6b3c9ea827923a1809da3fbc374b0 -12333,0x6c22b379edcbf092213e2763635f698b495cdd8f -12340,0x7453378873a8aa7d3822668d3753fed20c23e585 -12307,0x7518ea3bc20474d4d9871f40e3ed4dc31ab2b48d -12298,0x7bf2392bd078c8353069cffeacc67c094079be23 -12299,0x7fa86681a7c19416950bae6c04a5116f3b07116d -12306,0x834c025fa5eb6726803a2d67f160fcfabc49a174 -12300,0x89b36ce3491f2258793c7408bd46aac725973ba2 -12310,0x8d69eacdffee59c5f4c9936adabe72eaf891745d -12287,0x982ad8f6b468dfc460e3cd3087df348895ca3080 -12295,0x9abb27581c2e46a114f8c367355851e0580e9703 -12341,0x9b0efa67e8112d8ea2eb3c86c798b2bb88467335 -12348,0x9bcb8ce123e4bfa53c2319b12dbfb6f7b7675a30 -12303,0x9d22c080fde848f47b0c7654483715f27e44e433 -12294,0x9d3f42c5ca9426303b91567288e461230049e092 -12314,0xa7fbba9bf51a3c4301580e355d9c21db3b89bddf -12320,0xb6f767453b41d71112a910d3b3f7e35d7ff7231f -12346,0xb868c6b48a9658f526caa262bd188947b32999da -12291,0xbfdc37499d99046710a9c567016791c71cd25cf6 -12329,0xc3e71dc2cacbb2b973ab43afcc2ff5238e4a60c5 -12286,0xc4b2c51f969e0713e799de73b7f130fb7bb604cf -12330,0xc73fc9215a9e6824b15b212918b5f8d0892ebaad -12312,0xcf18fc4a662aa6a5b2ad6af873a82b6848f6ce17 -12318,0xd10851101533a7331debee99e49736e5d20d6b1f -12342,0xd34d11423ebb6454fb91518ac53c3a202b4ed683 -12339,0xd8a0d357171bebc63cea559c4e9cd182c1bf25ef -12335,0xdb3b47664f3211c389db9f6136927d563680a0e0 -12311,0xde8400fb8dc171fe452f11b5b6daee8ecefb4aa4 -12321,0xdf9f14992503ea0cb61b30ea3b0c5cce63be7a33 -12326,0xe835b7ab7807d1ef33c9fbe1854983292040d7e1 -12345,0xf2d39faee1f598a024a57da2e5f18542f444942d -12323,0xf4f8746bad0c350e702585c6a1f602961d5741bf -12304,0xf61f5830e14cd418893b216c7bfd356c200f1b40 -12285,0xf92b8ad7a62437142c4bf87d91e2be0fe1f44e9f -12296,0xfaedc9496a76597002d42aa3ef12b47ebd9bd8c4 -12336,0xffced8801fb873a4cdc2304eed9905cf54083ac9 -12601,0x01a1f0699356afeb850f5a80226c35a9319caf74 -12598,0x1470c87e2db5247a36c60de3d65d7c972c62ea0f -12581,0x1bd4745df0b5286d410f22fe6d43e797c4d5ce98 -12603,0x267dd4034830bb4ba9314e0471c1ddfd79849777 -12608,0x2eb3f768a00ec81e8a0ac337870572907d3b953d -12586,0x314060502e823de08d15ff3f5208b8e82f51acfc -12604,0x46d2f8e4d8ff3d76cf252d89dd9b422f04123d2c -12602,0x4c65f496b78b7e81c15723f56a43925e5dc3a0e1 -12584,0x4ecb5300d9ec6bca09d66bfd8dcb532e3192dda1 -12576,0x574834c934b3784aa8430a5b45724aaa2fcf2c7f -12595,0x5e5b54cd73872ba3103cd95a58067a7079d0259b -12596,0x62bb4fc73094c83b5e952c2180b23fa7054954c4 -12588,0x722e9bfc008358ac2d445a8d892cf7b62b550f3f -12579,0x73a7d35bb34e6c7b0bc06e6399cdde05320bcbc3 -12594,0x74312cab0d0ab42d4b4f7ae424ef2ea35e3cd472 -12592,0x79a80e8f952cfb4249613315eb71bc63b1d2b685 -12600,0x79bc8bd53244bc8a9c8c27509a2d573650a83373 -12583,0x79ed98ef5561f1ed3058acb5a30f05e8f64e560b -12607,0x7e8b7b3ad23caa7155603db3f51e4529fdc606c2 -12585,0x85c0d1c1714da886052c1b04c42ff7076ff5e222 -12599,0x890a87e71e731342a6d10e7628bd1f0733ce3296 -12606,0x8d7355075b85b2add775e02ec581e0da66326e49 -12577,0x8e4f3098192948e342fb11ed7c4a23cd5a306973 -12593,0x8e6670d62ea8d0ba4662de8b4db690c70af9440e -12612,0x91a252e0bc6082e6ca0fc554968177bc5dbd53f1 -12589,0x94db8aaae2b1d143dbab67257cfa608374a4ac40 -12605,0xa2c882c66797b201788eef25e3ca3411204bf3d7 -12587,0xa365317291122b44a549c34a383ccd117b71941e -12578,0xa6ec73c91d0ae39a854bd50a6b1cf9aa7da46ba4 -12590,0xa843cfbf6549e3c93453a74e762f1af66575ee89 -12591,0xb47e3984b81298f10c0ba75e26362e044b8488f9 -12609,0xb75c7e8d65fdc32678617fd20f4c2404cb492562 -12580,0xc88f04d5d00367ecd016228302a1eacfab164dba -12582,0xcddfa3bfc0e548d9a526e3355fefdf987f4e1aae -12610,0xd23723fef8a16b77eadc1fc822ae4170ba9d4009 -12597,0xdd96e37d6dd515a5e7325503fb3958192f98e49f -12611,0xeef2297e15cd085a0ad33197ce8c2eab6f023c2b -12755,0x0052a341bea99c4b452816235fc2216143c5f21c -12678,0x014c8929557aed2f7eabfc0f82e286aa954bc5f5 -12774,0x035bb9f8d4d390bf75c97e2d95f1eba6eada1ad8 -12763,0x05b7ab7924000ce12e304702cd82539923c26476 -12745,0x084b02a201589242159a3c717b6957cd9b502291 -12681,0x0a49571ba82693353fd2285e894c00fdad5fc4b8 -12786,0x0a54608c6bfa09a5e95a3824154d59005ae03bcd -12671,0x0b2ccca8d866cca4c025d595faa29760870575c9 -12768,0x0be3a0e2944b1c43799e2d447d1367a397c4f573 -12738,0x0cb199af5f402506963a4df08b11053687e09802 -12656,0x0cc1ee0750445defe8b48ec9e52342660d8626a4 -12779,0x0cce27ce6f81470afebe6453c2d36a7ca738eb40 -12663,0x0d45c2198b3a5a30e82bcb1d02643fc366d17b01 -12655,0x0e3dae6768b979fc4325369cca940174c2775f7e -12666,0x0e69fce24d170c3fa172e0c7e29fc444ce0b704b -12701,0x0f827ba4d9389f71724739b7f9784505e0716d8c -12813,0x0f9315eb652f69e2f8eac6f7f615c8dd9010dec7 -12739,0x1036c75ba376354d455b5d98e8d847c68a1b4bfe -12780,0x14d582327df7a9885c299173dad6ae855a3e046d -12788,0x1580673b3002dd94104042f4cdeacc56e6fdbcb7 -12729,0x18b2bca751f33d97eb8e0ec7f20c3810473e98fd -12749,0x193209391242b585ae0aa6029ddd23f9ef99b779 -12665,0x19828283852a852f8cfff4696038bc19e5070a49 -12724,0x1ab15c21a7e58dd899581fbf7d2a06916cf7434a -12688,0x1baa02fd3744b299723f2e3ad2b7c241bea1afa5 -12830,0x1c2eaf0ed3e566fbdf528ba97b4e5ae0bb2ad051 -12818,0x1c5bcd2839088d995902623e9ed711080f23464b -12694,0x1cc4482c109395b56df6e6daf3f7bc1c957765d6 -12804,0x1dd01c452992f0bc3938c192f8785d62a919ccb3 -12658,0x1df92b3ac9aed27115ae4127ed098baec1bd8b26 -12691,0x1ede41b8d5ff5e8cb2b143f47aaa1fd541eab17f -12728,0x202695ad97cdd6e34855a656d8e0fc098de4102c -12826,0x221b474758173cdf75a577dcd5998cbbf71d1370 -12735,0x22b73bc2231f759b0a311ecffd8cfd2a99d89bce -12668,0x23cb080dd0eccdacbeb0beb2a769215280b5087d -12825,0x246dfdfa491cf7cad0f0a189f9836eaf5af15ef4 -12716,0x26446cd5b099b63da029c27d9989375b31bfbe98 -12758,0x2787028f58be0b8c0121b4321757685e6cc83cd0 -12809,0x2841a6c22cedfdaadab976764044f8531ebaecec -12797,0x2901f5f1d4c99f9eee36ccd9a264729c5dd35c11 -12684,0x2a42246d19e06589b6d47f8b3e93338aa172015e -12715,0x2bf91dce25b34e80fc915339af115e6ca82a0883 -12760,0x2cbfd62b20e6489186a423b2cee80977c41e4248 -12829,0x2d46292cbb3c601c6e2c74c32df3a4fce99b59c7 -12757,0x2d4937ed79d434290c4baea6d390b78c0bf907d8 -12773,0x2e09e05b53e991b04212f22ba6098db49f14c96c -12686,0x331cf6e3e59b18a8bc776a0f652af9e2b42781c5 -12815,0x3349de7822aa05f857e92d167b264809419dc620 -12650,0x3391696c974ca24559d3e0b970b075b3a25cca09 -12765,0x3768231470780a1e3a17503252d0978903edbea9 -12676,0x3873fa06a6c16a7be42047bff8ce83a1e6c151bd -12777,0x3961a5dccf2f7e76f0fb7ad4b92bbac645fb245e -12800,0x3a1ba86818cabcbe141e117ed9544f11f6c520e0 -12683,0x3a3482992a37cdf2015ff6efb90504911d3dfb38 -12725,0x3a76e0f571d9e2f7f2cab3f0d9cc6d9e04df5a3e -12753,0x3e29470d34cd4ecdbfdf78959af7bd4eb0971d1f -12718,0x3ef9bdfda9e6e0c778463ce6d81d56b075ce0374 -12654,0x3f3455f14f7494758aad45f8eb5c1856d0a8604d -12795,0x3fe6bb6275a00b235e56ba99100022eba00816a9 -12673,0x40ea796885f981ca1d2af0da8e6f8dc47b384933 -12798,0x41d4c067b82da8357dfc38e3f24f6033368af4bb -12713,0x436c89f77f6b6fbfe14d97cd9244e385fae94fea -12781,0x4395104fc82654ddeac3ecf01ab40a1814bfb2f4 -12689,0x4458a323717db34b0263c383895cab92a6c6ccb5 -12822,0x4714f064b5cd068471901aab7ca047a495c2c1c9 -12730,0x4a0b3986cb7e23df85a64100bf222cf69f9787aa -12816,0x4beffd38832b5f158d5a05d374ae971dd0e326f2 -12664,0x4c8dc708d58651eb67222d81e60c8bc703245a40 -12709,0x4f664c082602c48559e936c17bc13db0396cf9f2 -12677,0x4fa30418e2b4e98f94c1443ea2bdab95bb0168e4 -12803,0x50ff859de6bc8e71acc1dd73e5c4d15b46d04e63 -12824,0x53268e841e30278ef0b9597813893fa8e4559510 -12793,0x53aa7387fbd46b7acd81de0d8e6b6d467a2e3c26 -12736,0x550432033c18eef82ccb1853f1e691bc0504544f -12704,0x558c403a907d3b010c10e9afccf652a0b0d09338 -12651,0x56027bae94142f1e129e66339b6994f27f174792 -12748,0x561fa02773cc4414c104e8c59e97981578a2a299 -12661,0x5630b53033b5343100ef6c44a9fa8722f40a5333 -12669,0x56e1592a8ebfc1dbe9542b77176d4eb2cd507b64 -12695,0x577f1ff5b4049f284725e8172cdc127af6328ef7 -12705,0x5de450325cfc837bc36ba7fcbbeaacdbb93f5cc3 -12670,0x613783c08239f23e444189243092016079a129d6 -12767,0x64012b5efd1aa5be59cc1c9637a7e3a24b51925c -12783,0x6457f43caa86008c595b037061788068bfd5e58d -12742,0x65044b79cf1e8bda471c3b41e78b1418166ec20f -12737,0x66671fe1538da7e40cf58a247630b90f51acd92c -12792,0x684bbb31eb2f7891523fafc2cc5a0fb0a45c4dc4 -12696,0x6b2b5128a1fe8aa3f9ca96c0eb69837dd9283a5c -12680,0x6d39090983e4957bbd7f7c2aaca6a8110f10e79e -12823,0x705a1e30a1e0d94bed8055586ca2ffb55a09e5a1 -12821,0x71afc4ef5c25915ef2d46bca2a39730978f066f9 -12660,0x720a4255a8979ba3655d46bdbb30fed51a2abeeb -12733,0x7302ecbf667cef2a95f21d31339ce6fea6802f3d -12787,0x7634e43aa3f446c8d9d5014d609355f728361075 -12659,0x7ba95ca22c2da423924088d72a0825f6e9eabc92 -12759,0x7bfa659e247a70b501cf2e19a51ebfee28028db3 -12817,0x7d2da3a932bc05309c74df1cef7d28375835b185 -12756,0x7d9dfe621d0c4b95711a64107d7b8350403d83c7 -12764,0x80564f8d8e562a753afceb7fad3983747adfe649 -12776,0x812d2d0add720322866ad184c8227417bda58e27 -12699,0x835afb7b0f1f3a0df0ecdc7a4cf86b7894072ac6 -12828,0x83a7a99277d588fb986b5037dd7f7eaf3d5ddcff -12703,0x86ea8030221bf025bb11fadabb794daaa7051ae1 -12731,0x8c9fabf2285ee615e9cabdbbebd854db128b1985 -12814,0x8d033e3fbbcdca59e2346a7cb82e3daa95089573 -12820,0x8e3125b97b641dcf7ca53b899321f2ad51ca79da -12727,0x8fff92fa0b13d089380914e099b4dda63e079dfe -12708,0x90c5d64a67425d03774439d8d37194b29c2070fa -12790,0x920df046848ee6729b3f143ac7dfa5a9bc95305a -12687,0x93e7e22524771c4a694260a9e73be9cfff9d8baa -12810,0x961359f57ba57f3f3f228c55b98b6a3ec95ed266 -12702,0x965e460bf5cb38bada79fb2293c6304c799d0b1c -12706,0x96d5691cb41a96894904d6c678e52b9d0eea696b -12690,0x97109b2ff77979826c20ff41cd1017cf11f5bc76 -12667,0x9cb1ea57dedc564a38f207b5bb240efa8e6c77a2 -12652,0x9f4e24e48d1cd41fa87a481ae2242372bd32618c -12771,0xa11451acd2fbdf4ae798f44c4227cab9517739d7 -12831,0xa4965dc079a6891c6f26fcd723991357d839c2d9 -12662,0xa7a031f285187f2f8415a513a5fc0abd101a8775 -12785,0xaa9eec57296d00347b8a0a786aef2d8a81d2e01c -12693,0xab5084c0b087a4a197a7503d3a2c27210eceb098 -12789,0xac9735138c4518e8b762a7ee3d98b60141ab0578 -12685,0xad8066aa32f96e6703f5b713539a01c2affc06d2 -12726,0xade850bf6de778a207dc44e610fe7a45272fece9 -12807,0xb162f01c5bda7a68292410aaa059e7ce28d77c82 -12751,0xb1d38d7ace1f994a9797e77d1d76bfff6fa2578e -12778,0xb24639a89d78cb9558ccbc6af5a8ba1d0d401fa3 -12772,0xb28df1b71a5b3a638ecedf484e0545465a45d2ec -12717,0xb34bf40f0064dd94099953bdb3e52f77a15bfda9 -12719,0xb3eb9e71d7e44a54a0f94eadb6277bc7f2f87be1 -12720,0xb75a96880a7c43f6716f0a17a8b4c9b610676a28 -12806,0xb7b4270cfd938f4f1c111ac819e7365e8ce0300a -12714,0xb8d711e6330607647d699e21db92a1b7cb754049 -12648,0xb967a4bf28c769871e5805e99449ebef5a7a0d8c -12649,0xb99818612d134b081c7d078615c8e8627e6ab8cd -12794,0xba07e67860391d98a7019073319c4ae37875af29 -12740,0xbbeff97bab754f5b0044e4567495953c4ee9e19a -12682,0xbd4413a42e0a33274fbf605183aa9c98dc7aea7a -12692,0xc1760d9c91e502fa3235f06a5544728bc386128f -12743,0xc1f7a43db81e7dc4b3f4c6c2acdcbdc17c41b0dc -12812,0xc5631c21aed81b91160cee00426f1866bccb5ee8 -12741,0xc88dba2bd809873ca43048921d586387a5a43fd7 -12754,0xc9347b307b4aa891785c44a63ea6411c8f19c479 -12698,0xca8067a5ad7e869a1bc8bac113f45c960009a2dd -12734,0xcccbdecd3214196f497495ec02f7bfeb9ddb3943 -12747,0xccd718d6d2b410e4a3b1e3402f4e5db681a1f363 -12819,0xcdb462edd31ec9b18074feeb04494b77ae048d08 -12744,0xcf0ab34aa28e81cba4e4a3fa691459218e867b02 -12653,0xd01263bb1c4b5ba7f2afe90397212d84e74e1d0e -12723,0xd0a36f2ed527eda6c374460dad6e9f23dbda846a -12672,0xd22a2f72f614246444e79fd8a78b82a5f82e0649 -12761,0xd2a19c34abc544ed8da25991a5811e8231f08826 -12674,0xd33d811ed08993ea56f1ecf985f665669ef88293 -12697,0xd34f4fae4d5ea96f15cb26af0e45a90eabcd7b47 -12808,0xd514a62fc186343e70c8ff6ecfee3a7a4acade1f -12712,0xd567e18fdf8afa58953dd8b0c1b6c97adf67566b -12762,0xd60eebe694cab912702e9207a63dde15dea7fcc9 -12679,0xd76d084908b0a03543a20eda683bec595637c013 -12700,0xd783aadb0338035dcc9d6b0b93d2b12379e24fd3 -12710,0xd7ef973de2761e2623f83397533688c5fdc0763a -12811,0xd81abcdd67c7eb1c67c56c10a2077938037e4b06 -12752,0xda3588474026bc257297f95c91e9d87a7d65f641 -12791,0xde16ce9ae3c6d788ca6de1e19e6bc6ae81e0630b -12750,0xdeeb242e045e5827edf526399bd13e7ffeba4281 -12799,0xe0d04697749e074ac3521b8b42cc14cd79d74ac3 -12721,0xe1e4ba79dbe0228aa5f65696ade1d147d590f4b3 -12770,0xe2bfa528a1e15e5abc2f5fe8980f743bc6b07ffe -12657,0xe38462409a2d960d9431ac452d5ffa20f4120f51 -12675,0xe4271c66783e7eeb6578b711d12e8b12ddd5ceaf -12784,0xe6c7a70b43cdc28c45705fadd3f79bdb2d1bc702 -12707,0xe7ec5389cd1f9a064b37813a6f46c57b41025000 -12782,0xe8f2a6b6ca9d6c342171a521bf9e22f20ed398a8 -12711,0xea48dd74ba1ff41b705ba5cf993b2d558e12d860 -12769,0xed2d957670fed2498093dbcd9f34b6d024b2825b -12801,0xed99d0fcdb1952e06a7be055eafca4f76fc65473 -12796,0xee3dd0adace9b29b56f7176e6190c2e7bfe54594 -12775,0xee7ae6c285d169d09b7248572388174762b8aafc -12827,0xef0868f755d4f94a8b11c20a5bef8b86804e9b3e -12802,0xef5f0c5d39ad78b5fd3bdb0e55704c399ee4f04a -12722,0xf40508173319df27923e7bdf52a3880c8629b1a6 -12746,0xf7452f93af82d085a1f1d0175436924ea765c8df -12732,0xfa923aa6b4df5bea456df37fa044b37f0fddcdb4 -12766,0xfc1cba42eb6febf27e0d11def37b6d5d40a88896 -12805,0xfc506a5d5e5c84970878d010b269b095d61e2c67 -13289,0x015137997fdbe5cdc53764e890f9233b59b07420 -13357,0x0195bf18d510188d62edc2f7f9b7bfc8069af92b -13150,0x01d505bab0bd18bfdd54badf1aa7f4695f3942bf -13204,0x060c4cb78f1a4508ad84cf2a65c6df9afe3253fe -13125,0x068d9e13d687fabbe6e1adbb42e9c4c316347ede -13228,0x06e843f88bda2c6a10cdc0b7f4782869675d4c4e -13320,0x072a62a495a7f9530be708d1210f8ad586dfa4dd -13057,0x07537044cf1f4e365889d0bedae28ee19771a0f0 -13332,0x082c9cb8e6bffc08aeee638a2e1a93a7de477c92 -13079,0x084a888d533b8e0a106722c15a88ac8927cbb842 -13069,0x0a1e7ff4b239e6c628587d6868dcfed6e4d07218 -13212,0x0c7d59f21183c6817a436173ddb04b7635eac627 -13195,0x0d32fae6f6c50f1bd67c562e308fafacc6cec722 -13388,0x0ee954f13d8b2d570df8e8573c63211937bbd8f8 -13321,0x0f0ab8930f96d991bcc2161b1c3ad56a6d643f4f -13391,0x107daa44d5d0cc93246256dea9c9c30052cbdfe2 -13207,0x10945d17fac497ddf0eb7e53424fd9472224bb39 -13116,0x10b38e3884d587ccb1130b3bba3f8f48ca0ba839 -13273,0x10f26a1adfd5f90a8f01bbd69895e822449510d0 -13181,0x131e573c4de13c39f62180e90a32ba36691b7497 -13198,0x1365997441a4b1d2446d9b350b50a91001f0e050 -13066,0x13e533d1c0154d3790c8eef018b5c70261f079fc -13193,0x14eba8e3dc746986c28c18f72d9902788607bbbd -13363,0x1508fbb7928aedc86bee68c91bc4afcf493b0e78 -13217,0x17b4ce6e6c7689176e665c89528fe957d5f97819 -13293,0x17e8d2ca5d3e729ee2280ee496cf985b797004d8 -13264,0x17fbf991c46160fc9bbb4f65caa07fb71e10819a -13343,0x1851ccedf7178946975017cb6315b4fd34ef10f1 -13051,0x1974f96cf5e10c7b1c4a165d74eebdff49218daa -13205,0x1997943afcf4ab46811494e01c05b634e982e70c -13249,0x19f7b17fe8c33a1170123599cfd642591ba122d0 -13102,0x1c3831ef242a86ea39632f9c54cde54f0394d4d3 -13121,0x1c383347627fbc0ec262d5bea33c071cb95015db -13373,0x1c93a60d5de0136dedae91eb026321c1278f30d5 -13153,0x1d9e33d4003f6c5d5ecc9fa7bfee2d70674f558c -13081,0x1e3ed6df37bf987b5f3962c6f5cacf3aa37358bf -13393,0x1f18df8a80eb9e6d255e8ae5867fb101dfb0172b -13301,0x1ffecc488267a048a98d9a5a29a7485d176e18da -13192,0x205c2c78f49c7cff5f7c8095964d39854fdd2788 -13231,0x20d52d95db2ad174b59b15d95bd9323d8094da65 -13206,0x20fc47f929a0dd8ea697203e84d2bba5bbf41635 -13108,0x2105fcd5a815c629e661ec4c04e949a2074e848a -13272,0x21a4a5c00ab2fd749ebec8282456d93351459f2a -13092,0x21e936e16744ab41fe6dbbddd28cb2175bc3a70b -13164,0x21f7b5bd10e417091debb63b9bda43dac60241cf -13131,0x2249e277e68f3fa8edb07fcfd214788a1b872bed -13381,0x22ae0fe1e175da3a94112b14b6caed37bd6dafe2 -13285,0x22b23fa2bafc12774490d47f1c9f7205d668ad14 -13227,0x2374b6be58fbdf51170ffbd2fc29f4b3a338f378 -13342,0x2469ba5fe8cb1bae385189e337bfc14aacb39662 -13300,0x250e7ab9b5ea0e95bf68f9fbe2f4f9dc5c8af746 -13106,0x25fc51d490deb96e6b87f1934ed01c94be4caac4 -13068,0x266dd2c406db8390b1f8bc81906ddf626bd2d44f -13425,0x26a2c7480cbaee8a644ec7b64fd8e096dbdeebc0 -13414,0x270a49c498f02758800d688a44c690ffd4d60a07 -13119,0x270b22738490a11f08bfc60912b9e659a91f75ee -13109,0x2714fb3edc6c6b53293dc9e0ff747fdb40650f53 -13422,0x283271f69baf4a10b7d5f3dc21491f5d8a7858fa -13055,0x28aa833a78c59a568cafbccb5c86bd6f9e4818fb -13317,0x29b196e03922db2aae851f7f6323dfbffadb6fbe -13431,0x2a3af1d84b40ce74ba5f09a0986f7728813e16c2 -13100,0x2a3c0592dcb58accd346ccee2bb46e3fb744987a -13052,0x2b4bdb23db71c672d7b1b9481093b4053d964662 -13308,0x2b6742d0806e2527b8e608d77b11cdf64470d3e0 -13242,0x2b6a78309ecc78cc019672a5ed55ac3b933608ee -13324,0x2bf36e05e5d3c4ca32f36d5193b2c7a0266703b1 -13183,0x2ca510f0e2961ca0cf382bfc7b48c823d1adf288 -13237,0x2cba7e9e655fe722d73cb8ac96e821ffdeea5eda -13403,0x2e55b6fb1eda360004811f934535bb043756dc8f -13239,0x2fae8c7edd26213ca1a88fc57b65352dbe353698 -13222,0x3020a226c13f3e2ac6804222d9e250609492906f -13111,0x3028a96b55298be190e6e366c4b4d2dde006f1de -13433,0x30e2105949f064c0e7f09e0dd7272147ad6714a7 -13371,0x314f63f366a6cfa9efc054a0bbec2490d033075a -13140,0x31941d7d930827ae86443925d7adc16b1810ffa4 -13397,0x31a738c1bc7269c2cec78fbf738a898c8389c426 -13394,0x323c8b8306d8d10d7fb78151b6d4be6f160f240a -13159,0x32896e4cc9a51f9a2560eeeb3dbb844f53a94126 -13423,0x32c262181ed4da5292045aba8cc9607bac76a04e -13184,0x332a1643d94a98ebeea8a72efc64fb66daa4e427 -13395,0x3465457aee09e5dc7d08037817b814644a89e258 -13133,0x365324e5045df8c886ebe6ad5449f5ceb5881a40 -13201,0x36ba9386b729a3ddba58b3f85eaf5568ecb10ae1 -13165,0x38ce8d1cadb5595a9973a20d7b41da0a9131ed5b -13444,0x3976b7c6bd982ab0e3a0d6ed898932fc81e38bb0 -13408,0x39fc8981d1a98f029dc06cd7502a68f474f59573 -13067,0x3b62099985a6397086addb2e4043268742b07fe7 -13223,0x3b62270513faa6acda890bbed377f437c44cb9a8 -13401,0x3b8ed42db806f3d8dfdc491255ec9b3363d21005 -13389,0x3cbdb9e331b19ba300538c6df9a5338a0b25810a -13233,0x3ce6b32cca0f609f138f069f6e8ccb0e7da61325 -13383,0x3d0b7810830c4be4a425169f28f816c50611c378 -13305,0x3df4169072b4718466aff13ed01a61a07e2743de -13370,0x3e397d1f5e3105bbdc1dd17f31bd808babac529e -13082,0x3e7bf77b12218e5734aadae0caaead1a3599b1e4 -13113,0x3ed64f6c9ffc653f1ccc6ed6c153df72f8bd96b8 -13135,0x3ede1b06d26a0f7c98386b1bd80e23c55aabf0ec -13333,0x403b45da8e88ed1ebd14ecc826e5cb04c9f244ca -13384,0x40ee5ebdaea1ef5ba0becbf0f495287831c3febc -13352,0x4152dd6e717ead2222271867c8c367888fae5ff0 -13278,0x41b4e2c89ff4bdbf408cf04b0becb11969ab4671 -13313,0x4212e30db51616d3a7c9634d76a4ce963b71d037 -13452,0x427f5439051ffc4a1486df9aeefd29c4313d7f46 -13248,0x4382866f9a997f07355f1f601be985a7059ac6a4 -13162,0x464d5d1a8b0046c2636e960f99454ed4cf335ac8 -13426,0x4794c61ee05573017cea43896796ab613be40f35 -13199,0x49822b8945823e705be8b1406503272d97b57066 -13075,0x4a7335947e7877a617201dc25889f3fb9707247f -13105,0x4b6c6829ad4d735ac333ec143c89cd094690647b -13303,0x4c55cf41440741ed6b556257ba14241cd7d9e99b -13072,0x4c7557111afbb68a9ce2cdf28cf733f548fd9de5 -13275,0x4ccc5224ca64e7da923d765238e08bdca8455e1a -13097,0x4cd495ce84200941142dfebc138b139ebaf47edc -13080,0x4dd0420f7a5acfc565a6c55827c5f23e12d818ec -13118,0x4dd64c53814fb0956db266c9e877c0d8bfbd5cf3 -13155,0x4e3ad6266a26c31e9b221d3cf48f1812531095f2 -13086,0x4ecacf5d7f690aba9d5873cb1026a1cb15bc47d5 -13107,0x502ab14762d8ab0d2a901757106cfb8fba650ee1 -13279,0x50f2644d20924103c0fc5bc46386fc08fcc56db3 -13329,0x513d328f04b50a65e59f982db77bf7d7b76e2e0f -13076,0x514c504e4bc7b7eaca251714bc2cb1a60e1e1a1f -13351,0x527effc42fa82f365786ec7e16e663e3c511947c -13412,0x528f7853fb962c4e061a7ecef03d425829e79d3d -13270,0x53918fc21aac6d26b56745f44bbd6cc03d1df8c1 -13045,0x53c8b98de949eef2dfb54352e04216e101009d82 -13176,0x543603c6938f0c0731ace48c5b4b3e4a8eb3a0cb -13355,0x5436184f83fd96689151585f34cf76a2bdd7e01c -13331,0x548cdb848e63980dc0fcf21a5f97816cc2cb4edc -13073,0x549406bc1370a7efe86e2ae71dea3b67fa08b43d -13141,0x549ea2c6f6683f8eecc6c5799744d0ed2d65c5d0 -13340,0x5652655b681599da40d60c7b50e431d6dcba77d1 -13156,0x56b1103a375d6e12be3bd9f23332558f570f7a8b -13424,0x56d830de16c010e90a4b7d008c412110a8e598de -13334,0x573864748fae686fe6567e704cf83eefeee348c4 -13450,0x575420596141388fbce8663bf728ff8485114b93 -13298,0x57624d1d88757bf159b4cd8709ec185d639bf730 -13084,0x57b8e1425b4190d6e1d809e740127a9ed87cb4c4 -13145,0x5826414822a39c96c339be335651e3e243ed12d7 -13335,0x58488bb666d2da33f8e8938dbdd582d2481d4183 -13339,0x5aafbd25f20658ff3da2d54983530778bb9db30c -13325,0x5ae377fec6b1cf6077014244ba7ad01025110c83 -13189,0x5bc52ef6fb0c4c5c064945690c3bd497fb073c54 -13437,0x5cc3044d58f1d4809330d9f03e7bcdcd5510071d -13380,0x5d013e9a9ed598c503ecdd6ec52ed1e91c932288 -13071,0x5e268e07a782b58d0fd131022b0843c9ca0526c7 -13169,0x5f417a5460590f37df878084abb995bfa6fdc380 -13441,0x6051e842965067435831d3259d72229a664a143a -13407,0x615c43239072501029140bca389d5d171e29158b -13078,0x6173bbca90c023a99384dbf73f40101d8ace1bd0 -13294,0x622a3549a3c857095f7172e76a92bd58979c7b76 -13175,0x648dda7779d9e3e655fe7a75ddbdfed4fdfe1935 -13353,0x649a5e005961adcf7cafea57db8aa71a59753058 -13360,0x651b092dd53a22489a2c2bf24233de81e6a0444f -13336,0x666d4e8e17b0b40debcd74364ebc99ae0999dca4 -13208,0x66f63f7db6bce657d1d9d68d7a67255864282b05 -13147,0x6757378c1b15880e52956eeb0d4838e01c4706ec -13284,0x67a9e34a763395266612ffc8c9305eb2fca4d4be -13254,0x67d0fe7dea3a3d509c5c3351e4bddeb73f577c1f -13160,0x67f3f41c76c15b356be619071f59b208d5dee5a9 -13211,0x68a56be3bda7851d16e6e16b621c4677be8af527 -13247,0x69db965bfe90c687b5229f446f420bb09073c344 -13229,0x6acc84bf50ccc5e436a4fc30ca49f9cead368f95 -13255,0x6c42b015b9a078640acc89672518679f46e98bc4 -13210,0x6c440c20074bdf92c3c83e558562910ac645c14a -13090,0x6c4ed8cccd7546d5bc30f468b1451a3936489276 -13151,0x6ca92f61b58151cd75201722daddda113fe0e9a5 -13182,0x6d611029a327f9627c6f026b93ed4a278f609581 -13238,0x6fec08aebdd33f48876d2860642ef4ca9ab060e1 -13060,0x703d07e3a431dc47086851b869c12d0ea2a344d9 -13328,0x708957f05f7547ebaa539a84649d1cd0893a0834 -13451,0x70be4278c15d2fcb4080a40e68f53f2538d7f384 -13434,0x719b833246407501d14760afab7494345246ef26 -13310,0x71b4d60097a7430125b196be4e1b73625bab4171 -13271,0x71be803096c7863dee95eff6646328d5522ecc55 -13349,0x71f25bac9fb59a04bf39830ad2d8d0df9c277605 -13258,0x7204da288b42261bb834df52af756f43c0f51968 -13053,0x723dadfe3da9035653e38842b94a40ff4f4730a0 -13170,0x730506083eb07f26a8816a17f750d1d984c74eef -13350,0x749c9c18e33f56d3cdef9e5c16dad4a5f65cfc3c -13124,0x74e2857b3ca5632e98627ea270e89931c2ecf5c1 -13432,0x75dfac9b01ac55ab6a1a076fd2542bf1df98278f -13442,0x762e7ced394c8a0beadac28c1b0bca30898a0171 -13230,0x776aad6248bf5450b9671cdaab54de7ebd2eb68d -13261,0x77d4320fab5ce9130a096b7c542508aeac828edb -13390,0x78136ef4bdcbdabb8d7aa09a33c3c16ca6381910 -13413,0x783dc9b689c13b69bdad25b49b7032b6502cbedb -13054,0x7884b1ab6f1ce5141134c04393aa4e9031805b13 -13274,0x7902b9ac56ad10d71683324b938a591e012ed4dd -13262,0x795424c9ea9a4b6ce798bdd28f06b3188a18993e -13196,0x7ae43c69959e71207329f834592be6fb851406e0 -13430,0x7ba146a9cd8fc2595a790ebcf0fa5a6f318c4e92 -13260,0x7bbe1342b8a3bf3dfcc645fc89b4b1c71ce2ee80 -13417,0x7c4ace93f0d279f656f9ea4baea1c39940d207ae -13177,0x7c52004195b1d60da62abe6d39a84512ee0ef030 -13048,0x7caa1f37309bbb94a35eb78f515338336ddb0389 -13410,0x7d29bff5b90987314b7ee5026b4d0fac1f993e35 -13226,0x7e1b1279087441112d657465ecbd4feb590a88be -13191,0x7e47f4b446e42b8379207998822095438040fb9f -13130,0x7e6bc08ede45a7f63b393a88222ee50f470493fe -13316,0x7f4de8d4033d531babac1efc754be46ed31dca78 -13098,0x7f7368046ad88d27dc274280ee93d1c2a9e00677 -13286,0x7ff82c1676fb8aea16ddc729d4a885caf57e425a -13263,0x800626769935c27db68f5cfda25d3ad7e8607f27 -13369,0x80590b598999740bb662b494c4b132051bf6e6f8 -13115,0x8123dce565111f64c01864b2ae0f35e3181a0a02 -13203,0x824026ca57bca5c7c0b98f62e5d0173cd1630dad -13347,0x82d25ebfd66d91830d270a95cc3038ee16e8107a -13344,0x83222ce449d4a8b1cff216de293b39226ec04546 -13188,0x8358836d81d1641cf861e74f1143a4a3b8003cd7 -13409,0x835a179a9e1a57f15823efc82bc460eb2d9d2e7c -13171,0x83b5cbe16823138dcca7c7578b57f7da5ced3dae -13163,0x8412582b2ad06a17b4e69f4f11675a37aed04c64 -13348,0x857f05b7031f948fe6f1b60c05e3e0bdf8174c85 -13244,0x85be35d9f396a327e695e2347bd5cb10d63d24c8 -13139,0x85c4ff09a013568264354b81283110ad3621e43b -13178,0x868c37e04462247e67273a4fd5fe80aacb724e91 -13297,0x87459f3bb88577f7f0d4ac589fdfffb07106efc8 -13326,0x875e7a3d81e65fdd72617e3b79e251d9aa2fafae -13421,0x8912d726446aea0279e4bea2f2dba0b1a34cd92e -13282,0x89c71833589bde8705c230f54247c66b1fa320c7 -13128,0x8add31bc901214a37f3bb676cb90ad62b24fd9a5 -13427,0x8cd0a13c65ad7b8c0adf888740c9662484db8193 -13402,0x8d65d220c5a73bf61abf07ea79c6c89aed4208c9 -13362,0x8d86106c614f25daf3b60d83974e131824f6e589 -13376,0x8e67dbe72ee67c4d799c4031c34741841f7ebdfd -13372,0x8fa7e4e2ec4afa86d28a667e7d0d90a2f3d6cd16 -13315,0x8fcb6e31656c4ec19c4a9e0be0b887c70ae60d3b -13311,0x918ed9e3aaf3bb2a39055d3eeb745f4ddf7396e8 -13309,0x93617748ae22f31220b47cd1ea854e38a593befa -13386,0x9376e52cbd7b51dd75a07a57c908f5fcdf778536 -13190,0x939c11c596b851447e5220584d37f12854ba02ae -13341,0x93e4038210acb4706c73a0ceb75286eddd52d844 -13436,0x9507918b0b562fa4b7e0ad59079cbe67a29cd0aa -13345,0x9665041b084b954b56f86d86bc620ec0cdcbd23a -13319,0x9803e4d71444c33607a288d802d717e3f2c3eea9 -13257,0x98692f11c737f93025a663b91068dc8498a30989 -13103,0x986b846834367d68fd6ee7197a1aa638578292e9 -13056,0x98b410d537e49aaf732b335c9a033031c46c1221 -13449,0x9937035eecfaf1e4254317e8fbffa6963b86c0d8 -13220,0x9948f303d6108746d7f653139a89cf1a4d32d304 -13083,0x9a1286cc3e90b3192a532274152315f3fe93dcff -13088,0x9a601c5bb360811d96a23689066af316a30c3027 -13104,0x9b86b2be8edb2958089e522fe0eb7dd5935975ab -13358,0x9ba3f2668027709ebee340aac10e6b6b1fafd076 -13277,0x9bef98489f586c53e01f2f28b27058b3ce415bbb -13318,0x9e67c6e04026a5feb46c9967eb15bb3ba8f32f68 -13142,0xa01e233b0f8d511e4385d09b31ba6994fc2bf3f4 -13361,0xa0be4c09a03f5f91c68f18eac598155812101be3 -13337,0xa10327e3f70fce3cc5500d3011846d2e6adf79c1 -13110,0xa4dc082f61d8d73b51557ce31edaecabb228070c -13283,0xa4dd6839013092e84daa62f14af5961aaa67244a -13154,0xa4fad24f788055a145bdfe0164a23e1c781101e4 -13174,0xa54c435c06f0f4442eeeaaa50a9b0df55f404b20 -13136,0xa693d6dbead9686a393666060aa7f06f44063fe9 -13236,0xa6cac988e3bf78c54f3803b790485eb8df3fbaeb -13406,0xa74d0f3e00ce077b30487c6e4f676c1e6f0ccb6f -13058,0xa78cd820b198a943199deb0506e77d655b5078cc -13398,0xa8a2438b8f20cdded80612343056d65abacf9f02 -13440,0xa8e943e01d2d9b4642280531b8d413936c2a7bb9 -13059,0xa8ed3167748683ae300f98d573950fa2e46ba494 -13312,0xab04fe648aa589d3c177990b04c41b0ed0b6c002 -13099,0xab44013918063e76c9ff3d5f0887350a2b3edfd5 -13306,0xab9c4415c713e7ad1e5193cb40bbbda6aec2cc46 -13172,0xabc2529bec220cb10720c72e95b48912a84e6b94 -13252,0xabf0b54f3d6987292251e6ba967df744058a6e59 -13447,0xabf1efe1602c1ea4e10347ab08fbf28b579d1e30 -13148,0xac0a3c4c9b539bde43704c35e886ef7882eef6ce -13114,0xac6c1bc42c98104e9dd5a8a2400033e7b8388928 -13322,0xacb86addd8e602186e96e3fc55d8b4983d80e314 -13219,0xae2b15391a694a52cd731b236c0ab174f921574c -13299,0xae380625241f103626cabbaff166fc8c9f25936e -13288,0xaee7e6dc3728520292a30a2ed07400c9cf405ae3 -13173,0xaf3ac45b9ec1999773feb96d6ef43880f3c9e8e5 -13240,0xafdaff4f42830112df28b179af508ab9a103bcd7 -13091,0xafef59a11410680b4a4aedf32a88473fded440e1 -13453,0xb01c13726d2dc1e916520e3802c381026b4f2d14 -13251,0xb04a6496a4a7efd84564fd2dabe7756d10b46a2f -13194,0xb101015ceecd11d8f5bf57be8604365cfc889f79 -13375,0xb122d68ff9b3fde5f86d328f2cca3d11ba0aa849 -13266,0xb210f369d87ba88e49f10cfd90cbb610f64b9b62 -13127,0xb212c6cc5dc3d40b4224eb58d5160458124d7bcd -13129,0xb22a50859a8101724dcef7aa3ab68479e44351c3 -13044,0xb2694b75ef6da6bbbf1c365355679388a27c43f2 -13149,0xb2c231bfe8b338644ab7dfa16be34b62fcd14f99 -13213,0xb4b4e337345a23ce8b14960e2783fd9c4fec2717 -13416,0xb4fe9f6b83fddf7a9c0f75c86bc1b6cf56f6b8de -13225,0xb52858d2fa670ebfea5e2a119f01a87f29a0e8a7 -13446,0xb56568f5d2472f98c78e52cfa9f7137534bc164a -13241,0xb5cd27bfb3aa9d80ef57328865463d9c37672d18 -13112,0xb67c152e69217b5acb85a2e19df13423351b0e27 -13448,0xb6fd7c0e6c7189beebe5aeff13c43d303a2379d1 -13120,0xb7a6d2875b0b289f82dbf675ece93ca25c332449 -13087,0xb7c159b9ef3e397307e739580cbbb8754fb2447f -13418,0xb7d509ce2bda849b05eefef550bd94ffe31a8ec5 -13405,0xb8455dff628dd9ce58eda28032a6374594615f88 -13396,0xb88c2541da2c47b0c131e35e2a76440b41b36dce -13296,0xb980adb094088e5b08d7a10f6550c7c4f5946f90 -13400,0xba67900a6f91aca8f35b572b98d176b83c320d01 -13070,0xba80753ca942b67e3bd8a2efe8b785e06c45162c -13221,0xbb198ffd0e1490ca108ab6c409473408230d261b -13185,0xbb6b0f841007c5bb21cf3e9f1ef01a8d06a4c4c7 -13134,0xbbcba81128a1639f2a3afbdd3a90249454f00d03 -13379,0xbc1004212a059a2114bf8a97e5ac726cd7a0f7ab -13327,0xbc3ee2388a061b89672ceb1abcd498f31cb85ad6 -13179,0xbca22c04a1ad859c4bc717db0c69f341a538c7f2 -13122,0xbce11d81191e95f936e1dbf7b14e37641a5e82d9 -13378,0xbddfc30b212fae5770c92b22194c35cb6a8ad7ba -13268,0xbe53a21fbacb2f1432af135c96d3fee4c4be44f8 -13399,0xbe765ee11c3d70e47c19f82daa0828d5491d7336 -13245,0xbf991bfc8eeae7f0d6069e941fb2ee32bcfd202a -13419,0xbfb5903f856403f0aa5b63fc0793b8b7996ffb7c -13062,0xc190ddc79226fff61cb07f4f85d891ca22593bb3 -13064,0xc2308cf21ef8a4faf71b4060872cb33000dd59c8 -13385,0xc37277937ff6bffffe3da85eb03d1bfc6b646953 -13235,0xc37e1fb3d127d6194fc2f5fe18ec871c7366df63 -13307,0xc4100e6ee15e6dbe4ed733da4d489c2b451713c3 -13415,0xc46faced04391a40f719a5aada14c1c3703cd807 -13377,0xc4804658e193651dd8f87d1bccb8939eb63475d4 -13209,0xc4eae4a3c6f611be055f26c1e093b77dea1d6014 -13382,0xc61030609aa87223c9acae126afbe36303dc67af -13281,0xc67ce28d0343e5972f3b1711c32e206ab5a238d6 -13143,0xc701a96db31d1ac2c8c19731e2d22404a40479f7 -13074,0xc71ce791233ca567de468384ce1a1145fec4acbb -13117,0xc8440514cefd152302b75345d1891380c1b20be2 -13454,0xc88f826d833319201bd5529522f272b26f0ca47f -13429,0xc8e617e5cf23fd9213cdfc1e1e5f6bba81745a0e -13304,0xc9b595e7da37ddecb9f0f7ecf1ffd6135033939f -13085,0xcae484ae944d88de22f04e0775c25452f4804165 -13259,0xcaec8e3670aa47e601c2968a5e33e7d88910e5ee -13046,0xcb2d8d262f88fe43941295fd57a4617c191466ea -13364,0xcb650262a30c36b3e515b5a122b6f361aecb452e -13392,0xccb94ecc64f1d8fb671c2d7e8b5f2e005c22d3ca -13267,0xce54e3c7f003429c2cb51b00cd0774097da9ec0d -13186,0xce6f28ae1cf66592799cf8fca9106ac0ebd8a569 -13152,0xce90fb6589d74b4480d09064ef332a6944e082df -13330,0xd026c3c1225aeeb905ae57f8663bf06e744f6873 -13387,0xd15887992741442a667ba019c6321c73c063ee39 -13292,0xd1fb7706eb0b8c9dd09567cde90a6077f2fce553 -13368,0xd26404289530b1848b8fc546c6fd70d2ebb090b3 -13280,0xd3e9b37ef2e749f9cb1e92655c9abd5b40667ee7 -13202,0xd42e39d93ea63b12831259b43437a549991cd3f2 -13367,0xd46f6b5851b4ba2f6aec893040b94051350f4771 -13291,0xd4bcfccc277c0bceb2681c6d88d3037f8647ecdd -13126,0xd5a8f233cbddb40368d55c3320644fb36e597002 -13455,0xd61d11dd34f5b18f4d371c6e1a0e7260254d50a8 -13200,0xd6721d71e24717aff2b2ddc80c1a6d42e8d4ae18 -13077,0xd6bb261d5dbf9d7cdef06c47547d251dcf11e6e5 -13456,0xd733c50ee90db115de56a6fe7b07d3b37fc6543c -13374,0xd851c6cba7e3438f6d548851b5c3c254fe68d2c0 -13346,0xd85379e2fa8876777f85e9fe2e4a787e4c9f197d -13250,0xd8c5b5fe1b951bd582c1c6d9c6bbae0d8f51a051 -13218,0xd916d839f71c0d6d21d61fa42ee90f53b64f80c5 -13428,0xd9a00917b4d61e17a0e5d62ffb747fbbe1efc50f -13137,0xdac57040b15e2c0853e2c8bf89ba373f2324d208 -13435,0xdac67bee072ca679f16b2472fd5a22428b6b1e55 -13214,0xdb4174e1a4005a30f5a0924f43c8dfcb8cbd828a -13216,0xdbdf58cba7f978f8745faf8ec45597d1ffef3479 -13276,0xdc839cc20e0daee5352a6c0d1aab4015786cd12a -13050,0xdcb08fafecf5bd0187f76287ff6b554984329e21 -13295,0xdde437813b789be322ff12955be5978f636e0d44 -13065,0xdf89a320d8834f95f0a95882db83fcd9f3b275a1 -13063,0xe07db55fd8c920c0272cea3dda6a9e6e3904b2d6 -13049,0xe108212c75920b728658785856c4caec6008a03e -13404,0xe1714955ebfcc5da00d849d7791c5eeee82d8c9b -13094,0xe2152f0c927b947c1b2794b5d2764df4643cc0cf -13356,0xe32cacd7447b81c9ba0e2cd0c89445d78cffb86d -13323,0xe3451b170806aab3e24b5cd03a331c1ccdb4d7c1 -13445,0xe3f934e7757ba6553b7f349a37d184af58838e99 -13438,0xe5b41b6c0539240a605d8d088af779cde33c5db6 -13047,0xe66d357a3428f7b868f002210eaff9fd0acaff6d -13095,0xe7c1cf3ce3b5282e82af5caf81b6d7c2978ab25e -13215,0xe939df43df4099642d53dee1525a0ddb7c7776a9 -13420,0xe979f04f24eb0c6ffc077742ce6fb75b8da8d0c7 -13167,0xe9ae0110044bef983a44237bcd9d7255f1ac1a9f -13061,0xea3d3025a71203fe53b66edfc7d30db6b17a28cc -13290,0xeb064629c1454ff3f18dba99c713a7266a30cec2 -13093,0xeb66f5e821f1820d40a1526ea0e229103de8813b -13443,0xebcb3e7851a666aa1cb474dbc78cf8a2a62b79a0 -13302,0xed57fb46927b158f49c1eed17da700b6d67dee21 -13439,0xedfd139ed507663811e7b98a8456950d0e85e70d -13157,0xee964ba8364603eede6c297fd43a7db4a3f48c3d -13197,0xef2dd75846741d19d71b365ff6e75684168c2ce4 -13146,0xef5855aa7a6e6035a3e39fee0ed1397c4e0799aa -13144,0xf07108249edd2f59abd1d091a0778d58ecedbc49 -13457,0xf0f7b9d3d04ad158676292e372f0c56a47f6b0e7 -13168,0xf11fc87bf8b13e13e64c7cb600886f85d26f4a55 -13101,0xf1a19a0b7a31bcc1b6cbbea132d8bf5370044cf6 -13265,0xf201ce7490cd14993b232258ad9dbc5dff41fdbe -13123,0xf3ea7b1f45e2c5ae76b5dc80cb06a97830f60774 -13256,0xf5b9474f7370521031b553b51e761f80cbe3ac22 -13166,0xf61b1cbdc381cb1ad95c04f22b3fbb2ae2296be0 -13187,0xf6bf9ea4d660c24f62223dc37bdd194f68bbbbeb -13132,0xf6f64b53c44450c8ac2d69b2113e2e52d3ae7912 -13366,0xf795890a2523ac59462571232511cd3f60176bbe -13359,0xf9135e7da8d14220528378f5cde936ff7ee45e34 -13096,0xf97b94d0eeccc2600aab25d6b2ba5bbd0d9fa600 -13232,0xf9b19d0e62278ec9cbbad5cca5e7a270979bea4e -13246,0xf9cc04fc4086c0e1e9de4d8c43655694d3c4494e -13354,0xfb427bf5da7eb547eb7f9309d38dd8d4c4aeee97 -13338,0xfb64c04ff2dd5e0297b27b2bf286ad29051cf07f -13365,0xfbdcc238789e2616f117202370410a08f5f3ab03 -13089,0xfbee678d5deac8a1d528514eb60d225d6456140a -13224,0xfbf4a17396cf52c42d8ab024c416ee5b0b456811 -13158,0xfbf70677371fd1f39d578942e1a18f34bf77ec28 -13269,0xfcc293db3b7396a1c2477c9f24f5f948431ef6ec -13243,0xfd1d31201ce72c054db0f560a672049f1bc1ff96 -13138,0xfd407e5cf2d8651c0a05b75577e18d07166bb2da -13234,0xfd9d52762c47a173182d26090c8c6c1bc3d278ca -13253,0xfdc2b50ab6b4afc0a3860b8b0bad4b8fdbe42427 -13411,0xfe39d31be143515079119449e4117bfe55c8be55 -13161,0xfebe2216be8c5d7c5c9407622229cd8627fd5ecb -13287,0xfefd4d591e28a4ed28be1811e957e9b0deebf8a7 -13180,0xff90bb6a8034c0483beafa76ef207ec70fc528d2 -13314,0xff9e26b804ebc60deca5a019542815aac028a22b -13516,0x082be61153b0af71e50f07ac0be5f176656d5ba8 -13573,0x0a04d1ef4d0658142097b68cee33b72a9a768e2a -13538,0x0bea755c9ecfffe1d701c092d6dd2a12bb17ee03 -13501,0x0c5b4c92c948691eebf185c17eeb9c230dc019e9 -13491,0x12e6749c4320d6f9f583646374f2763cb87c0bb0 -13489,0x143bf0f2bf1632eeee03dd3a0eeb5bdaa59f884e -13577,0x1570b5d17a0796112263f4e3faeee53459b41a49 -13497,0x1634e17813d54ffc7506523d6e8bf08556207468 -13574,0x1ac33f8eee3bd06481abf286cdbd7bbfde5681f2 -13522,0x1bb40496d3074a2345d5e3ac28b990854a7bde34 -13572,0x1d9073254e23d90287110428ef2acb392a55c303 -13503,0x2292077247e15f28dddec94c9ba9b4875990b8a8 -13510,0x24f8b36b7349053a33e3767bc44b8ff20813ae5e -13537,0x27811a7d034e5eab0f25e5cef390418aae51a0d2 -13519,0x29020d37f1d231afdbbb476ad01b601f16da6c2c -13554,0x2dd866132bc31c5bd40b7ae381c67dbf7a74e56b -13486,0x30ad09d5ba07dc9edd793f71b9d4cf0c91ee9194 -13549,0x31668821de523dc46de6c6653a76fb3582b68e8c -13498,0x317d6ea8a81b3dbe21e0d295498707746a792e4d -13534,0x3360a6614a06a40153c63d807fe38d36908d45c2 -13500,0x359d3043973150f0295243c83cd6c6cce6c1440a -13555,0x37cc6ce6eda683ab97433f4bf26babd63889df23 -13502,0x387c985176a314c9e5d927a99724de98576812af -13541,0x3ba3f80f0c689f73e7606b2a49806eae72dfd5f7 -13508,0x3c7825811a2426491356cadce143bc67db071e40 -13543,0x48394297ed0a9e9edcc556faaf4222a932605c56 -13540,0x49da51435329847b369829873b04b537d2dac302 -13542,0x4c29bcc6b037430ac1e230077711d8f8b0bdf8ca -13507,0x4d78414fdb72279c8c5b5b03a31f50b1b2912da8 -13523,0x4ef90593fe2b0e1858231a949b9ccc0103a6cbe6 -13485,0x4ffe019c104a7f8f6f58014d05ce5d4cb22968b8 -13539,0x5558396c859d3f2ac9e180bac31f55ce8b7e54c1 -13512,0x59b903bcdc3e1b96619fb3f79b602da95c5a52f6 -13521,0x59fb62d4a15707bbf55cc1501f2985b0e7c62c40 -13506,0x5a34299c61a3ef4a559859fb389198c69c989665 -13518,0x5be52adf0c54598f0fa6f433b398492748ecd588 -13544,0x62b007aab4075eac2608f04f6ac9943193a4a7d9 -13561,0x637bbfa0ba3de1341c469b15986d4aae2c8d3ce5 -13504,0x64ed97052b6a6d42d746a84087cb26311ad4f624 -13553,0x668d30c30ffb182964516f549528e06633b48295 -13529,0x69ab6153b32775fd6068a958d8c8dda1c7b8eb06 -13493,0x704616361bd379b67af5d00ea8d0fe16c311b7c7 -13562,0x7446bf003b98b7b0d90ce84810ac12d6b8114b62 -13548,0x7513a8878afcce59021fc6ae62c1778c80d63ee5 -13567,0x754ece9ac6b3ff9acc311261ec82bd1b69b8e00b -13526,0x7553b5ecfa93aea9a5f661c13fb8e7ef53089dad -13568,0x76ffb63845a65232c1f8a32aab80e8702c121302 -13499,0x78227524c7c13b78d04b8edaba192a16ef5eb029 -13488,0x849c283375a156a6632e8ee928308fcb61306b7b -13533,0x86d36e2c6c3d7eb7a740cf136d37a9a0edb4fa8d -13556,0x8cc2f2af67beaf595eb666356867abbdf407d45d -13532,0x907393df1e221e16e462b98f1d6093836935775a -13511,0x90cf236c4104020d493a8e919c42794671b7ac13 -13536,0x91907f5268f3485bfee913f55717ef9187a465a2 -13494,0x9dc23189bc539b36cd51d94817880e33d92aa0bf -13520,0x9fc985c7fa20141ab70efd04af3827aa7c60d7e5 -13576,0xa0eb2b679d06925078ee545ff6e8960496303e4c -13487,0xa1d43d97fc5f1026597c67805aa02aae558e0fef -13528,0xa1f13ccc3205f767cea4f254bb1a2b53933798b2 -13565,0xa526bb15be45ce698d04f3b564a5009a69be34be -13530,0xa58d59a5d96c8411263d891cbae870546cfe6587 -13515,0xa936511d24f9488db343afddccbf78ad28bd3f42 -13566,0xa99e8a5754a53be312fba259c7c4619cfb00e849 -13551,0xaa50bcf5a83609e17dfb4cbe6ef1893ba25907f0 -13558,0xae2a28b97fff55ca62881cbb30de0a3d9949f234 -13490,0xb091a25d00e9782ced5a18420ba3fe4a88adc1fd -13545,0xbbf8233867c1982d66ea920d726d24391b713550 -13563,0xbd469fdedd9a8e62bcb1aa1e479a8a287e5718e0 -13535,0xbe27c2415497f8ae5e6103044f460991e32636f8 -13527,0xc1ccc9762414fcea62d6f918990c18b15f23555e -13571,0xc2d0035298ab7411f033ccf20599a703cad013bc -13547,0xc335740c951f45200b38c5ca84f0a9663b51aec6 -13509,0xc7b58fa7bb3ac9cf68163d0e6e0d533f441cb921 -13496,0xcaae855034d402eda989189d7e2d67bb29207807 -13552,0xcade0c1c4c0c25cfdf62c26bb1a0d749d094f58a -13550,0xcca0596158cdce209d12a2163dce49810b4548b9 -13575,0xcdd0816c4e39ca9abfcb739a9fc3ca267f5ab502 -13483,0xce9075ab4158aa05447df12e5bf09d51e21516e9 -13495,0xd0a77bd4daf7d65e9c4967dd35fd2dd3a333e621 -13492,0xd5c959fdeca82a38a94e16cb565e54ab8e32c4a9 -13557,0xd60514536195573ce4a4a78ce5706e94e9ee7917 -13560,0xdc954e7399e9ada2661cdddb8d4c19c19e070a8e -13513,0xe039f8102319af854fe11489a19d6b5d2799ada7 -13546,0xe40be09cbf2ec3b3b437d902bdc5504788b17d68 -13564,0xe5eee84f2accb1fcad55e98948ad1ca41336d647 -13514,0xe8a90fb61bd8b219bf71447d5ece387fe1e81c68 -13517,0xe9936818ecd2a6930407a11c090260b5390a954d -13569,0xeb48138a30c80af6050a2ae53230936c69973aea -13531,0xec50bbc4ab56a1a85b279d35b0c4c77b94af4d94 -13559,0xeda1f6c87dccaf65bb5027e29b86f1db303906db -13525,0xeedef926d3d7c9628c8620b5a018c102f413cdb7 -13505,0xefcea4baf9d018b3c2a629a3b28fa29df24bb30f -13524,0xf279f383622f2a9904a51cfb7a7f4d1b43d6f44b -13484,0xf66b6aea7a1eb478f538b1ea21ff4b21045675a2 -13570,0xfc9e707bbf7dc954ee7c1a42fad987b6c80247df -13740,0x00a0b6d11ca762fce125597a52135f2b007d19dd -14120,0x00cea6cd9fdd2acdcde88c5a7a5df87688725ecd -14388,0x00e198d71fb701f58b5d5c31759efcc911ebc960 -13797,0x00f0381b4509fee5fe51ecc0b241f67972cf0729 -14161,0x015f7ea8fd10d050feb2905b0b2d7908572cae01 -14335,0x016becff054ffd6f16727e6070f4a5a1127053a1 -13698,0x02ec0d78a6e981dde14efef0792774fd236645a7 -14570,0x0315b83b137ef6d1bbc6a29ae5c524666e030c75 -14579,0x0315ffdef8efc88d193f79ff52e04474f3d81954 -13693,0x032ed2fc1c42c47faafe6bbf54899a8ba1b719ec -14356,0x03ca5e832015ca2568ab7a5b118463d4abbba0c0 -14114,0x03ca92d5e04162e54d6d591cd334c7de7864800d -13933,0x044e6f675e092ccbd5c47e8c4f7b6ecc98d894a2 -14111,0x048f5bcb3e792b4b473a01d951811efacedf2bb1 -13928,0x04b0db8f38c90bc8e51a4fa40022de34652f50d3 -14151,0x050d343edc93526dc8f9e542f6936a5c12dcc83f -13825,0x05333977d60f3b687576493f825915407d3febfa -13676,0x0569274a0f32a1bf9438b07d47011594ef6d1eb4 -14009,0x0578cb47a035bb80dc0881ba12b8b688f4b0d1fd -14328,0x0593db04e076817c045287fa56123f6df586190f -13741,0x05a60aac6b2a2101e35f834dff6b3c4ec7ad3827 -14280,0x0628b64512ab0ee57c18419c5d34176b284c49d2 -14601,0x065047856f2ca23d80b6068f29be56958698f965 -13876,0x065ea8b912da5d5291a740b60bdb18d36261a43a -14199,0x065ffc903474eec7120591855cf4ce183742b159 -14631,0x06e1c95c83768de7ccc30debb84c08b596305563 -14486,0x06e4c8177846f588b58f0baa820ecd1a830293cb -13765,0x06e4c910d8abe386542e0b22eb08a30206a12833 -14045,0x07fa48193a388099c93fe93f7ee96df824b2597d -14578,0x0816332dbb603fbc82b386ef8fdc576605034ebf -13672,0x0824b0e8759aa958e7dbfb338de779f698a3e0b2 -13834,0x082828a68acf2723b927018bba5660eb961fe832 -14583,0x085450f6ead075b91f5b4c214af1830acaaaef47 -14459,0x08b719cdc57d7e07f4dab7a6e97bba3bc90fc326 -14444,0x0908c4646a8659ece6b3d6632435518d22cc7cc7 -13923,0x099a9a67f5a79c63d66789a985ce271b525a5eb9 -13955,0x099b59d7ac07b765e3de0ee16dfce846a0baa61b -14322,0x09b355767a3cc98ae9d0be7870678265f1f489e7 -14433,0x09e044cbb6836d08079e48fc943a18cc84c4fc88 -14346,0x0a03498ec169247f81761d9b67bf5b206ff0c0fc -14237,0x0a3e5bf26744a7cbd2228090ee475983043e277a -14144,0x0aa7e7d11138adec68bbe43913e521b3080d42f3 -14441,0x0b02b24d2f96983d7d73180e0389b9c1ea3cd9c3 -14215,0x0b6535cbc47ed5d642383f926f521d3e32e12bd8 -14135,0x0b98bf7a2b32606be08bd68dd3520773d22c260b -14469,0x0c25f10ea076de9c4a0618ab7b020017e1ebe985 -14025,0x0ca14d901ffe8461b8196e593083d004993916b8 -13918,0x0d1b56799d7fc81ef6deaa55511c34b9133262e0 -13769,0x0d1f17edec5788bd0a19d661423a6cc2abef86c6 -14014,0x0d344c79d6e7b584b39216128dd04149a461a863 -14035,0x0dbb5acdd171d1e2046dc644c75f30bef9cdbd9f -13934,0x0e5a2456934bfa0e51d3eca870a9c4b38aeee2bd -14190,0x0e6d7a9f9e7704357b8ec797cbe8c208ef4613fb -13917,0x0f0890e41daff66b518bb25dcd98b37acc839874 -14301,0x0f0da8980173142381cb9b492e153c0af276fbaf -14032,0x0f474fe6c39ff7af82a8d3f6acded09587e0402c -13892,0x0f9fd1839c3792d6308ca2a5d6cb0bf8dbb69ac8 -14085,0x0fe5ee407534611881ec6d7957c9ead11a851195 -14277,0x10aecd10e1e054810f3d1839a56253c41610707c -14259,0x110efe74f9d619aae1949c252f17cf21d82291df -14596,0x1138639d5d2cc47ae03f0aabbd840921b641330b -14647,0x116fc46c048ec547a960a0823b3c42fcccf17dff -14611,0x12341524a1860aa56ff43c066d52aac4ac6eecfc -13970,0x125a8ae230a4cd602aa8079ffdb48e94c311fba5 -14226,0x125e0cfb77ca7b369ee773c507cf59c019e6c6ee -14180,0x1291e6f9a3148e2b7cd6268aad956b4db4b9c4d4 -13712,0x129f21c9bc7f348738dd6cf8e3baeaa12eac78d4 -14568,0x12a5ddaabaa461c3a8d4f32f0cba2b854324f108 -14082,0x12c884f45062b58e1592d1438542731829790a25 -13686,0x12f24251c01f39f4fb81ab0eb7cda542f9f40b35 -13809,0x13cce51659f08ef95de1596b4a73f48109d5bd23 -14000,0x13d17a3520b6f38d9f4fdee7d0398899846c8e3a -14192,0x142408558527b1a63b736252472e35e1110829cc -13860,0x145650f7aed8b34a7d21598cc8fde0a95983f669 -14613,0x146372657bbeb7bbfd95543a33a4041099ea3fe3 -14172,0x151bb01c79f4516c233948d69dae39869bccb737 -14604,0x156e878c09e480e86d8c72a8484fea1d2066e5d4 -14289,0x15cc2c5279d85feba048c19c55ac2f136fd85a02 -14042,0x1630d78623936ed6fe782d88ca55d268a5cf0732 -14431,0x1635c48cbe5d5a927ef2d0283cc0895a9b6e997a -14246,0x16511488eb8e0e57156aba64c0f82809a08b4e59 -13820,0x16af14701b86a41e5cfd4c8c5d647e7c28e85534 -14136,0x1734436f851cc3e3384c55527b692b93da19cc72 -14183,0x174f29b4b12c74344c0d4b3011490023f148b22f -14090,0x176fecd21a04be65363e7a2a3401a0abab3ba1a9 -14409,0x17793262e3625b1a57fb325b5a1f79b05de30b14 -14228,0x185516de54f4fac8ceb272ad785f982bcab5a258 -14227,0x18723a91dedd393f7d1ba09c750629942e9741ff -13685,0x19188bc00a3666bedc618f02f7d6ce48b67b3df4 -14304,0x1942eba8d0f49d7e9687b40702c4e08a2abab541 -14302,0x19613dddf5317aff9397a9287120ce7302a65151 -13680,0x1990446cc2fe92b097e837985741404920a29751 -13982,0x199093f69e98150ada8bc9bc8fa02872fca7cc13 -14465,0x19a4dea5470b2d8e16d9f8d929c0b36fe44113b4 -14581,0x19b618811e0e0c92c9e71290c48ac9f1fe0222ef -14271,0x19ba1a78aee6f2c13204076dbb746cab36cd4410 -14012,0x19ccc0c380f164b6d09313797d01d06dfd4e8598 -14270,0x1a3696a95b7fa93379e9b2b285a9ab427e03cb31 -14493,0x1a459f2923212b9df6419fb8cede30445b26643f -14208,0x1b04224e24f5c69473a57ada876da94f2cf52271 -14384,0x1b19b99ef3923c48acab66206b337b0a974c89c3 -13973,0x1b6d49be8a5b2e21ba0dabf0a0ee9223c23a6e65 -14023,0x1bad546abc62e9a1130228d22a9c923b4f3e6de5 -14575,0x1c9a192df3936cbf093d8afdc352718bcf834eb6 -13727,0x1cb0009757ba38ecc15dbc7f607b293b2c488c6b -13840,0x1d60631e01a19b211301d6d70a403d208bcf58a7 -13988,0x1d72ace2078033ad827eec9723fb738c97b7ef84 -13798,0x1d7df7366e8dd463c6eb939cf1654e5e540a18fa -14411,0x1db8eef23a443cee7b3f947669d89577610cc5eb -14098,0x1dde11b421bced5a9452ee5b84fe39dd0a78e67f -13959,0x1dea4192e1e2bb72bfa16a994256ddd7a44c7317 -13738,0x1e34e2114b2a51f29653f9c6f994135a69f7bc3e -14293,0x1e4e85ce3a28e65d0b4cf75ed35fab10521e6792 -14147,0x1ef6daa02e022b8d6648034df9f4904136c4e31c -14028,0x1f4f6a97bc58a323a66531696d72718d818ee5bc -13866,0x1f51a3bfacbcf95e5fe36fdcbfe2b48f9ef38338 -13992,0x2025c0525f450c14114db683ea8789b1dd5a6c90 -14143,0x202932a31f96a6359bceeac630e5996f3eb64477 -14348,0x20a595f570590d92bbf03f8d230e4153c2c45e4a -14567,0x20c889271ae8198074242740ca736936b72afd52 -13905,0x20ee8f8ec44eec0aa4c559e49880ec6fbe835bae -13701,0x210863729508500ae4b4f6cdcd0c083ae6dd8b94 -14620,0x211a3f48fa48043ce7b09eb4b5fa83a71ddce99b -13940,0x217e333a0c920b8410bd2f21674a7f4f0462bbb6 -14423,0x21b6558a58a8d89a789d9eae7d331ba9ac98c1ed -13666,0x21f0a827943c22f79f87ef612a98829ec5f86d7f -14263,0x222d45306af217850d0d5341756bed361ab72023 -14031,0x225eee739cb3d95a94c73efdc994710e7324df69 -13722,0x228ec1d32567678d5a0fce16cce2a1804743391f -14345,0x2331796ccefb475ce9d37d89c6aa544401b8099f -14329,0x235eb8f89c8bed05ed06d1eeb2d5ecc0aa233832 -13705,0x239240e8cb50114b097c71819c56a3a4ea91d516 -14168,0x239891d6c6aaed08cf49bbe3dfe3fcf11af7d2e5 -14520,0x23a52305725ce9589171e5f782830ec6f0517239 -13732,0x23bf79a3ebae5dd30ab919ff853f795de023beed -13995,0x23e440a6a792d3161e963b9ff6bdaa005c06ca03 -13879,0x23f62e5744556607e06c59a93edc375f61e19bf2 -14387,0x24bc4e048ec704ecc372326275df061069bc7efe -14241,0x24f9c3078c3c832173de30e2ea71e386a59f7e91 -14133,0x2525d19636bba311025f87296afb5c10eb369d29 -13920,0x254257f1f2cd142471e7bac4f89330922131766b -14292,0x25637d5e528566d597c234c279f21f1e9e689059 -14628,0x2568b87f26d6719feba6b43444e2fa5e984957fc -14087,0x26a7083953cbc7080cf89733c410ee4f6ca61c0b -14242,0x26a74a9c08c94d176992312a1f3fa69601be4076 -13990,0x2720ca57503bfdbba6e197c8bbfaa0ec9602317a -14485,0x276180642c6a7ebd043b089dc143b575b58ad9a5 -14273,0x2763c70bf29326d728528623d90cb04eee579866 -14007,0x28bb48207c761eed2a4aa9249083c429c719aadb -14201,0x28d8a1a6bdeaf9d42da6a55da8a34710e3434b97 -14491,0x28f1e979ac21b80543adc7c70f15cf0dbaf340e0 -14573,0x29ddfed42698b467223c600d0e3751f1463f2672 -14552,0x2a0727d1162d83c8d808ca3016cfc83bcae1bf87 -14037,0x2a1319e7d53f787de3938f8341e1805479d487b0 -14438,0x2ac50992168d956b4e0ee5dc09c22def5a5fc382 -14546,0x2af1730f825b511d18d5443d4618ed6ebf6f6b4f -14406,0x2b18962f8d636b395cd145722ddad6b78fb08784 -14544,0x2b781009f25e540c899fed72346633eecf1692da -14288,0x2bf384e025f4b749713bd54649cac65fbba932bc -14231,0x2bfc897811d1220420e6e940247d6e8f194bec6b -13681,0x2c5c54ae1d7e2f5eb24012a5b1db46d0190c9044 -13931,0x2c867b5097eef11fa77ba28ede1c09c24bd11eca -14518,0x2c8adf9770dc04de18a75f8868782598f4783656 -14145,0x2cdc982efe12e16b0af6fc2b929594a7b8012fbc -13737,0x2d0e51c782d609854177c6a61aacb25f329a2f16 -13700,0x2d107fa6a4ea70be788da482be502e759646897e -14315,0x2d43a9ed77591b16b198e569c426aff89b02f1c1 -13767,0x2d45f26cf60fe1c8a4e683c169529380f39efcb8 -14380,0x2db8d2db86ca3a4c7040e778244451776570359b -14058,0x2de8e18bdaef25c2de0bed29c8b72e49261ca88d -13687,0x2e3079a976d38d5930f2ef18235e5dde54df16d6 -13778,0x2e31ee4cbb912024c6e7e4e8e4e68b33833cfec2 -14033,0x2ea4911c8f4a129a9b152be15e472e08ef33e408 -13806,0x2f038c5ed9252519ae0e2cc0812992771c8a9d8c -13906,0x2f1738126fc7a72a556b6e00b5895da126598ac7 -13899,0x2f198182ec54469195a4a06262a9431a42462373 -14617,0x2f6955ef3038c71eff06fa97c714e21620b8c43b -13948,0x2f821c990e23410fe07f2c228cbb6f2bd7701f08 -14391,0x2f8508fc4bfe338b944a9b93b05a9b9725590023 -14566,0x2fb339ef3661e15ba4f682a2be766e6dbf39f9f2 -14267,0x2fd423b3e151b4c39b5fbbfc849941eed1dfc4d0 -13771,0x3033203533a544a0a62aff8e46dc01651b64a871 -13814,0x30804ac0cc389f3b592d8c1613ea0718eaf469f0 -14450,0x31edda4d02d2b85f8e2236fed5f1cdabbe399fbd -14287,0x322551b09823acdc1f8c782a97cfd17baab55b40 -14076,0x3230cbb08c64d0804be5b7f4ce43834291490a91 -14610,0x32bd830aab3bcae5422e241a04a72b417e75e397 -14205,0x32d633c7ab017942732ae5fc7c79a235b7f65570 -14097,0x32e3278db40d6fc7b0761621d6fafde03b045113 -13915,0x3319d55bd580f52c0a47c6769b743cbc41f87317 -14285,0x33348031976643f159b63d49ccb18105026d79a5 -14310,0x333b1ea429a88d0dd48ce7c06c16609cd76f43a8 -13901,0x3380cf565b29b367d6f78e79a6c6bb362ec256ed -13857,0x339be9d488e05afcc6ae2a80225021d051d234f9 -14070,0x341caec1a141032781838fca316d42ba21784835 -13733,0x34235c8489b06482a99bb7fcab6d7c467b92d248 -14599,0x344b82a011803819ca794b7ba5d9e476d274b1cc -14624,0x34d0c67bb5627660fe09ef869b53cf67a6c6b6c7 -13987,0x34d15c9e1b0b7551c33b083251dc95598683602d -14531,0x3510a980eaf710f843a6d15dae569e89f67a5643 -14093,0x355615cee1883986de3567ffea289dd4b7a10405 -14612,0x35af19826c79c21f54612d1913f3f471dbad44e0 -13898,0x35b2e2365ddee06e13b577e10a88097628e1bd65 -13862,0x362181371b9b832d8f71d360bb41b13dab3ea330 -14211,0x36218a5ca404b9bf69b2f7ea28e41cb3027888e2 -13720,0x371ac283b53f19a24598fc7e0b5c6406ae5e0927 -13684,0x372a8ab3e03a197f09dcc64bfdeabf0d43d98c22 -14269,0x3736da10ae5a338f9d5d419d862a8af8b043eb6c -14381,0x37830e3d891572ab9a50bb06641ef757b6d745ac -13938,0x37b5c40b14e0b81adba533507c43946c09b34252 -14519,0x37c90107cce4567c691308944c8a1ea7c3e3e63b -14332,0x37ec0661c1d25948d6a145ed6c40ed796960e922 -13803,0x3891d3f32bfde0245e3e3618f12fc5e9e535de0f -13663,0x38b2535b4d6d395d505aeac63200ad8783a08a3b -13816,0x397925c8f511a4a1f8877e198003b0b90174796b -14539,0x39a3f08bdcbf2e8465fac953a582acb19a6aca58 -14223,0x39cd9443f8b920ea52634bfddabf475762524592 -14410,0x39e64cd778b02b9929bd15710e57425d142440ef -13793,0x3a11d9b7778dc5d5e5e9b6562cbe714faaace3e0 -13837,0x3a29ed47ae5957ac377b606c7b6407911ccf4e63 -13960,0x3a2ce0e94a14d3f160e97f26956d94344f526ca4 -13762,0x3a6be8d7bbc965073c4f7ac882ab479e120e143c -14137,0x3a976c5096179027a4b1ad95a96a8e858bfa6f48 -14179,0x3aa504c41ec1d6d7bd3aad3e94c6829b02bf9852 -14621,0x3ab4248e47494bd832f125397aa7ec644a44b012 -13764,0x3ae1946546b77c730c8ccb12ee38dd1cfe2b3ca8 -14107,0x3b06ef484c141d4aa9cb2439a60dec1d63a4e336 -13657,0x3b0f28ba2c0743d968cc356cf277560108cb3f37 -14015,0x3b3bfe1d3f986dbf34feb25256fef544b0fc46ba -13708,0x3b4b740a398c52c0aa3c647e560950fb36047b87 -14041,0x3b4f015e27db8a03e06f57ab48d767db7003dbad -13786,0x3b993734e067dce3024fce4a8abd638948ce5e9b -14483,0x3bf3a554bec3296dc3af96fb2e30c54ef9878e84 -14153,0x3c14107e6a11d453033fccc96b619e889903fea9 -13718,0x3c16d5f2f47e8ac0fe7c063afec318019815eeb7 -14261,0x3c7af62d096427ae2ccaa8fcd05a72b3f9c6ef6a -14200,0x3ce4aebb3f2ee94b06b063cb28f78c3a543eb52d -14193,0x3d03748a0fbba8dd5f07b16c0178cdd1327fc58a -14640,0x3d11ac520e43121222180271b462f987d1c312f4 -14378,0x3d9a8e57e4e1b3ea511509e8f67d0c25fdcabd0b -14591,0x3dad26c84014d1b30c48124e32d3b1aad3fc8d36 -14299,0x3df1b33c8bde48b8f125dad9cb83b601daf63a3c -13717,0x3e3243ebca4af1c7ddf2d3fcd1cc4b771b983054 -13665,0x3ea18d4e4f66298f13f3019db26f263f285fe462 -14053,0x3ea452b381cf837f1e0211db95e8152a54a090b8 -13890,0x3ee739e1b20a78f3ebebc912725dc9d30f6c8301 -14018,0x3f1a8935a6ce62c487c831565784c2f60741ce4c -14457,0x3fb3282e3ba34a0bff94845f1800eb93cc6850d4 -14619,0x400f93b1661185d9f7ffb916ed4f82085a00843a -14069,0x40349e96f59acc623830b8209b1596ce550fdda1 -14063,0x409901c6cd357ec6c5f093961fbdb8ca01b8d595 -13830,0x40fa254803e8268eb72c32bedf05b2cab44e5ecd -14590,0x41a959fba3fbecf342ce2b757382237a93b16f4e -13873,0x41c8759533bdd05d73f8ee8b876cfe6600de288d -14083,0x421c25628595d1815f927f1cc85bb711dcea2f7a -14392,0x42d430c34c9e7af5f57808b496e57e2836ce5553 -14533,0x434c11700bc7945f1cad2ac7352d566090a537b1 -14349,0x43a402ce8a87f9d184cb80c0172d1fbad6918339 -13850,0x43baef0f27c894929ecc1f7422e553491d65178f -14094,0x43c5474d1210f0308f7835490bd810333e8c4310 -14337,0x441a3615b3ff4123f2de1aa355db21258bac6e5d -13854,0x441db83f916563211d9cf879c9a7dcc6ac0290cc -13773,0x4423055126bc979cf20b6cf63cce083bcf1a446e -14253,0x443a4ffdd77fc89ee113399bbec66a312d2c391a -13833,0x4444c56728e46031df909c47cb02ec6d2d5efbc7 -14529,0x45d27940f9ffb52bf71baa20947c1587809103a1 -14584,0x45eb7b0410571065ac1010cd3f57f441d1df52e6 -13658,0x4603fa8f565b36e17586e3efb621b2f86d3ee993 -14503,0x46453f42f41b52fd9423ce54b7572efb45f2f1cf -14233,0x467a8e38c8758964f7e54e1a0e82b7d54f4134f8 -14339,0x47334de966aa36bdbaeb480a86050a9b532e6599 -14514,0x4787a0e5d6d89cb9b537a1e397caac55c68f6147 -14303,0x479856c48bf3756105ce7a56fc2a6c00748b9797 -14480,0x47b10b765fad4d4f76f34b3c6314b1de89355fab -13710,0x48118cef3d2b980ce92f136232cf1f2c328a540a -14239,0x482a4b4457e02d1ce6a58cd23b5a47fe3e305c28 -14461,0x48826ff6b5045c2887bd959c728b0d7a52ac9971 -14104,0x48b6d0b608f330126a04bebc17f976fcfb3a3fd3 -13943,0x48c73b88bac41f419e5106a74cb1a8507ee475d4 -14291,0x49608e604b812f098d16defea17f6de18e740452 -13670,0x49755aae322b9c6b76c7843bcf5afa5b11cba99b -14602,0x499232ba2c8693c26011745b04fe254de98ee9a1 -14541,0x49be41e92a41862906d6ce64f188dabe1d116425 -14377,0x49c1d2f0214396e9a862169c0a2920b4e8e07173 -14615,0x4a1096f1f7558dde05600f44952a7dc692787e3f -14446,0x4a22e9d476832d724535d014e2cf27f5d48fa087 -13953,0x4a2d20e1f412f78182b71780803cc7c8234a9794 -13882,0x4a7ac7da231e6d5d30a305d5e26c6a2bdc97ee6f -14212,0x4afc88b1a4374ee3694480611aa85e78cd1bae3a -14400,0x4b1153fca71bc33d911b3bf01b0003afe6b279e2 -13735,0x4b58c9022faf4c4e6dccf256e311bab41bba72c5 -13689,0x4b629d0b4dc2a73e1f9163e997f6f384ba20db8a -14130,0x4b9ff76ae449a97f551875eabb2997bd70b8ee19 -14152,0x4bd5f4ae1d4b644991b7510d35977a6435482f09 -13682,0x4bd6123d0243b378d49a4875ddd06cf36de95fa8 -14186,0x4bfdd532771f50803bf535f18a1d5741b2e909f2 -14132,0x4c07b29c16266edbc866fd0bc935d2e460df99ed -14645,0x4c0f652bed6f607d02e0b4a6206e4ad7e30cb731 -13668,0x4c82e283c5519c04d4d53eb9d049a2e0db73eb41 -13742,0x4cc1d3a85e6ff8736ee3dcf52b46e756753eb95f -13667,0x4d20ed474dd0be231a265ff7305327c8581313d9 -14632,0x4d514c39929f7d316d89aea3776806768e0a1dca -13947,0x4dec1ffd2ee50d004a39167be60a94dedcc81eaf -14534,0x4e16724b829dd68c396aa888ea37e8d8b30d37b4 -13965,0x4e26b6815d82baa6b8c15fe4ffb646dfb4b474c7 -14366,0x4ea48ce3339c9d33743e1ee640f50f44989c19c5 -14428,0x4f12ddcc9d2118efe42f4816e7b3c60f9fb7de01 -13974,0x4f4c524ffc3d9190b53066a4fb190365a65744ba -14476,0x4f7961ee13bda96bfa9381b87d21b2baed96f0b5 -13780,0x4fb9ddeec462e688bf93bb6d5e79c80bee841b37 -14595,0x5039cde9ed71256be27a36440380f36cad45d270 -14123,0x51960a5ea01126c24cb3b565cd8d4bb16f6cae03 -14526,0x519ed94c628e9204aa7d7cccce16716d4b708ee2 -13817,0x51c8c073cd6cdc26b43dc2ad4a3f3e899eda5208 -14576,0x52181990808ee38c883f6b94585f9f1909225b67 -14358,0x521b207eaaebddff20631b1abbb3928f6619d9b0 -14489,0x524af0267bbe3d2b8117892c5f8a470704c08bdf -14547,0x527831e3fb9690abc8f176579293695c1f81e3a0 -13790,0x529385186a697292eb9a81948fd5caa354467ccf -13930,0x52a721696a0fde023e89662f5dc040a1289e1f47 -13755,0x52b42786df64658d5680deb1422185acf94cb96d -13864,0x52c081e7b86ffdbfdb3171de53e3b53b21e50fcb -14407,0x52ce0fe10b74f472a910729b8748d9c7ad75ca5d -14095,0x53113e5daaf067441498ad28bc1a6625966a3896 -14249,0x5360a4c06b6d34a2f5533800601160dc0b6dc43c -14047,0x53e00970ff9c968570e52d751f8f2ac3c0c83b4e -13831,0x54189b3724c366d648929feae0f99f74affc8663 -14167,0x54739c0f505db217b842c59a592e270664952832 -14105,0x549851b5f86ded94661b4e970e407d4b2b161559 -14627,0x54aedb5e5c98ecad6de1e9d2e3314cf2c77ff17f -14323,0x54b5789a0352c3f48418ba94acae71c70a1e9a1a -14264,0x54dca948f1460a2ba0eaf024358c7ed95e40e59f -14219,0x54e32c4c28f6e9b3f428635fd8e52b1c5742fc88 -13777,0x554c7321da41e0dd1a991a8611551623d9b04fcf -14078,0x5605e4d1d190e7e3602b5450e1452803c2ae5078 -13967,0x5616f1d59e0853046ca3deaf9b517850a43240d1 -14467,0x566d81bc65615d91b1f4258af67a9cdd10c16283 -13900,0x571e69a33dfb3a18f73fe7a42cd0d74f1078d1d3 -14550,0x574981052cc40d4b78159b0bd5a27732f9944c73 -14081,0x5845bbf8eadbb7fbbea3e4846d03b79c1de3c47d -13785,0x58859b93ca12b6e929df372320dad84372c9d2bc -14305,0x58a1f90e1498b50d4603e22b1646f2a80e86f6aa -14313,0x58df1b47b0f604fde9e3b99759f546f5d442f5b1 -14449,0x595bafededf664166dc008714dc03a1506910fcb -13935,0x59b80aca6bcd01f0cbd510e11efe43853dae578e -13692,0x59bd601fd9ab091a62a669aa4c12aa6c6e43e235 -14086,0x5a0e10d4a301a804547ec2ffd5b70111d5f7edf2 -13750,0x5a3601575bbf4ccab3c27dc18f01b3d3c84df6bc -14102,0x5a51a3c9b17bbeefdeb6c019be484705811144cb -14160,0x5a5998f4ab9aa61c7a939acdc3eb73c663eae5a6 -14475,0x5a656bccdbeafa3f5dc597f610d55df74d9d08a8 -14478,0x5a7ed3ce6096228a9fe1b673830e6a78c9cfb71b -14317,0x5b01097280a0cbd475d2298b523b9d50adbd6ca0 -13886,0x5b842b0d96d49dd946fd6b315f5fc2b5b590c2d7 -14432,0x5b864c890d943996ae9cccb071e0d57639e28247 -14369,0x5cb79211b00b2c499bda567ef12671570ca5b565 -14412,0x5cc862f6b19009a9e57d9acdd2d5bf6b69ca59ee -14353,0x5ce0616eae872fbb13cc15488583967ef8750e45 -14633,0x5cf267d9ca0692f51e6b185d97f94292ca3d4d90 -13661,0x5d1834350704db609c244a4c2e81be5526852756 -13983,0x5d1a0b0382ad83ddf34ea3aa70d5c51ba37467f8 -14417,0x5d2637c15c0f3ef21983f7569331549a25f734e7 -14311,0x5d43db9ffa312ea851df24d6cacc31dede15aa70 -14027,0x5d4debaf4e988c49fbc84698f352e75c853a44c2 -14278,0x5da8e71ea120de22447bc8ae339fe398c17178fa -14442,0x5dbdc6745caba9868ef4142a20434c60d35bd0c4 -13954,0x5e0544145abb6994904d2800bf4b241dd4b3a934 -13808,0x5e09125d0af9819eb1fd6d14c1b9916ac391a9a6 -14150,0x5e359becff1c8f6a1d5dbeb5f115dbdb1561ea41 -13878,0x5e5be88b3e03a2976b9ad48543e12f2d0f7961b9 -13671,0x5e5bfe3f4f9a6fa9affdc8cc654a8e2595a7ff50 -14484,0x5ebe7ee72ab56f82194e61d95d5a0a32cdf722e1 -14521,0x5ece025e6c05837d513c6c3c4758ad02a46fcfbe -14494,0x5ed04c578d3ae362148af980bbbd6cc49e2225a1 -13772,0x5ed5eb2bdc4f8c30b19f69b3dd9bd1d239de48fd -14221,0x5f23ad28459d70717c4a4690ea42f12d1304ede1 -14375,0x5f375635e7d1136393b34616eed4b5cc8f00a5df -14127,0x5f515a33ae1411d1f717cde065a5d513917bd0c4 -13845,0x5f714b5347f0b5de9f9598e39840e176ce889b9c -14057,0x5faa136fc58b6136ffdaeaac320076c4865c070f -13756,0x5fbc41e747756e8d8a7ecbc29bd25b0879fa94cc -14195,0x5ff262e9faeb8670709e676b55c8b57be954acd3 -14052,0x5ff31f4eb131bc82f1069487668a7be646de7c1c -13888,0x6008a4d933415ad1149e3b1cf760dbcf7facb926 -14618,0x603b89d154d5eb309719c33ad95a0fe2ee449f98 -13968,0x60417ec91411c884cc69408a6920975953482ab2 -13699,0x6098ab0ff92d2d022a8f862f9fd95baa541c41c3 -14367,0x60f97320844add7a23a9c0c5e9a288bd558a78a0 -14216,0x6164a4f3bbd87a0a11cc5f14278b8ab260217599 -14046,0x6192a98a94b3a1c2eed586433b8d6f2230799310 -14162,0x62030f110f4e953108e369fa2ab7fe2f2c3a9aab -13858,0x62254f0395bfc7995b6563c9dd3a76bab0c79c03 -13811,0x62f5a47dde7a2de8c5dbe4d415b27a708564f8c1 -13768,0x634684e3f0187cc45c427090227e85ef580eba6f -14463,0x63c2d84bd589adbf826573f9bc1f6dbbd381f799 -14398,0x64689ae2b2aafa02e3e687bb5dd09ac67db27792 -14255,0x64b18e6d7b4693f86aa12c1724f1e195232bf044 -14359,0x6519b73b495958e989fc2f26b529d0741e8ac8e8 -13846,0x653cf8527fe8d1b9a7f8d3e8cd9ef686772ad667 -14445,0x65c2461444247d50c8cf6b7e174902a2e2a064bd -14556,0x65e2ee1e9e8896f7fb391ed52a9200048fbea116 -13800,0x664cfa01904e4def83388e94c5451e8cecb29e70 -13656,0x669145d1e1565eaa3f2794d5764917c1530f9cc4 -14225,0x66c4196284837667414d6a4e9e974e0627572909 -14109,0x670e5cd858d92c441dddda6d903a96998a514430 -14126,0x6744ca13eae509427a87befa81b041c526bfdec9 -14440,0x6747017a41a8c78ab4d771fb5e17c0dcd74431d0 -13836,0x67bc3ce6c6833973cc90687d09e49acabb864bbf -13851,0x68096cda9e9d872dd76f187e3aa32bf2083b10cf -14561,0x692947882552b219cda135cd1705c21ef281c792 -13730,0x696316bf68a72f01ec95cfb92e38e6e5c15c6ba6 -14606,0x6967574169f5537124049560220e09b38f3f1691 -14110,0x6996680f2609a051e1e9027e7839f989710d2fbf -14429,0x69dcb21ba87b9a69dfdf1154b0652a473cda1651 -14569,0x69f8f6a457e5542d1ec0c9ed07201cfe4344e39b -14197,0x6a1b797a2cabf24277f7306c77c4b76cd6588d52 -14252,0x6ab84fc005d7b5d0899b621344634031a2ed8931 -14156,0x6abf2cf5728bcf3488e40b73bb3b7e2d501404e5 -14466,0x6adbbfbe58ddef8afab1a4316464c1d737c68ab6 -13946,0x6ba44773cc1388605f57e7c3730f8aff70773e5a -13721,0x6bd7a1371952eb7dd45d8555d7cfdc3fa521bb3e -14050,0x6c243db5f0470fcf692e481ec656cfad5a825d9c -13916,0x6c747d7ea6e4630b02ad26f98f6572b6885ad675 -14363,0x6c7a90ad09c2c31e6bc6b77be80af20a6bcc246b -14112,0x6c9df7301cf3c48a69fb91675df9c3c0cf87147e -14564,0x6cf904f23e325b255c57dff3bed337c1b553e8f7 -14202,0x6d4a7ea11cd9e053319db911e2a01a45d4767b63 -13744,0x6d8de59450585a86adab82c736a591cff7d2e400 -14537,0x6d906364efc365abc86ad338cfa60969e0dba0e6 -13853,0x6dbd459c6d3836c66c6fb6b0e3e0ce9f2a4224d1 -14500,0x6dc4b1829347d83b0d23f6b6bb0838341c604577 -13739,0x6dd52f76abcade1959b45569c696eafadcad3b74 -14415,0x6eacdf19678ac6c5e4e11c63213c53398c29da99 -14616,0x6eb2dbfd01a22fc64595c37bc3cb6c1bc7ca6e8b -14149,0x6ecfad9434fb2b2f3abd4fb21080cbe842381940 -14504,0x6edfee2de18ef5c9924bbd9af01aa0f9734a4207 -14282,0x6f345ed81515eaa1c9202687b0e119b5826c5566 -13944,0x6f62ea5633726f29b0c18b86c902f62f20f7cbd6 -14117,0x6f73fdd76de6e935adb975a74a7a3804f5a872e2 -14084,0x6f7d3a1e8fc37de27ab1fc1ee49255c49d00f132 -14525,0x6f82f89e5d5dbcd21769391f5ba3f6ec70f72b11 -14623,0x701244651138e150d6b24f576b74543f6026cd00 -13828,0x704f5bc8a2ef5889b1636262137e0fae3b41db9b -14092,0x706ed810944b3dc854698da5f7955bc8f1f720ab -14416,0x70c49a90cf888a3912aace9191cd55a3bd840ba3 -14562,0x70d91f4f6bc5656e7d5c9de7266d00e998f61c28 -13675,0x7105b732f2f0d5a6f7bdb159f0074b8587e2425b -14342,0x711d2830b94c80e352f9b4efc6c9c761b37f11a2 -14134,0x7161c3416e08abaa5cd38e68d9a28e43a694e037 -14536,0x71718d7e2d353a49c387e94e2f6bcd34d9f88f2b -13865,0x7171e835456a3c681e586b1ba181fefe0d9172b6 -13724,0x71e5df9b92b5e1aa8993d882eb4f18986e87d3c2 -14382,0x71ea18e0e521c06123c4a0c7abe1d1456ea7d502 -14306,0x721432a9a9b34883c519bf39a1c1fe4a3089ca8b -14390,0x7216e155c34fc19bbbfabadf03820c36fb004054 -13703,0x722ef09f933f09069257c68563b715486365b895 -14049,0x7236787cea93bd7c1e6fdfb55219af6a7c240adf -14637,0x7238efb70ce4cd5697e78d02f6cb3a3d9a042052 -14488,0x72781ece8e60e56bd887cf52357b30fde7523afb -13832,0x729c4240950faa23649f70cd9ec4f759ee34d5ea -13679,0x72b664d82b0d1b4e3d9c18d73c4c8109233b4bad -14419,0x72ea27acf1cabd4387d150e2eb057feb44e849fc -14376,0x731826088f73c246fc2580ab6a966d1fffd85a58 -13919,0x734a2538632914e8b14dfb710e2575c72decc28b -14013,0x734c736ced848f77ac8e4f12f8f66a975e48e509 -14174,0x73b67170f36f45f0729b376ec391a64b7148f7eb -13856,0x73baf282823f397d4fc190fcb3f24aaed22883a3 -14039,0x73d0d0cf7f3ff7a3d5459cfff0ca0a2b5dc03767 -14312,0x740dffe87407b36c470a5d7953a5230f3f84ffc0 -14124,0x7410f132fb548dd70e0f0ea997ad1b04f13d458b -14405,0x7465e2e29a447bd9c816bb1091cb48f78e3d8eea -13709,0x748a38f4a430504deb15fd5bb157dd466f8284b9 -14357,0x74c82c1f68b5cfde0c512af3d8e9fc766aaddf68 -14284,0x74db6e192fed8e4e093c072deaafa2b45f178c29 -14481,0x75241cf5f54dcc431e432bffd3da987585cbb5d3 -14454,0x75420cb98a31e397acd2b5de590383d20a2d89bf -13971,0x7566e94df784680b5b1a29632ec6f7f65263da69 -14059,0x7577f06af3a26bcaace96b7f7137ae8cd66a1236 -14487,0x75bd2a63caba8e6394faa27bebe240c648cce3dd -14096,0x75e51dbbfe9ea2c3c52a70a78b58d6c0eaebafb3 -14286,0x76618a9429c65bc4094bbeb1a32bc0d91cc7c092 -14191,0x77178d12342c445ac7a19d7398bd9690d4e26ad6 -13695,0x772f48f073c1f328c264619fc3bba28e3efdefb0 -13867,0x779b20c1631721e12befbbb2a26b4ea7fc6e0a1e -14341,0x77d0cc9568605bfff32f918c8ffaa53f72901416 -14188,0x78120c1ca337007323de2226d677e7fcf42d6ee7 -13924,0x7841b4973e734d85811285a2958da88ab2f1651f -14318,0x784b4445ab18839972a70ef8b0d489e8de39de66 -14316,0x78ab37e32c04efa43cbfb782dad1af3a919c597a -14582,0x78e62b4fb997807b0b83675e9259bf853ebc677b -13895,0x78f1dacdeb229e26b2fa4f18a39f1d500cb0c6c7 -13688,0x7913c7da57a53b58ab98b7a569d3f2f23caec981 -14385,0x79931d6f726d33d71cbec0afbdf846aaf33c3849 -14477,0x79c1ee1f9fd02aaedfe032323283c809ffa47bc6 -13731,0x79c6a1ea9b1dbdeba5d0870993d5a561118850f2 -13664,0x79e45d7f55cebc7321d7e868ef6571ac7aa2b29e -14121,0x7a0bab5c2ad4a0774411baeb14958f2778296393 -14327,0x7aa210b48553ad6317354357e1694ad49254b6ff -14594,0x7af98c4edc50cd7ec18c8b28a34200a16f53f3a4 -14036,0x7b989509629ad8886219de6de61490146955e273 -14622,0x7bad05452ce6e4a2bf05bea51e0085079348aa52 -14513,0x7bc367f5d565937b1ddb87501019aa0bd0b7e953 -14029,0x7c3a48f30b417815302aed0ef1cecf1dea98417f -13805,0x7c61aa15901bd79503f5f617facc98ca89586b65 -13956,0x7c7504511e95348fa59f09944f96592c73565c0b -14492,0x7c7e447a7ec5882a45fed3fb7118f30e3030381e -14017,0x7cf8f5f3092fdfd034c3ae48efb5103f8ac8f33f -13719,0x7d462952c003b80fe16bbe826e4ae34cfc4aebb9 -14106,0x7d81cb639c59634005fa7604b087a44068f78102 -13986,0x7df0bbe55189ee1d65f6475576783fdaa125a1e9 -13669,0x7e640dd1ed987171a0353155901b88948a721a9c -14119,0x7eada83e15acd08d22ad85a1dce92e5a257acb92 -13749,0x7ef5e7a29e44a58a0e587b3af115be16e305a694 -14515,0x7f330c97dad8ee9ab92aee69990b655f90f83d2e -14030,0x7f729861dfc946c907375fde8dfb0a61fa310aa3 -14336,0x7f9e5453d4a55e9b617b67168a3e05baa849fcc4 -13887,0x7fc3171e0cbea94e545d22ef03c1e5723a07d6b3 -13929,0x802ae82dc6973e384fff0ab72f0a4de46e933ea9 -14054,0x803500852b9aacbe347e7889de353619de8e6981 -14330,0x8059fcac9a7121dcb9954d1232b4573ceeae6668 -14511,0x8098c6273bd5f9d32d03e6cb62472a9e6608eff2 -13781,0x80df9ba3bff758d6728aa6b7bf09faa86b3e7586 -14379,0x812b0a4e74eaeca344fcfa2db15099f036943850 -13972,0x813654799874321fb997401dd8ccbf96aae26ad6 -14101,0x817b80807dc832ac626ebbd90b379b3ab09a4a76 -14169,0x81d4bdb63e59339375a1376a3bd0d99c22fc0126 -14496,0x82ac2ce43e33683c58be4cdc40975e73aa50f459 -14427,0x82deb459f2323885db79f1a2d3b4f23782aa43b3 -13813,0x83c861b6c7a35be020c6a3c4f9432a5a3a8ac430 -14319,0x83d6675fe072928132c1b98ca3647de2fa9c8d84 -14148,0x840048281d438bc834fa5c8a3d0ba7822553bc88 -14248,0x841d4da8ef4623c67b7c5945b3b80acd367b8d89 -14402,0x849383de33378e7dc0fd05f5d03daa33f231dc90 -14048,0x84bb9bbbbc4ecb187d1e2f310381ddf113d569fa -13696,0x8537ec3fcad0cf8ba7d9038b639f33a1cea43b75 -13962,0x85707e5abb9a31464efa1dc01ea6d3349d39b3ab -14638,0x85879d28ed28ef8928af5c5048401f1631a94c53 -13746,0x858fc8349e0699b7e976edef14cf712ce2e2f275 -14307,0x85e881f9ff62284dde440416b077df9ab0602721 -13908,0x85e8d326bb7d93a71085206604caccb793309ce5 -14598,0x85f411fa07be5ae5a9a435b0e5b0dede1385df6f -14244,0x864d09ef3046d04f651f1bed72cb4d22cba75e2d -13799,0x8663cc62dcedf8a50f7c3c6754152e925d4b50aa -13751,0x8691506315cf30e15809013134a272a0918c1283 -14131,0x86c2ac0ddb3b8209b1c2cfbc5884b8ab4a88fe5a -13950,0x86e8b6acf92a83101db4c91d716acbc0ef0413d1 -14194,0x86f1e0420c26a858fc203a3645dd1a36868f18e5 -14245,0x8711ac690984badc42e7abe71f351b5ac2a2ad0d -14011,0x876bd8d3745d9db0f5df48d34fbda1074eaadfe5 -14187,0x8774a1038471ff73e10b1dbe165287c03289eb8b -14279,0x87c4bf6b6385f674bcbdf007bd861e80be59c1c6 -13859,0x87ead9fb6d5a3de026ac17b7a2c1323180e0e2be -13745,0x8878f9b11ed706c576ac0930e0a6a6301f9260a3 -13993,0x889db173b71fd98341f6195babb339e50f0d1b3f -14474,0x88b6948513517c46637ae2195b184d75da0f7d1f -14295,0x8914bb02e93cf66bb30cd39870d247a1ea7284ed -14002,0x899dc6b79c4b57ae649d4cebd14c74f9dfb43cca -14254,0x8a2c66fa05b503397e41e81c70f515916341da32 -14038,0x8a4136490acd583719c0073b67e85fefb5a952c8 -13841,0x8a4593af89612e8a4a0b129822d4e81cf736809d -14163,0x8a9ab487cb98747a62e01a266feb6cad53e1620a -14555,0x8ac835c05530f10595c8015467339523154b4d85 -13848,0x8bd0e178f14be4ae11d68442fce926781ff4a9ff -14122,0x8bfe5e95055f8c5c56bc020dd1b15221a00116b7 -14281,0x8c33da11f3b376c31aa759430a32987601d72cb6 -13925,0x8c43f2910902b26fa7635a81088bf615ec618861 -13978,0x8c835dfaa34e2ae61775e80ee29e2c724c6ae2bb -14019,0x8c8d948915fb637db88fb6fa42efe462e1007bef -13989,0x8d4b85ab2bfe11d63ae637ff552e8fefdf55ff01 -14075,0x8e5c808cfd0ea3f61cd0a8a61338be71b73f72e9 -14545,0x8e934e34182bd2e4416f08814793029f140956c6 -13964,0x8ea0e20e9bb736e66f6aefc4078fc339d058f1a3 -14560,0x8f1b5ed853bda4f5bd164373de22e5240aa83494 -13827,0x8f38fc5a38be39d8c477b9d9076e9504f23e7933 -14230,0x8f449bd8602f5d3fcedbdd7c72ac55d03b178c8f -13889,0x8f9acef0cdc067819cf3ce7fbe23148cb1bf851e -14585,0x8fda522477578bdc12f88e45e6391b374660f69b -14060,0x9008a9e08033bf061814a0f0220710a1b7ec10c6 -14214,0x90100d4731e49c8ec0df584d404c9f1bbcf18999 -14298,0x90d596b9253eaff58765d358e8372a5abed81715 -14538,0x90f57ae74bc77ab7d0e8329c4cf9919b941c16b7 -14113,0x916660262bdf70e35fbcd002d8056ca13bc02ee4 -14443,0x917c374599fe384818275f7c47ab11b23afdeb48 -14374,0x92023ca5a2a9d1d33ef07c88afd421539d562544 -14325,0x927314536a55faf469ceb2ec82db44b59a062204 -14198,0x929577dff77995fd970e16be588b66950d0a1e04 -14418,0x92ac05fd72035cca60af6dbc4524c51d46479875 -14355,0x93098b98e71da7fb6eff275ae51309d4374acc42 -13922,0x9320a1c70458db72d34a19171fdf99bebea5f74d -13907,0x9365d5fa93d4afd93622f11843d0913b07c0c4c1 -14159,0x93910ae54530784ed7ae194af85b1012839c1603 -13678,0x939aa93354709524f6e99447718015cbbaccee80 -13884,0x93b9dac2d57aad1966859e8d1b5ccf5b95de9af4 -14196,0x94740ca2c47e13b7b0d3f44fc552ecc880e1d824 -14558,0x9482aafdced6b899626f465e1fa0cf1b1418d797 -14354,0x949f5dd07ad960c674757eb4408e7ba980b872d9 -14116,0x94c13968eef7204c9ef5bb3756cb980de0fddf8a -13958,0x95170d027c905b83dc356a75e866c7c20e440356 -14361,0x95320fd234c9ac9b6b18860fd35da8ebf258fb50 -14181,0x959c99e14af1e9312d176eb6828ac890e6ef18c1 -14362,0x95b00f33d83173059c9ee9c108f9b78d8d35c7e7 -13875,0x95e1e391b65cd83c7ef50edfbfa0666115e8bb0b -14043,0x9621780ee84283315e1ba7462389ea9e7bff69af -14565,0x962d51781580e2c09026e2b9b34c068ee9420679 -14639,0x9673950ddb7b9a8fcfb5a5daa2cc29eb51dc3cfb -14505,0x96dae65460d70adfe3d16920efcba624b9bd02bd -13903,0x972dbdeee88898a59ff88ae9a4cff3b6d61961d9 -14210,0x97655ee5a41171d7dfd3ab9b21e75b200522e2a8 -14258,0x97e5f62a2d91d3acafdbd21b48f186f5b7d69c13 -14371,0x97ec4f25d7525eb9f7eaa4c04c4549031108e3cc -14173,0x986f8c98787ae40ebc43c9958c598f5ce10aaf89 -14103,0x98a731c7cd1565b7fa26e2f81be346afc5ae2333 -14157,0x99d5db0d4ce58d2d8410243971f5cf414ae64382 -14554,0x99eb05388e5afb15795f63921195094999155971 -13796,0x9a2d95d0cc891297df203dcb25cd547af70f4156 -13683,0x9a3a364018bbb0ddc9d19049eeb23c60fb802524 -14603,0x9a496666d33c9dcffe007a46b4ce829552a015de -14634,0x9a50ad16b544ad6c859d33b3dc2f0d5e44744846 -14034,0x9a562b3b337aaf23dc579ddfbd4d3b498e364633 -13725,0x9a5d8cfbcbc67a806ae63f32ac2963aa88b4d092 -14206,0x9b1f149317c92375e31f2762535ecc1e766bdac9 -14535,0x9b4a4c2dfecf189a5ca98a11190c8e6a74169d16 -14563,0x9b96d189958c5be5e4a5d5280b5b58ad610db157 -14166,0x9bb3e55b319d21d75fa193d0704a8b494a75d3f2 -13697,0x9bd8a303ef63606c68e033f90f7fa1fc94c257cd -13819,0x9c68a67dcf9b30b9a53d7d19cfdafb0e8b2fa686 -14498,0x9c80ec37c4e4a0000612043c9694c8dd5fde1650 -14283,0x9cb642f2e6b96f5ff3bf2350c94e1585250360e4 -14296,0x9d0a410d18705ee58c30211efc6025627bebe824 -13835,0x9d102d9640ed42bc8c12b090adc100a7de2c61e0 -13966,0x9d34f1d15c22e4c0924804e2a38cbe93dfb84bc2 -13852,0x9d39570483e6df70e9cbe62fe84444b100571b68 -14077,0x9d51148eac545e3616a7ea22492f87c9b783605d -14268,0x9d610931013e37626911d1a3451165331e5259cc -14630,0x9d933add7f9c97736cdeb1c4a2a5eab9a7210dc0 -13891,0x9dd013f9083741bc70c7ce32c2d6ab022a3d7ff7 -14182,0x9dfc36d333a3c03dd02c4000d14c2c37d7258220 -14530,0x9e010e930d00f03f2911058adb8d996795841689 -14001,0x9e0d0b02323910e47b42af96ab1ffc4a04f491ce -13904,0x9e6c8fbb66614f28b35a196cd8e1fea42b2fc688 -14055,0x9f284b4b450e8113c1bd4332c9f37666a81e48ca -14625,0x9f4971d618360eba4e14a5e93b5c8eca4c53bb7c -14099,0x9f6b09fc2ea2ef9f4454ac6875829a7a89c9cd92 -14314,0xa0102f64a395035a945f150aaad1be8f7ab163f5 -14479,0xa02a0d5913e44669afed94d38764760b41c18c4e -14066,0xa08fd04353e11301ff3e33093cbf1a8a47bd9ca9 -14240,0xa0d9966de6dcb7982dc9a1bd7a6087f6f3edd928 -14609,0xa110bf95801f4aaf8a0feb84ca7c7d2088ed50e0 -14129,0xa18fa074a2a5b01e69a35771e709553af4676558 -13912,0xa1de0bdac29ceaa2c2afae451d06b069bab664f8 -13909,0xa1ed4798591b85b5f62ac93078f2d3b552081f36 -14532,0xa2c2b08897fb3ddd19ce8e4e9af2e9fef1810be3 -13977,0xa2f78b820d55e23c83a7d7088b4aaa1d6b4cdb09 -14139,0xa2fee06e27dbd5b68a76d7aafc7b7403e25b5c57 -14644,0xa3042385f775c52ad7e038cec45a95c2dc566f5d -14022,0xa3409548fa8bdcb9e6993e465b53e820675dfeee -13770,0xa36faf16f31c12285467b1973ee8fa144ed4d846 -13855,0xa3d2c36e046faf6868e32fdc0065550f5d0547d7 -13775,0xa4269c83aaf682403c28ebe33c350d7c20d4a449 -13804,0xa461f10f41aa26e05a8efc6e38c214cb8a880e8b -14414,0xa48b26819b017c9355d933c664088588ba793a9e -14543,0xa4c817a425d3443baf610ca614c8b11688a288fb -14005,0xa505edd0f121cdc70125afaaa77e6ab07c2d719b -14396,0xa525d5eeda8f8a801e0e2f2eae05bd1889d2e064 -14213,0xa58d58455e46f7cc4fb3bd10a1ca155becf11bf8 -14397,0xa5995760cd0acfce836a8230b34c0f15adacc81c -13894,0xa5b043955d59e2cafd267fb12c4bb0a1c41cbbf9 -14331,0xa5be1aab90580c9cc2b7f49fa4847e440e384e37 -13747,0xa6e8a12250aa7b144ee587ba89a29f3978f16e51 -13839,0xa6f425e23df0d65799a494938c4de812e641c824 -14548,0xa6f9e1ef0833a56fcc00cc69d07519a7332fa29a -14155,0xa7127bb76eba78b3467f1cce20daf84f179df5a7 -13743,0xa74b55b4a5db0cf46df162f606ff29d82e385f1a -13821,0xa7f3fc32043757039d5e13d790ee43edbcba8b7c -14452,0xa84fd728a7cd878d0aec4ce155677d720a3185a8 -14635,0xa85771964de94326f0bc61699cfafd3ec6d1c6e5 -14389,0xa85c00be2c8cf77d06c3ab404434763d536c1a65 -13868,0xa87ebcffd8f19588b6d1f7d85cac304465a38c66 -14350,0xa95b27c8d12d58a1aadd6b9e7f1bd9a59ece3fbd -14435,0xaa00d9a30728ef61f6f7e2285f008d8ecce29ac4 -13994,0xaa1318fe7cacc71726d195e006f93ee68147ed56 -14274,0xaa3d2ddeaa10358a5ec730eb126ac57e9fb7f970 -14238,0xaac048b5cd4abd2d40c52147c90586e26902970a -13795,0xaacc98efafc86211a1b4ecb6824edbb8a011364d -13975,0xab3c0f3840dce66846769413f0aec4dbcb06373d -14236,0xab3dbdbba7d1f1214a301afa4a418d9064fc1621 -13877,0xab3f8a9599d62f09a71d7337dfff4458a4c7fe27 -14024,0xab41d08caa8bfe02ef640b6ce42451582afe2e01 -14495,0xab6a4ae035a07513f2735c4905bd18812638d233 -14551,0xabaca4c361ffca3e8494430971b13888c5c9f200 -14512,0xabea9cd285b402331386bfcf370440d8aedfd171 -13942,0xac320f018d2572c1ffc4094d8e4c82d4e067b95c -13838,0xad38794180df8ecd3d0b92ca243f5ac2e188d144 -14044,0xad3d8e164e3db55c7c5128229e3652e70c8d82bf -13874,0xad499f09aa463da515906f6a956364634e12907d -14464,0xad7b4c162707e0b2b5f6fddbd3f8538a5fba0d60 -14177,0xad927fef0ad5b2384bd5a09f70531815b3a1f50c -14455,0xae06ce9220ceb19b7d153699df9baaaff6d19853 -14517,0xae38ca61f4820d63f9c7d720a9329fedd2e69742 -14051,0xae7a87efa013778f8fbe64a23ccfe451ea2b18ee -14128,0xae7c17527a77841b099f20318989f0561ecea000 -13976,0xaf32c18f1092f1f3d37bc776d7d124c619176d1b -13691,0xaf4f641a51bbf5a8638de13a79ae2c0be7565ca8 -14600,0xafdda167f9816f535a58c2a68e7a309e68a1cf6d -14165,0xaff811c0f1623fdbe5d457e1e14e13ccd53ff065 -14424,0xb04a6c20a37848a2f210b95b54324f8f3eb6df6f -13711,0xb0747415339f270543898e9fa97e6526c4ff2961 -14373,0xb0d9f06051d7aec0319aebae63316dd686667d57 -14522,0xb0ea0a8cabaa2770f213b9c47d5adfba07f7c01c -13881,0xb0f82b031438117049439abfb3d8150f06220c40 -13880,0xb12430a7d0a51a2ce958c910c764e47f41a996d4 -14462,0xb18aabb37929646364509b6c93b2c05836c40a6a -13949,0xb1c51b60fde87d09bbdec7092869839aae8f31a5 -14232,0xb1fde0165bf076bd5ff9d1c53e3b4a5bfaea8f68 -14308,0xb24f50dd9918934ab2228be7a097411ca28f6c14 -14395,0xb256a222e71375e942730fa445a3275f725a3dc1 -13728,0xb2bf3132488f383ecbde0c299c53f1c735277842 -14499,0xb2da5d2a3797f29842eca660c22dec2f710038ff -14250,0xb2e40a4f41678178a9322e3233d010313cd53c99 -14209,0xb3c1e00132c341378dcd1aedfcf090a45aad5144 -14062,0xb41c20c642ff2870f1a503eb1cbc9d69c27f586c -14185,0xb4ba990edd8a5c575ccf582c54bb2b9c14006b65 -14605,0xb4d616a489e09b40740a601934f7166f840f7748 -14460,0xb514b0d5abfcaae85456ecc09758c8ef4cfbb5be -13757,0xb5ce2b5c570bd5e59501f9dc226a04eb13a07129 -14430,0xb60b8a2241ee310d6348801b37e3d9ee700a7ed6 -13702,0xb6599bd362120dc70d48409b8a08888807050700 -13690,0xb668d970a9f10a05162a401768f4ce1bb289e4e7 -14020,0xb673b7878eb37ca3bd83bf005823149d5f6ffcbe -14372,0xb6dd13b843b1c272452c1bd5f74899544279b4e8 -14222,0xb6f721b463d59fc5348cc9339dc735cd51f9e0b3 -13957,0xb70d9359ba2006417c2c5f21948d82462e51fc16 -14320,0xb72c27ff766c9ffb6715d7bc5f9ce82b6862618d -13961,0xb75bb96f6c783ceb47fa0f1f3bf6f47e6225d575 -14508,0xb7a9060f6f474265ddbb617c0d794fdb34245d96 -13707,0xb7c6b934fb64ee968dabef05274813ddfe42f309 -13829,0xb7f33d39a94169dcaf0f85767ad2621f0640d5ea -13951,0xb82e47d1670d45275a52f0ce6148b95bb41d86b9 -13715,0xb8579434256ae1d9e691e00fd68a6c8dd3d0aefd -13734,0xb87749c21a9d65198c4937480fb19e1f4d6708a8 -13714,0xb8c1f850aa136f5414097ee5ded65fffeeaa93b9 -13723,0xb8e1b172098a80ddd3c3e33697594c340c562f12 -14527,0xb95899f0e73539271079b7f2330a358f968ec392 -14420,0xb96bdca8d88ac40cfdf48d443eec8e9263e6bf95 -13842,0xb97bfd2c4d9df9804bd1019c8b664d5ba6d26f14 -13945,0xb9e6b8952b1e67685a6208af4ff2a32a5a3c4035 -14224,0xba0320822e711ab94f0d6e3dca95e6f9bf4d102c -13870,0xba073023b8278cbc90bb27490b8279369bf7053b -14607,0xba32d89fab75ee8f5b85516128ef47f607f98e3a -14456,0xbad33428ab0460ca8825001f3d6785804bebbcdc -14571,0xbadffed8695448d132696cfa14b5a5b7c86d340b -14473,0xbae4fc1956b635a739ac3e11f9e25f0d0c1321ca -14501,0xbb00cabfd199d23f64f6468a00e5b9858ecfce24 -13969,0xbb16344014a99e3691752cee710413e42753a412 -14218,0xbb948b618b59fbbca8371454e74a38f5544bd104 -14394,0xbbfa0478ad6e5d5040cd21f7aca64e56ff3426e6 -14204,0xbbfc9c6eeb1589da9f5e50ce9312aa1c0aa09410 -13789,0xbc449be12a6f475236283c92bd4462e4ca8575c4 -14592,0xbc5f1ae773f5545cae3a01baf4f29484ee086330 -13910,0xbd33a39bd82398d4ecdb8230b135959242acf49c -14146,0xbd7a3b7dbeb096f0b832cf467b94b091f30c34ec -13913,0xbd8fa1348a0c576eac4dec9ccc8237a23001b6a0 -13849,0xbd9a52d76f814f6baa553152542ba40b9f840b6f -14360,0xbdde4edcbb57e9bcd67bec10e8425b8082f6d8d7 -14448,0xbe1119dfd2f831185cc75d5f97348c19575f5088 -13843,0xbe1614f5caa23948392ceacd62446ec238d6dd95 -13999,0xbe5de48197fc974600929196239e264ecb703ee8 -14158,0xbe760e38e8878538b94ceec168b1ca458e96178b -14189,0xbe7dc0896f0f0580640266ee5228942e15561331 -13844,0xbe96ef03af28266c51598ddd0b1460a2ea90d54b -14351,0xbf0184af499b6701a7e742ca265c858fbea09501 -14439,0xbfba3368d94b8b006a4fd59c13b9e9f9b071d106 -14403,0xc087482f00df3c1eb58dc23af04347dd78709ab8 -13936,0xc16d624e0e16ec04ef7f9c5db11fde4791ca22e1 -14176,0xc178c8cf86d7f62cbb773db9dca88bbc60276385 -14577,0xc191a98c92ceb3f05f740f6c8f496a78fb574b26 -14256,0xc1a005875a56aa7d5a58e47a192512e5b565c973 -14404,0xc1b9ca937c1c328e16ee89bad89af2d23ff7bdfe -13758,0xc2bbac5458290de37263f0fa0d23fc76905437fb -14073,0xc2d09cbb079a38d18d9865ae1d170de51833f4f2 -14071,0xc31bf706e2309de50727ec86ee2b607e7869fa5a -14080,0xc3562808005d71953e8a6f430464039319680675 -14064,0xc35896651ecd5c71c8cb2d8e88c8f2791b7ec46b -14649,0xc50e259972af64e56dbb3f6d17302139e65ddf05 -14294,0xc6063b8655c7cd45bd26186f17e254d17143ed83 -13713,0xc668ed8d5fe77316ddbd2444359a04ab380765f0 -14040,0xc6a32fe0bb7784e82002042b2df0cd272791eddd -14436,0xc6b1b6c51c690f006dd8978ed6698585bdf96bb9 -14229,0xc6cdc0414cce84021bbf077ec4e35735b1e1222c -13694,0xc6e3f755725d174a6b7923a2d8a8b3db73ba8d0c -14383,0xc711252c26cda0603a6e3103354c65bb668e14cd -13802,0xc758a8f0601d9b516b228140ba032f4250a99307 -13662,0xc8486c57f8d1e01b440363db6f148681b829c685 -13759,0xc84da6c8ec7a57cd10b939e79eaf9d2d17834e04 -14016,0xc873f72d804f61e5dba599026e752ca56f63aa0c -13794,0xc87c04bcdb88dd61ce04ce7a77140b9e2897ebcc -13782,0xc896918e24ab34a84368ed251a28a2c5a57e1cd3 -13760,0xc8b5ebc7af15f212cb4bdfa8ea77176872fc73f9 -14425,0xc8d6c21d79c86708b787220a29cc489ef0ecdfe5 -14065,0xc94ab096f8d3bfb48893ac4fcb2b70bedcd47398 -14338,0xc97868390ad1600919919791f37e8e185a22729a -13726,0xc98ef8491606af40a4a7a1fcbf39ccb9beb48faf -13776,0xcabe95efff9570961404440254ee36de5aa5f941 -14333,0xcad994415747355c8a2e6556d1021905b4e204ce -13980,0xcb12019df98a04db971ebc7711e41bb0b40b2a71 -14321,0xcb1f03eb220c9e7f851c5ba7e30282a554c050a7 -14203,0xcb5151d41845c63b002b262c4bd4b0df76bd0b52 -13774,0xcba9adc72bf8b09934b4e1d1f22db877bd822abb -14142,0xcc59673feef8d8d1c053bc634bf541a1b8337fed -14217,0xcc7f55aac69f6edc52872a98fe5d2bfc1a7a0684 -14597,0xcc887c74eaccae5cf33f2d76f6dfcc0bbb5d879f -14272,0xccc170bdcdaa3c026302b19baef84ea05c8afb17 -14509,0xcccfbc35eeaf17a19ac3f17a9edb4c7c18c320b0 -14642,0xccd76830ec5ae954f29eb1cda80598f36771e836 -13893,0xcd0b93827ff8925ec7a2c89f827ae962f764583f -13766,0xcdccb246cbc7834f1543657a61a75d2f2467ce22 -13984,0xce38997e80b1b585b4dd57a1277a066a0a7874bc -14309,0xcedf20450c307a552f35f7487d83cd81d4063b11 -14557,0xcee33faae88f89f389e603057fa16c6ed4c55ca2 -14648,0xcef69e1e0172f15fede9a007c0ff6871e9f6fb00 -14574,0xcf10d17bad67ce190a94f08fdd7b4e51540fd860 -13926,0xcf46cdb74e737932bbcff6cba344f56985120e30 -14646,0xcf52a8783fe35b9149e53d1df4c1dc40f6fb9924 -14184,0xcf76cf5d8d6f6094aafd64062887d3d699103e4d -14010,0xcf94ae82979a08c56051d14010e603f78e9c0024 -14540,0xcfb9d5d6667df4c2fdb543ff26a9b86d01c37ab0 -13996,0xd05aac9c986294ae9c9e12967c5e7f433fb8d0b0 -14003,0xd0f8f476468e0dbe0e74235b42bff5d85ee7cb01 -13897,0xd0fd83d02fc5fa531f709b2bed313e320a63e5be -14100,0xd24b8feeea13a0ecce247e37e8ad1a0b2620fc5b -14626,0xd2601a124e56b303d3db32445f9d5dc6d59bfe55 -14175,0xd2af64185331f9b95fb8c25c235e434b2f6ccd8d -14276,0xd2c1f2c0615b51d8586baa0473874c107ecb2e42 -14510,0xd2f1e3e8df85b1bc9af3b478db3387dc0830c4ce -14072,0xd360b73b19fb20ac874633553fb1007e9fcb2b78 -14170,0xd40491cd20f8e8c616a5b9cde5a5ee86b4f4c30f -13791,0xd40be8dc81e4758bea51311c78dd9e000b6df827 -14413,0xd4cb96d0f252bf20f2871a250524bc573fe61110 -14243,0xd50d10d913d8644d2ed9bad8b9a1d430008a65ec -14523,0xd5820ee0f55205f6cde8bb0647072143b3060067 -13752,0xd5853907b2390a9aedb6a4b11cd26608b2147b6a -14386,0xd5b851e83a987ad3a6fadbc83792938369f65e91 -13753,0xd6596c472db5d498158db9642645f7126d1d7b9c -14141,0xd67e3c461474678145d92644676f08cc48874e4a -13823,0xd707799ceac0ac103eedd3ca6bde9ad6fa8bf091 -14262,0xd72b6d37fb1e04c802a5dc788b219378200c5e64 -14447,0xd7480e6a18186ef2e79dd1218c5ba85a1f9e5bbd -14421,0xd768e9c9ee63cab5f9aacd304b271bdf5d4aabb7 -14056,0xd7a4d932d8a26d5af0c2d09dca32ac84a1b4e734 -14347,0xd7df5272e10e4ad2fcc98dd8114c059194f29c3c -14482,0xd80e52a51f38838455df1c1724a3b7556d1f7e4b -14006,0xd837d6f0f9abb1a31e63b0d7534e6ba6509b483e -13810,0xd987203cf96adefec0d14405dc5c65e1a6e78e16 -13815,0xd9b1a9eba74943f26887e2bb36b7305e829e05a9 -14553,0xd9c0894fd94abaefb9278b2c7d590482c0732642 -13754,0xda022787871559a5b531c657d5562e9efa50deb6 -14507,0xdb21bb0389b616bb2ebde855975df4f2ce9fb74f -14340,0xdb5bb90f94de6f89eba26e50a438339b115ee7ff -14528,0xdb80dabe81db21b02ec1b252b11efaccd4099ab2 -14074,0xdb87ee6088f0e2037faf9c21dbc9735f5422592b -14138,0xdbe692a5948f8215924ec6de6eac1a31b0ba910f -14091,0xdcb6c8bb94ff0365cfc2f52f97ef4484865530bb -13763,0xdcb820d383ad87737da41f7f201866ef8792616a -13736,0xddde0c1bb69fc81d9ec13bc82e96d52fea2ea4d6 -14125,0xddff2c119a85a5d9ea3d6b5d6ec8c4dee25d1cfc -13921,0xde2840080d90bf76f10036f4d46774a8c3711a50 -13914,0xde6a7377b64734fabb3b0ed4724a5479ee391705 -14344,0xde819447eb5ec500b11291ffcd73416bcd3f2e4b -14490,0xde9a3ae00759c24fd183db7ce5437d44b1ebaf72 -13871,0xdeaede17c3bb68218d5bca8d5ef7276597f926d5 -14164,0xdefc659db6f391775f8c1949c78211b735ccc42e -14324,0xdeff4a009e417606a311bba899ad5e8d67139307 -14437,0xdf446838fd48ccd395c0a7cc666d85e8c05367bc -14118,0xdfcaebe8f6ea5e022befafae8c6cdae8d4e1094b -14470,0xe06d686c4e375db8f1156efba1d3d1d44dc255c3 -14368,0xe0751b35106398a8cf6b4367557e080a994a044a -14115,0xe09b074a710a9c9c5f3083aa4f46670845f34e73 -14608,0xe17642a5f4c6c18e0e83d69ba04fdb04028ff7f7 -13997,0xe1a56cc3a61424230059112a7fa5733467cd2a4e -14079,0xe1bdbb1e1de2e4a836993b276acc91432686b095 -13716,0xe1cfba047ef7f009c79dd9dabd8c464938a4fa90 -14426,0xe1ebefc179218874c7b6fd31289ef4480ef6edc3 -13674,0xe1fdaefe7531bfeaf7d9ba5497c675adcb05837f -13660,0xe213f52e59fba46e8920c248bd11721fbb0475c0 -14468,0xe23ea26af3eeef4d998930e16c1252fdd581b2e7 -13818,0xe25663cf310adf46a83b9bfa1e6ea8a9a140ead0 -14257,0xe2a666e23dd670f0c9ca3f619b667ce872f4cce5 -13863,0xe2bf24d3400eb9c1498bb2a2363aaa13986c6622 -14643,0xe31e2ec06ae19c0e89edb16318f082b9c09d6af3 -14614,0xe35d62e4dd1eac088e88901c1422846102a969d2 -14422,0xe394e968254797e2eaa3cf344262f89a27aa3f7d -13847,0xe3c8bebcb28c7886d945bf5a93dda2d0032746b8 -13985,0xe402d5eef58aad816d7240e50f20922f53a81711 -13932,0xe42fb253e2d32910e62f2bf059cb8fb0daa32a45 -14451,0xe45815cdc000c3f8c121cc1f7aa1abd1306cf9d3 -14516,0xe47aaa8c7902fe4c801418182946d861fc12bd13 -14265,0xe52031dcde30cc90eb0e1cbdcaba5775bf89607f -13659,0xe566e89262fedb8e43677e11d8e1a746c67b8a2a -14266,0xe5fc30452f61cfbe8914e0bcaa752cd10cbf3949 -14434,0xe62e90efc179a36ca80a9e9f2a64e02fc6066e00 -13704,0xe6410ef478f3ba4a9d983d205226acc6cc794b07 -14506,0xe649d4c1beb63be602ab69e73d3d846b812bc78e -13937,0xe6bb5f96e4a8ca1d6e36aa0e59c8780a11200984 -13869,0xe6cdf21a157d7e2c8dc8daccb663ab674a25ea77 -14343,0xe700e001c969c136960eab517fda11c4ff17906d -14471,0xe715959dd5313a74da1653f63ef800ce2f71eda8 -13812,0xe80ffe6971c4e646374cf523f8047cbdadc92b87 -14636,0xe8128639c3cf0b46c567d0ad9cbef6047caae181 -14026,0xe82db4e747ac9245ee6020cdabe92a9eed4d949e -13981,0xe8ad081b97c547cf9c65a315f53d36036760be13 -14497,0xe8edfa5ecb14e96dc1d0c6bec371f9d30372be92 -14408,0xe90ee8accfcbe348a4b1fdc22bb148507c090d45 -13748,0xe99d0fbe203e3697285166f599d8e57f8e335ed0 -13824,0xea17d46e4e6bcf6b550435711ac609420f21088b -13939,0xea5133a413099b55c3b1538c3b19c5e80d8305e6 -14171,0xea5c68c9e1f46d9153fdba5afc6d371a04c434cd -14364,0xea9d55e9e92b7fbfd5cf07b05311dbec9febc716 -14399,0xeb883c35a1c2b4702fdc58e3ce2aa198f19c9b40 -13902,0xebbd6c12c341380c073f5299cebaa673ddd0190e -14580,0xec5d8b20073b1adb5830fdb3cb5cfcd6b097b250 -14207,0xecd4aa6d40df0ee749d7e0bf1bc62b5052dd36d5 -14559,0xed2a438838c8c3dada708c9bf2a72eb064da3e9c -13673,0xed54bbfdba40c9390f8b9b0174811cacc442fdf8 -14393,0xed55c0c9c59c09b8917615bb172e2fe907c1c896 -13779,0xedb35a19d3bbef1be77121be0c5765fbe28e43f0 -14140,0xedcd8fb2ad745f15ac17aa55369dccc91a263436 -13872,0xede9e83f1e01c902b5dfc5cea5af0fd67a0d4fe7 -14593,0xee1adaea27e97440df594d601e366b3a1f1703dd -14572,0xee3873fbc86467b01a1c8362d6e99c81bfa16f25 -14370,0xee8945c707357fc5abac9ef481d4bec9205f0c60 -14502,0xef3011899600fc213f7d7c8662da1850c7caa4a2 -14088,0xefad5bf597ee676293cd0e8ecf7e33ee41a7b33c -14275,0xefaf7ceaaaa97505c134efa8f4a2d7d185b989f8 -14178,0xf069e80b861a1f35aaac515f883b264031cc7bfc -14004,0xf08d00cce7747ec52a34daba027e3eecb5f54846 -14542,0xf0a18c27d26a319679e59b3a5797668006fd032f -14247,0xf127fdb858f009938b4530aac37e5bc8e9a09c28 -14524,0xf1ba37773e1096274367c75d65af6d001a9964dd -13927,0xf1dc2664725f89c550f84596ba9ac0ce5b8c796d -13761,0xf1ee3b9882142c4547b2a533cdc9feef67af2b49 -13941,0xf2295414bb8d2004d4501beed78b92a7230dc179 -14251,0xf25fd268eb3206b1989c61c084b6a305a5c4e85d -13807,0xf296a56ca4b1cc3f58006de21f81684468120814 -13822,0xf2c886623309c93b56537c8c4d4321813ef78439 -14352,0xf2db8098151510aad13b418936daa456ea18e49c -13787,0xf2ea31d78a10b30191e46ac65df405201e552148 -13885,0xf31861ef4e7bfb96503e10bb9a5e0060915d3697 -13784,0xf37db3d4a689dbbd47dee4d217cc1a9a2721a64f -14472,0xf38bc41ada278a8de82ac7c6cbc3e8f33770c3f4 -14234,0xf3a4f9ba57d5f90ffef0e2c1879a3e5a103c88c3 -13729,0xf3b187d01c0fee921762ee774f9bb9fc6254e63b -14154,0xf42f803b13f8cbe4e29644790bddd8ff468d56d0 -13677,0xf44e50784264d4f227fbf426484a9645704367b2 -13883,0xf482304d9d9fc5629e45f4077a3a2dbceda15890 -14326,0xf4868d747a8010deaf3ff30515070c08d41814a2 -13952,0xf4b3e7b39c149da1f412b9b2f124720c932798db -14300,0xf6129094ccc7e69c7cd5bd84fa42bc62274f5708 -13655,0xf664e938de3f58b7ba5307076d6bbab027a2b112 -13861,0xf68b6136c2ec043035afb1cd2bc84370a55e4752 -14260,0xf6ab52b700f73672d67887802fe15e5e11248927 -13979,0xf6d5ea28ce5056ba74c08c53c6c3bb5613ae9427 -13783,0xf6d70e9d1a71c00842c396ae66d7919d46d727ee -13911,0xf70174619f067ee8ccd5265712a407cc34701d0b -14458,0xf70c85165c1728f4780ef5caf66705c382c32c6e -13706,0xf7ab0a71c8b33652ebb55c3ee80e79c84f3e600b -14290,0xf7ab214687e036935f78d9c6437b7f60c0b828fc -14108,0xf80ea364c7ad4c34657bcc049e074e0b6b072547 -14068,0xf81cb8de7dd8fb3e5261bde6ad350bc8777ef3a3 -13788,0xf8c77e09a1991ad0ae790aef6a13bfdc9501c211 -14401,0xf929359a77ac765c9df38a4b15c1b2d18a971b0b -14008,0xf92c0eb708b5182ed2288f21d36338f83f72714f -13792,0xf9d9e828a2965418a2fa04ae78a6cb904816fd6f -14220,0xfa18c6cc9a05a8d5a54c248b716c4cd33e64c973 -13963,0xfabf3031ccb57a65f936881412ade8d4b138c0e5 -14297,0xfb30308ddd98d16f9cbef69ea140fa4725e257af -14365,0xfb413a935cdc0cc2b5f0c97dfbf60bb91d749fda -14334,0xfb8627c9ff2bf7c9f524c15b4d0a582dfecd1571 -13826,0xfbdc8af4f55985b577c62cdb5d565e1031a1968c -14629,0xfce49c4ce3c8b6722f1edf18c449fd069b5d4d63 -14235,0xfd2236efeee110b90d63e7171d8c1cfaa26cdc3d -14089,0xfd486adfb5ab594083c85f4fc3b10f7a34ac0deb -14453,0xfd7bb5f6844a43c5469c972640eddfa99597a547 -13801,0xfd849d5df5cb4b631ad595b0e9ff70a1a66c743f -13991,0xfda08aef411a83140f6ac5fa516ff37482d5e5cb -14021,0xfdadab426e4505030b958f17fa41547e018e3836 -14549,0xfdeffacdd1186e3a40a142e366b6f0f36e14b54e -14641,0xfe234f934ddd6bf00e2520a570529fe7f0a40817 -13998,0xfecfd57ff916ffe90debcf8a1ab795d9ee2a08d7 -14061,0xfefbe7f7f619b1bdb7cb9194c70e24bb1779d572 -14067,0xff20372c23c9462704f50be915083f32df69605e -13896,0xff254a9363f7c75653e7d3c321ffc5af4c86bf9d -14768,0x0927fd43a7a87e3e8b81df2c44b03c4756849f6d -14765,0x0aa8b0ef37c482ff80f9d214f9e09b2aef089265 -14742,0x13bae69a10c013e21824876bc500735162e2e4cf -14748,0x2637c588c7ca5138032992c97f437f55bd9fc782 -14744,0x2cdcce2b00978bc8788f4c7db8216f09f5d33df1 -14752,0x2de92a5de61feb225f411f0270ddc9f65d043521 -14757,0x4669d27a649f5451e0d44c20a2b246431f1b0572 -14747,0x4ae8ad57912e207f5bfafbf2e90486e5feab1634 -14761,0x50b89fa00b4513c2f58162427b9f26a5276a074c -14754,0x554221873a75bf921e0aaa6a7564bb1e78f50f8f -14758,0x6295cd918b48e1d2a96cbae2fc70b29623c593c2 -14755,0x680baedaf5ac0a90fe279d0fc26efa591735f9d0 -14749,0x6e7be86000df697facf4396efd2ae2c322165dc3 -14760,0x73740363e361e8c49f42c90d17547b4a18151473 -14771,0x8547bd5fb04737dd879d76472164eb25c47fc40b -14745,0xa2f581b012e0f2dccde86fcbfb529f4ac5dd4983 -14764,0xa7465ccd97899edcf11c56d2d26b49125674e45f -14750,0xaaca165fd5abb6564098e8595bc836470f19e209 -14746,0xb41dd984730daf82f5c41489e21ac79d5e3b61bc -14762,0xb6ebbb9f9957556fd798927ae7c250759a235407 -14741,0xbc30af9080cf750fb96ff831b7b1c52e5b604ac7 -14751,0xce4f05e51b7edb3454134306a84864d35d6ff734 -14769,0xd63b7691dd98fa89a2ea5e1604700489c585aa7b -14756,0xdc6e2b14260f972ad4e5a31c68294fba7e720701 -14770,0xe2137168cda486a2555e16c597905854c84f9127 -14740,0xeef30844023b355408c44224b9d4031609d316d4 -14763,0xf298381a2b6b072f1998ee4eb261101f2a9f9516 -14759,0xf3938337f7294fef84e9b2c6d548a93f956cc281 -14772,0xf3a92c8aea760a3af04a8ee59017cb9b6b834f18 -14753,0xf9234cb08edb93c0d4a4d4c70cc3ffd070e78e07 -14743,0xfe65c468b18e620e07cfd30b33ae642ec335bec9 -15221,0x005980270c2ad8728e8479ac3b1b25d8671f46b2 -15338,0x006a0f4ca319363c668c639fb999a170fa5808e8 -15383,0x00c89522f416602d16c98e73c8573d4fadc385a7 -15320,0x01092b6966725099b0105c2925df91d1f026512d -14962,0x02803f6fcb3eda19c854b2defb39cbc3817ed986 -15151,0x028adfc6e1a569720dcffc6e36ffc4278627e740 -15546,0x0296b27c2041d55c7220b05aa2079ef1e8e3b60a -15522,0x02b7a3914e4bfa632c5ce2962adb0cfe46048b32 -14894,0x0399588d9c12dbc0b8a54ae814ccb3ed4b22d6a2 -15114,0x039d778af09e9b71a39f1ba09609b529131090bc -15504,0x03a527483ae3ee08173fc410e3d9fd1cb5f29509 -14824,0x03afe3617e5251b976b9d20a3efb68eab7799479 -15171,0x03df70538334f98a5fb71dd6341d3bc6dd104b9e -15216,0x03f4951afdf7b92f4c0a814d03dbdf4fabcc0027 -15466,0x054ea6e84c4ab0017b210dfadb34dfd6d36eea22 -15392,0x056c0c732bd61714284e74cc5117b768d90107d8 -14969,0x05a997d46aab1888ecfc965434170df7019fdc56 -15572,0x05c07b7462cad6250aa8cfcd5da43ec90c9244f2 -15514,0x05e85b0ce11b874f3d8c8b5c65f2468aa29f63be -15090,0x062d8e9189230601329a0ca09fe8de691a3e92d0 -15302,0x06468f03c7eed2507936d1e90d9a3359d91d9cd8 -15628,0x06694d67f5d70c55797c5a28035249eceb9118e3 -14955,0x071ddc318b180cf4312456b8d328e3c245197c01 -14903,0x07b8a053cc6ca00a54f8175b1742860470a57332 -15294,0x07ff385c5e60ebca5fe9843461b37f72b8c6f089 -15078,0x080e46dbab07e9d0356af6a6d91168e4c42d8ec9 -15082,0x081fc004df1f88e59840ec04fb0ef1e4b0854064 -14930,0x085c873559359886e33f7469133a820edccd6a16 -15637,0x088b22502d3df4a55c73e341f6990114b629d074 -15443,0x08c742924315b5512620bb6380d47b39f9410807 -15536,0x08da988f914bfbaccf1e8e28b44b42c135bad0f3 -15645,0x0914663038b0c15d92f3bec226bd13880c25cb63 -15465,0x092ab9b8897dc38b7c3f472e372c3ce81b9b51ea -15535,0x09aee63ea7b3c81cce0e3b047acb2878e1135ee5 -15245,0x09b83424385d34367acbd3a657df9352e5e9b13e -14988,0x09c91107182e2234dd24d170b3705fb8379ac55c -14923,0x09d05ffdd9ab48bb397e4a49299a29b66d1f7cff -15553,0x0a1d6ca79cf1a346c458fd21511d5d5509817401 -15574,0x0a5dd3a7e54150bceb8e93876480be0537c6e6de -15165,0x0b22af2a9c1ab06f9e172c911a006aa2ef394d34 -15109,0x0b287606f1867be9d0435089cb08faa16109d31d -15408,0x0b874d3f3dbb29b7925321f3fd8251dc8d28db46 -14952,0x0b8aff85d63c77fdfdc5a0ec93db691d506366b0 -15614,0x0b8f31480249cc717081928b8af733f45f6915bb -15102,0x0bdaad500c5839d20e9d9268d841d8d081d7f692 -15400,0x0c13ca908cdf6a49cf3d17f2aff419d45fc590b6 -15084,0x0c4d1201edda4656b68553294e3193e368bc8bf3 -15440,0x0c8b81de57abdbb5af09beaa5d84d83de1bf53c6 -15199,0x0cadbd2c6e91f78aeb5d76ecd1cfc8bad289f9c2 -15519,0x0cbb6e12ca925ba5eb484d7b2edaa3c0702b26f6 -15599,0x0dfc36e099fefd886c210ae8f8ebda981d75be5f -15126,0x0e2155261e464c6d9f5dcea4cd184ee0632959cb -15017,0x0e2b2e48d5d10a5ac71c6d468cd7f92a962a5679 -15088,0x0e4d1db5f9879e8e64e8b012403163ebbaa05507 -15208,0x0edcb417cc15fe46d0f7f760b3b41f3b08ce0a88 -14972,0x0f25f66138b5c9e1c6f532e83f5dcd10068e8967 -15159,0x0f3af405ff7754875c8f2a4209959f2aa606783a -14951,0x0f6c2c868b94ca6f00f77674009b34e0c9e67db8 -15163,0x0fbba9778386eb874dd937feee98ab9cff2802bc -14944,0x0fe7aa84db235656d09c5b41dead2784efda3b39 -14933,0x10392eafb5bf939198388c8da0712fcdcac8b496 -15423,0x103ba715376ce6116be51e3317fbbf3c4128c498 -15057,0x11070153026f3fefb9d752c0f3d03eefcf3da21f -15554,0x112a87f97e6e43908d5f70873e809355379c8751 -15153,0x1136e80d0fee7ed17d9dfcdd681174e8c6f8514a -14831,0x114fde2581921cb54d7f48e4abd010fe5e03b6fa -15010,0x11732e21d9dab3b6ff6e7dd9edcb24770260c7b4 -14809,0x119e1a84b0f1436a270c3f0ac24b6463c3b26cdd -15598,0x11ba5ac369a2e7ad44bab98b83433fd2329d3070 -15357,0x11d70858d7cb66ced1bdae06759284ef08f29cf8 -15164,0x14172babb0cba753fc3aafd0f5b92d7373ff546f -15570,0x1459e45794f5a784613682af42f86793038eeab1 -14982,0x151a2259bffa3f49c091638827701b012f738bfd -15525,0x154e2d188c5c9a25dd66dc268a578affc72f188d -14850,0x1595eefcebe3f4c24a4afad14962cbd31c02420d -15606,0x159f28f598b5c5340d6a902d34eb373d30499660 -15384,0x15f61d989df86bcfb86847a08699ceac6c90363a -15407,0x16114ccf0e3cb07df4e8b6178e5cb896fb69692d -15278,0x16c35dbf74c824c53f71b1582ef61ddbaf19e672 -15008,0x1731100aa1469562f82f5b2d7be282eac189a63d -15323,0x17fb7b4f85ed246a0df7a8caa5de7f3dfdec9751 -14855,0x188ebde9b486b8e4f4cd1a60af64d6efe5f80762 -15511,0x18df2ead431595da94d75b0d1d444e771de6c90a -14836,0x18dfd867a29fa57d5cf23ad0eb9f2cf477b488df -15085,0x18ec79b8b78a196704d60bbf9af9ba9899679e5a -15269,0x1946cb50db9aa631b60b5b54623eab3bb84d6d3e -15486,0x196f418ec6ede5ee06497191c1e9d06c55d9f797 -15568,0x19c0f0388521f68ca73dd02622943202f9e6b461 -15526,0x1a5448b01aa119c976476140e8715eebacc71ca4 -15456,0x1a8bf92abe1de4bdbf5fb8af223ec5fedceffb76 -15116,0x1a9d09dc11a4d97fe681bea763ef3f741245bf4b -14825,0x1ac4cea131e1479903011fa14e0da499bf239896 -15100,0x1b3b7b72e0bbff68c1311459218aef5de5716c53 -15398,0x1b797450434e0deda4d2c3198eee1d677d3dce4c -15306,0x1bdf6a3709453cca9c5cb6fdcc4de232835043a1 -15581,0x1c002b626db96e0f37b1a090de14cef00d2be970 -15575,0x1c1784ebee596345495dcf8f1ffd65138d3fd9c8 -15560,0x1c1ff6c58bada787ea5361ca33c20968a72cf184 -15380,0x1c463a66989ac051bcbe139d34ecdf0efeeafc19 -15173,0x1c5888e286672bfb50977445ad5e41eb081f7dd4 -15527,0x1c5a86ccc329d3b4b05ba6bdc7deadcabba0ce8f -15509,0x1d5dabace0840bebb2a86601abbfafa6dad856cb -15421,0x1e1f509963a6d33e169d9497b11c7dbfe73b7f13 -15490,0x1e5a3216d1d3d245ba332037898191ac2f848131 -15462,0x1e70f75f32af8241cf264204b770215ecfb3c228 -14832,0x1f4947cd5a5c058dd5ea6fd1ccd5c311ada9e6fb -15600,0x1fe9842d55558312f20c9d9732272d2584146c7d -15202,0x200999ae8e790f5940ca9bb9a0898708b86437f6 -15071,0x20c9c8a3f512f6c50ce04ed72a55c20ef4de646a -15129,0x210d2a3e81853fc5f12c8439184eb635055c5104 -15081,0x211efbaad81378e8dd161b802ce938b8b053dc10 -15420,0x2170b16087f0a54dc9744ec4c2124674fee7cbcf -15330,0x22209d0c5e95161328851f28293fdb6b192dc6fd -15310,0x22289a8d65bd3460926771dcc914635bd8c0fabf -14874,0x22c40e877ae928e90d0fde276973bacee2f4047b -15166,0x22e498bfc141908d4ebfb26cdf23ab2de8d2c966 -15013,0x230701ebf285371ebef31b39083bcb88a6886b1d -15454,0x237acbdc9e0739f155c62290c44e9b878ee1dd7d -15132,0x23afac0e3dbb563e2d813cc26f3bccca9ff0b516 -14946,0x240ed84185f56c45b72601c758f6861451b052b7 -14864,0x2427db2b6a709899cc6605675dacff8f79e4d788 -15226,0x24a6da7342d331a9103a77673966547ee9f0fae9 -15595,0x24f06e7b44426235ff097cb098f0e78e90f12a09 -14847,0x2533a096f9716c9403a59d7a1877581d1cfc8ac9 -15336,0x25587a637ac2de0461196b40c6ecbe072867ba16 -15617,0x256175dcd8ed54e5e5a31a8692736b0a64a52234 -15475,0x2602848d8eb53aa9bff5b41b38656439c15dc72a -15468,0x26d2008d059f962f51989424017e06c92ffc4404 -14815,0x270df474f4bd2b92a45a46228683c971765e81a7 -14811,0x27689d375f97b8b64a314b2c0c1f7079e40a267a -15446,0x27cce547762fa29363b42bd20b95d2d02b0e2da7 -15242,0x28ad29c59538a98efb65c1f6e300670a1a24ab25 -14997,0x28ef6f2dd9b5f9740f0f87c47127366c89167ec8 -15051,0x28f16c9674671e2d6915518c67c99f93e4afe9ee -15287,0x29045d62b0ca66d4bd99c72c6f3170387a82f4a1 -14922,0x291120ffa233285f82a741a2970faf246fe5e88c -15550,0x2955ba0fa44202090d840d36b2cae53036d018ec -14835,0x29664f19ea2fdadfe262c6057cdc8bc3bad44299 -15005,0x299d31ced50a52fcfc20ae7868c09e9611cd31db -15382,0x2a382ce0dd0e667724bdeeca72a96a96337fcdd3 -15308,0x2a52b4e4eaaf6e1cefbceff8c0289929f2de4c49 -15281,0x2b44d548ad411ad5ec00a89c3df41e62f8743900 -15167,0x2b54a380a67df852786085ec91eb0c7c97357b26 -14945,0x2b60ffd309929af6be194b1f9428b63980f9edcf -15396,0x2b65fb73a3fb0e738bbe0726754801bb422fad6d -15169,0x2b8b1f84ecd146f709b8b869cbb03fe190f6fa1a -15222,0x2ba4cc675f2d80aa812cc465c13ff0e5484e629f -14939,0x2bb38c2a8b049150ca52b01b82ce037c56fa2f30 -14807,0x2c12f1d0404080f65c5352b55de3cacb3ff9c360 -15530,0x2c80d9ee6f42a9af2f681fe569ab409df3aa46f7 -15641,0x2cc07397052e6e5b0c91a2b734a761d6d30ff20b -14994,0x2d4ebfae4d8ae622c5a7216a638fcecdcca32bd1 -15537,0x2e7a13dd36ca9111d45e88d55ccdc0f2877b051e -15481,0x2e80122b1a095c25aa5717b2be8dc1eafe9c8850 -15591,0x2f72b885eca39fe8eee67b25791944e024c8a66c -15521,0x2f7eca37123f70fd60d9339c622b14dd0e515b7e -15270,0x2fe05d910d4367b9b8bde0f4fddbed4352c21a75 -15596,0x300d82d020bc323188912e06e6c39283c0c89cda -14971,0x304d04acf5637d01588e51100c6b6fdee1b1f4ef -15232,0x307418340f5991cd895ca0fc4eba04995e9be861 -15555,0x30e5f417f9a4c2bfeee6c29b94303533ab57e7a3 -15597,0x3104212d74842ddb6e56970a6942b7bfc51c9625 -14917,0x314768ad11925aab3fbd5755169086c6afff9e0c -15492,0x314b7e649d15c6bd0872415edd0c1ee5e25e1d67 -15275,0x318bd91dae9f5e091bb925f00f19faa0b4214fca -15234,0x3198ebc019adfdb23ab60876767c9113c6a5d862 -14978,0x3271371d5ad147140bf321b6ed859cc02afce973 -15562,0x32b57ecf47ce8534b0cfd450fd78a1bc1140d8b1 -15054,0x32de3ec68eaf91a44d06a8c424527a9836e31ece -15301,0x33227153345c71dc3552af7cba3af07c7b7b762b -15375,0x336d5806a2d629193c3d724143b60a3cc897c325 -15386,0x33780001580ebed03362dc07372047523eaaf0fd -15417,0x33b15a1b4e7bbecfa2e67318d661ece53421d72a -15091,0x340372e89ba6e021a93716944dc2de36e360f346 -15291,0x345db956656fe91cf65a47e33347bd05618b20cb -15044,0x34e5fa15ae262d6d95b58b0a307afcc9faa23268 -14991,0x353d08666e22269561ed78389b9c4149bf774665 -15603,0x362c4d308515e69f3205bc698135512d87417ef6 -15120,0x36ca37949bc6b94d97d49a7898f9f259f8021af7 -14921,0x36ed49c698747f4f6b12880dc4e8083b112469be -15052,0x36f6bd0bd0456166fe9fd0d6671aaccc4884dced -15442,0x3765ef51c4291e15a07b398a0b45d3c54329eacd -14898,0x3788a0dd07cbe13ab5e1de8514b299815c8d02d2 -14990,0x378a7da7179f8f98b3cf3e87d8a6c3e6cdf3589b -15494,0x37a68c23b496bda8acd837f07405280528cb39f0 -15093,0x37d52c8cec27797c060b854b151c9126e42c5912 -15030,0x37e8c03e142c74e398223ea66a76e8716e0bc18f -15624,0x37fd36b65fd163876deb8d67de6cfb478126bb76 -15112,0x383a9c5f124be993852435074169a418b7aac182 -15138,0x397f553495d8f7fe21b43b5aa0030e1d1cc6b0f1 -15240,0x39a6ca4bbe0906e7ad4ebfc2d264a57420d54312 -15556,0x39cb0f37cdc83d2478d10dadec362bf3e8468c46 -15264,0x3a15cecb9b19a89c28bda5145334a00153013f2e -15011,0x3a16cbe7caa20eae7c0f6702928ef13f218d1fef -15652,0x3a18062f12939658ac8c020a0443a388db00ea42 -14872,0x3a3ae3313959e102fb1a66678b0b3fcfc40d8742 -15256,0x3a3cb24603138fa89f030192b8b364113f81408d -15605,0x3afba15c076e5c393315c2dd957f3f336163037b -15653,0x3b08fcd15280e7b5a6e404c4abb87f7c774d1b2e -15258,0x3bd1792bc3f8a6727285fe12db510e4cbdeea7e6 -15397,0x3bd22a13cc45c063a5872332f83afb905110cd3c -15289,0x3d363e188949d53ff900e8927a7e0fa8dad2e5a9 -15351,0x3db9a167177f35428b1d09902d44a069a76c9673 -14900,0x3ddd035b53035e5e72b5e3d36685f4e28893e09d -15317,0x3de411bcd6bd435f717178e88ceb01a1f097664e -15578,0x3e7d976dd0d5f60afff7b949fcbca412e040bc73 -15437,0x3f0225051e0145b27a38c8ba36427a1daab4c0a4 -15493,0x3f1b4e4fa31e135ab51d6b3aff36ac7b821be89b -15496,0x3f401cfdff59047b9eb0f07d844961d13415445d -14893,0x3f6ee2840b7d50053a489c05c0d319bc6a497b30 -15266,0x3fb98bf8c413914e796a09583fb90f9317a11fc9 -14867,0x40d993afa2f4ec9db718bcb8476bb1cb69c70af2 -14863,0x413fa8a46bb9401875d20c321c8a5901ae0dff27 -15274,0x419a6fe3caca968785ab6611335a283da9815e9e -15464,0x41b7237436a3f55fd383c1848c6d5ee94cce6135 -15022,0x420b3da53ff4c66d818af07fac867cd5b0d2cf33 -15204,0x4332b77f3c8c5b5a650555b01a404f8b4bc272a7 -15058,0x4348623da032eb4a66e6e851ea8580896ddc2e2f -15431,0x435a1a3acb128d511df896c8ef0216bb3ed075a0 -15300,0x435f7a1c9f353d61ff2d70299e8b300103610708 -15288,0x4395ab761ebaa01ec22940f32568845c2a8baf3e -15259,0x441e32bfea88bf127a6688712b80b03dc0467685 -15549,0x44842b7b3f2744f0af958858cec08b6761da2a9d -15497,0x448e87779345cc2a4b3772dfd0f63200837b2615 -14876,0x44a07ee2cd11d16ff65a79132745ebc06180bfea -15648,0x44bb1f5eea87cd910505eea6ad7eac6e14eee1bb -15477,0x44c4b5115eafe70c661d35583f312bb229a77cef -15337,0x44c8704896de8e011b8fce33cdc610d863e0bfde -14929,0x451458fb1710eec3af598ce4d93a68f94971f61e -15391,0x4526234308a938ff528d27235f5dac7ded9445f3 -15092,0x4594b02860c3f4738c7c863faaffd6f41ee3e50f -14897,0x46c4e3ffbf4894bb3653fce48f464783c8109074 -14890,0x48c17f4f1fc439a4e138b83a11664d760d3a4761 -15483,0x4957653e0fdd3ea8a76577b81e12e89dd3e56f8e -15225,0x496b901f9b251874bf73004df0c27b2eb9949e95 -14883,0x4a909528563ec3494c1acae3abef68f6d281e455 -14846,0x4aa2172b7c6359e0f5050b16e0fc815419220cf4 -15185,0x4aecd2473ecb676353d40fcbed929b2a92b9982b -15627,0x4b2e93ea678b755deb3cd29868f8a47d8f58b595 -14828,0x4b34af19c2413239b529aecf420a77c6859bb0cf -15430,0x4b9de45d62f0321e247ca1b2d1c382b34f42e25a -15412,0x4c2f2b34d30ca15a9641c748f87b8e0a3f64a2b3 -15190,0x4cd5d0ce882b391bb6ab35c7e530ba9d4c4666fa -14974,0x4ce19c33b477250fe9c50172f6125cf480259372 -15209,0x4cf9f9075225591f2a4a8c024bd6f3c2b8d7fe11 -15518,0x4d90512709935461f41c01db60267c9a4ac9c924 -15255,0x4de1a5746be0b5df757fa2b14c4125cd9c828023 -15083,0x4de6bc3934d3b86a183e857c3c8cdd3e5b3013c3 -14987,0x4e065f6128659b92343dbeab449ed9a2ac0a72fb -14813,0x4e2c93101f170aee0bb6ae4583f9b154236d85d0 -15389,0x4e55b26f3be9e954ab26ca0fc28cce8498310cb9 -15127,0x4e72010c179ec0cbf4af10252cd772de059aa3d8 -15424,0x4eba4d02a24564c8483d4acd9076b8008ef26031 -15319,0x4f7dda94b10b2414b7eeb2fb4152de2d6c91b0f9 -15573,0x4fdcc7e6185b1dfb349f3f84c2faeccdd2f95ac0 -14904,0x50048c396e821e5f881c6d0e0b616945826124bc -15399,0x50f3aa4c396bb3ebcbe9c494219c2368882f7d51 -14869,0x511c845346a7bb344bcdd7f5a032c536d0675bd4 -15094,0x51b34d541daa3a5195eda380ab1e1ec5d71aefe0 -15646,0x5226e549371d2a376a58c142c0f389e3093070cf -15304,0x52664dce9e0fc9ab63d148ceeec365b93690a985 -14960,0x52a69e45684ad3599fbb1edc27add0cab6b4afb3 -15315,0x52a8f84672b9778632f98478b4dcfa2efb7e3247 -14975,0x52b2ccddabe5c346c1a0c929a31a153125f251ea -14979,0x52c7cccd0554f8001fd68d579c00f362de0d3e29 -15273,0x52fa472b1cecd98cdabe02181fad811e4f9b675f -15003,0x5335e87930b410b8c5bb4d43c3360aca15ec0c8c -14963,0x534e230ef2e5335fd3c37d5e60c4404ffcd2e6eb -15106,0x5359781abf0362d7b259136751e718192a7763f7 -15413,0x537a7121ddb334594ff30335af95bf79125b127b -14860,0x53b5932caad2b8d62199e092b933d9892ea2c8c3 -14981,0x53f3d5913b67ec6c0c29e3bf81a1d09f09a7f010 -15177,0x53fa698a267a2d5408fde161e3ce371cb494ca74 -15561,0x542bde36670d066d9386bd7b174cc81199b2e6a7 -15118,0x543fb6bdc03e66217b67d18edc60064d38988e13 -15636,0x54403d59e8856d0fb4763ed92aa843f0cac6aa88 -15260,0x544c09424dfc2865c05dedcadf5801e09a1f8dcc -15064,0x54c4e51e699335e2b00597e0d89410c1f2eccce5 -15449,0x5518ed1dd612742e3369336ecc0fb22d94942725 -15363,0x55730bdf319554369415219f685984fd310fd548 -15474,0x5651d18f066e132bb90102954429aef1a636576f -15621,0x56fad4f0095b35fd74ee8be48412df49bf90f29e -15303,0x57d023ea9771f1bd007ccb1186403f4c4def5d06 -15146,0x5885e81c9e509c95366f63fd4608fba73c545cea -15247,0x58ca697e2e58a56c79b30971ac3c60b9b2c5cc97 -14839,0x58e1c54820048acba5f83515f6bce8468ab5a2f0 -15487,0x58ef93a402625ecd8c83de531488bc87b56fa5b6 -15015,0x5904847b6040e95fc6c5a9f165c7c2dabbe1df7d -14892,0x59c12c4014cdcf47a0b89a7a386ce75b417e1361 -15592,0x5a5c6aa6164750b530b8f7658b827163b3549a4d -15049,0x5a7f7418b2be069118287dda13c77a3628c471aa -14849,0x5aa5bf90c86986c61af2942342156133a956f286 -15544,0x5aeaf164e528cd827c56f888c35f870f37c13ea3 -15410,0x5aff5ff3b0190ec73a956b3aafe57c3b85d35b37 -15276,0x5b17f800def53fb26b7827732b0b2a28dea566ed -15441,0x5b41b3b03f5af12f64c88b19ddcddcb7dcac958f -15360,0x5b4f6f099d5178209e032644b1184bfe906de836 -15364,0x5bb0fabb12d3273584e6b6f5bfa2ac0f9b80cab9 -15602,0x5c8758966d0877851dcf0ddaa924f50892cb0c3d -15045,0x5cd76144e9c95c43e4d1a7f897889f60802a6295 -15404,0x5cea15f9a98f86cbca9cb3262c8f49ca45089fc9 -15047,0x5cfcf1e0e604430ba0d2368568c9db31bf91b4db -14950,0x5d5f135eeb56c139502543a91d257959d8e3e1b3 -15119,0x5d77f6103a5b76497c20ea5f3e7deadff7d31cbb -14961,0x5d9841e343d0a8529db2c4f3cfbfd3ee652ec645 -15077,0x5de3cb7d633f747e8ecd5c00be84ba9c6e0d1698 -15502,0x5e040e824d48d7759dfe2d6c6de6998407e9bd32 -15452,0x5ef0f3f3ba2e6d146469baa18e47e7f199db23fc -15140,0x5f4623f3330da4b05e0cce66d664a2beb189199e -14859,0x5f5037b9994b693414e27a4e09a6447cfe74123a -14976,0x5f856fd4c4c864830bffdfe26d886f4418da49e7 -15630,0x5fa864c6a281db4ba36f8aaabc56265b13b547f5 -15280,0x5fb01a44421022442661ead20d58996c232f2e06 -15395,0x601475413fe7570e9c1b13bc95d7410c6e6ccb75 -15111,0x6017b35b34617f9acd2635c76e35ee06e068cbe4 -14925,0x6035d915eb7e0e79726830e39c41b23bc52e4f4e -14888,0x610ef429314fd12e5bb838579f88c2f52269a7c2 -15345,0x611aabb813dd868c600383d1bd4ca901de37a502 -15026,0x61283adc0a841800d609394fd0884607f6cf4afc -15447,0x618cac699266e795d4045d1136b1b3dddc2eb267 -14866,0x61c130dffe20e1f9e579cc73c286826a4c7b8785 -15461,0x61dd52ec3c305b374244135d5059ce3c9beb2f97 -15450,0x61eff9dbf8d7722a26caadef7c2b65757d01cbf7 -15223,0x61f657547f7ed2f238c99468106cd555fe80cbd3 -14928,0x62bf6476f98a90d596f2af4f2e436731c6fa6bec -15616,0x62f76af974afab69baa4bc8b4d00156a72568b41 -15508,0x6380309a2b623eb4580850f4cdd3b1ed4087deaf -15436,0x6389d1f4b50f06c19a63a8f80bc67804f5d77e5d -15428,0x63989ee46a2e6c538151ec7f41b9406686278a26 -15072,0x651fae2daa54cda64c7405ce2ee94f888d9fcd5c -15121,0x656e9c676180fb9de36fc858f8478536ad3e3483 -15348,0x65aaa700ea8cf28f5693dac613ce8d76541f5afd -15012,0x6614ab4e6c02dc88fd6bb161a55524ecb4f6393d -14931,0x662b8ba72476494009f9596275abf974d5d327f0 -14843,0x66834fa5775523bae46c4576a74931d6c5f83b1c -15548,0x66f534ef4341b1d857585861e265f99e215c22b4 -15604,0x675bde1c18dbe336c8fa5cc94cca3eaefabbfe87 -15252,0x6779a74c1e6009549837d6dd4b4edffa1cbef70c -15515,0x67f4d2332b2985816e95cfe0179d885d119aa5df -15067,0x680fc24418b905a7450d720121aa621fe2b20bc4 -15593,0x68beed6a5144c33afb37c79370ec4d299ca72665 -15043,0x691e330721cec17e8a3addbee2252d0ddd5b0f00 -15329,0x695368a1a3b2acc6b9ac3c84ef8881cab6b5a58d -15335,0x69919410d826e5f378506a9521496e87f2bb7704 -15620,0x69f73581db179da64e7a5338de617d9f91600898 -15367,0x6a0eb9fe1b7cd01972fef2fd2dc4824ae640233e -15613,0x6a1a3d000f0872cf2af9b8db20f50af56366d7a3 -14999,0x6a9d96f5eaca97d61ad8f82c98591462af9a7fc8 -14902,0x6af808ebc532012e1d1906d4c859e1686c5d0bce -15161,0x6b2303bbb4f872fd26add9e42543fd5903eba332 -15096,0x6b3cabb57c12de771022669ef40294d39b4445bb -14885,0x6b4313a412d2cdef9317aaadc0fd518f60889443 -15184,0x6b43ed9671a6ba327354a349bacb22e294ff1b2a -15378,0x6c15d7a3218ed11b7ff8f47c2c752b569e5f4389 -15149,0x6c5b309628d4e572630a86530a8a22414ff43d15 -15261,0x6c6f25f2727351eb244fdcae24a68f3b8bea01ee -15037,0x6c93a2a9ebc61ce664ee3d44531b76365150bfd8 -15342,0x6cd26db69ebe1334173b035dc2148b2a00dba7a2 -14899,0x6cdfd6872adeeb0d0dc708cedf6f89de62abe3a4 -14973,0x6d1dd0e9f8961121b9a936dbc3f79383c7605b56 -15253,0x6d2eaec2f07358e50133002b112e9e9ca5c8314f -15038,0x6d3f86a678d221d611c20abe6d65258990e980eb -15143,0x6d774e67e7e1a09c05499b1d516b7b77685dabb5 -15377,0x6e406b69f99fdb0c67667ee82d9dbf7c02f43482 -15195,0x6e5f9368a95d5a81d6a49250dd3d64e4f01f2135 -15099,0x6ead6c6788aa2d5b8bb95efdc12a9e8d48045633 -15235,0x6eb517e2e12c29ea77c8f3b65c7eec200de05ccb -15283,0x6eca9df4528a00cb0d3715116d4d5629bfb14c7a -15158,0x6f0ea9a8c82b10723c406bc377a5b890638e420b -15318,0x6f330902465b7e8ef7ef2d340e84d67aed4a0558 -14851,0x6f5cea8464a72c5a50dd211478c8363db380a6cd -14983,0x701e4b49497098080ce99545b4277819529f573e -15651,0x7067903fb5501102442bb5cc0a8e6f5ceb391be0 -14844,0x717d9f265284dac01080b4f0d3d703d07042e1ec -15557,0x71e8b8c242fee8cb8fdffaf4894f7840ee2c69ea -15340,0x739a5d55754d2ad1e019d8b1b1bde1ab08813c4f -15186,0x73cb180bf0521828d8849bc8cf2b920918e23032 -15147,0x73e602c2ba547bd8f27bad223551ef0f5ac5ca70 -14865,0x74e18efd99a16b124fc3e5b475f9eff9673254a0 -15019,0x75081fbb720f3b6aacc684db4ed692bab52f645c -15131,0x750b3cbf2fbaae5e535dba3ca52eb59f06bc41b4 -14911,0x7568209bfb6faf70bcc595de233c8cbab909c691 -15453,0x75d5db90cd909bd074c0174d11ccfefc8057e8df -14993,0x75f3bf8259e2c29ab938b82759882476804c9dfe -15355,0x76d8099d93bfebb9044020f7a0ec7aa4350dbd5b -15250,0x77020e5b2f9a77667db39712408cb86c172aeae9 -15034,0x776f8b0cfa931d3fabb61e4643563a7cf625ca0a -15079,0x77af00b971f06671c8feac070df6205cbb43ba97 -15365,0x780662dfd377ea091cadb325ffaab89ea49bbba8 -15588,0x780890fc9e34c96692d7a338f6ad3f89604d8f7a -14818,0x7828976aa0ba24dd227329eefe3e770255024e34 -15419,0x784f498944b7477682e4058d98cccdc3f8c0bad1 -15236,0x78508f3be076a6beaa688217e85a87703a44cd21 -15403,0x78605a1dc67fce3ffb14959962e15b79df2f4d19 -15369,0x78cc4ef71b6b06773ebe7de68d4ee556333cc03b -15547,0x794fb31e55d3583c94b8f3344b8e2aa19ade7ad1 -14977,0x79c3833bbf13a8810ca7f796cbc712a5541bd9ed -15488,0x7aa24ecabc5e63379af9065272abf6ad2f570957 -15484,0x7aea7470d416378949e2d256f860193e408ec3f8 -15499,0x7af61188d005bfff0ac0c5ac02bce4b17d639ba3 -15285,0x7afe0c0fdb3a64e9fa86cd10499da0242bb81fc5 -15434,0x7b11560b1b29e68f53e4c87dc149ea2b579762bd -15224,0x7b9e246bca65fa800e73de6633b11601a552cf67 -15241,0x7beb951ad60f36142738ad3e4248229037dfff48 -15326,0x7c7938b6a1ef49470aa53da53be9a0baa8ce0b10 -14826,0x7c940810066f5b822364742bcae641bfda35b91f -15249,0x7cb1b38591021309c64f451859d79312d8ca2789 -15370,0x7d28555ef6d0ae1a1683f854df394818fe679393 -15157,0x7d3045d464b40875dbdcbc1e75cfd7e256b5cc3c -15313,0x7d5f9b4654c91fff897d8e13b2a2a49356c732ff -15246,0x7d8d66020cd461798a86e550e1d56a982042b515 -15018,0x7d9f995355094730b63f8e0c27f72a55e0573262 -14877,0x7da99ba6df2972fdb988c9fc007d0fc6068094d5 -15193,0x7dbcf80d97a7a3544fca0f96ba8f81ea04543455 -15334,0x7dbdb1a139f58b43ddd3f7a56edad6cab2708a58 -15123,0x7dc189fcc97c7531f79b07ac83403d43ce80900a -15635,0x7e086626e44a68c3bea70eec15934571b7e376c0 -14986,0x7e3bcbc83893e35444cb742350c6d005179bed65 -15586,0x7e74b80d7e3fb48273d89293ce4a4e7f25fb72a6 -15608,0x7f10ff4fe9ec4080c1f5c957bb630b4b623f3e4d -14881,0x7f343c16c0c69a714e0ef8d508c28ad82a7cb373 -15567,0x7f4f84c02e63a83e52e48b60eec33c1fe6700e57 -15312,0x7f610a08cbf99921d59ae983d5c3f5f3a2ab3018 -15583,0x7f947d141fed32595916e150740a5e60d479e95f -15346,0x7fcc5d4ac5e27a9f6d9bc0b87d57e4e82b9d42b9 -15027,0x7ffb6371af31fac5829239f51ba3cb191c855fd7 -15298,0x8010b679c929212adb3f07bb9bd6e91204bd6b94 -14884,0x806eaf9782dfb01e4e84f7e69dd03d2227bd1e58 -15130,0x80c9ad8e6946059c1f5cbb8c230d8d92c9210cc1 -15325,0x8154e90e6ad2ae2fe366720b1a8dc06f0e36b87f -15014,0x815e309df4bf6957e8ebd99106e5fd50af084a0f -15243,0x81a0576da9938235288e25fe96a1f6739489076d -15036,0x81b7fc3308d7e7cdc89a4090a23fed111d77278c -14966,0x821863a25743eaa271fa5e70d9e4632650848eb6 -15229,0x832f18e921375d9dc60ed0239698ccbb0f274248 -15031,0x8368ffeef892082ad0f6ed9821ab83ca430bd0fe -14909,0x83af0f6beb6520defe654516faaae0e88d659a90 -15061,0x83c13c1e1b3b6eb64fee20a3f7c3d407f7e46ca2 -14915,0x8416d215b71a5c91b04e326140bbbdcda82c01da -15059,0x849b45d86c24bf8d33bc3cf7c6e37e9f83e2351f -15344,0x84adf7f211338b85c3277f3d97875dcab338a841 -15050,0x84ebe28f2796d0b61a73a6161bdac64ee3edf52b -15230,0x85d01fc822b83f70a16350afe82b8a5ac0dbe765 -15416,0x861738ffd9bb61157e446a1cc352cd512c17b034 -15638,0x872ac5edaf732e140168f8c7645c47817a04d60d -14838,0x873a2654c73639132caa0afb8938bd81279e4e45 -15371,0x8767a49d90f6fd60295083eade854f3a67a67516 -15373,0x87e2b1d9b7d01df6a72b604e85ca6b6e4c1a15c4 -15076,0x8855e27abe0c121c5b9e1d1572b40cab184daf21 -15296,0x889d20d0e570917b1f2e784c31dfe4860eaf6ca1 -14873,0x894bca255f25471aa507fec0a6a116434b72cc1f -15429,0x8988fb2747e518c74f940951eaa7f7a498c65027 -15295,0x8a846589f1543a46afa9a61934fa27946e4a866a -15218,0x8ab9012d1bff1b62c2ad82ae0106593371e6b247 -15610,0x8b139459f2044cc908e7092d6cc71fefa0dc462e -15409,0x8b9e8868695e1b3156359f31335d6bbc83cb0d4b -14956,0x8baee447254dc24fcd2c135692510d404e80735c -14996,0x8c3dd9fef5d78a032f333b1a5f6cdc04705a37ad -15194,0x8c83fbe21fb3955dca900437dcb8ffe1f4afc8fb -15133,0x8ce54220e6688c7f0d9c8d03eba4b17730354117 -15482,0x8d2126e13d4fb57b4dd1e37cb0e74ac270528298 -15543,0x8db5cead6746bb3053fc24588a7586f2eed26a1a -14913,0x8deae4e487af7df7e3f12fb39e4c81884c4598bf -15117,0x8f15f86e89bdc0ba8e078e3cbafad9ead9b34045 -14848,0x8f80755f06157943d79f2c1efc7cc4fbdd7b7466 -15623,0x8fe350fed4ece9854a1fe762d04e335d81c15f7d -15248,0x9074a17d65e87ed43e3436cf032a272557008989 -15643,0x909cc74fa5fc832cc1c5c7d2d9ddd1574127d231 -14954,0x90d910532d49ff0de079423817ead88384bee3a1 -14830,0x9182d9388b248cbcc8a0fad6f07b642a3ddf72c9 -15587,0x91843a478c8c4226224a11689cdc831e7e3c0f21 -15356,0x9220b9b3f64a494089947c32a2ef3926c6f2c0f5 -14926,0x922b4e78a3062ed5a7193264c7fe6958ae224fe0 -15631,0x9255bf0a48d33427cced3b5f48c481cfead4441a -15309,0x92640fc836b12cfc693d21f49ae02bc6c1c349dd -15381,0x92f445fb4a239369cceb816cd6980dff4fab93dc -15611,0x930a4671fcb1ae3d351d8e7c2e45bdfc8de9165c -15584,0x93ac9588d66803fafd6c9de85d62fdf1c85a460c -14964,0x94135b2cd3ea682f8ba7aa3d574f5309548381bb -15503,0x943cf572962429852c96c1624d2a898db3f55ae5 -14880,0x9489d4769d3abd3483421fbff41bd6c7fe8541ca -15411,0x9520aef41161f09dce78a8e79482b654d4ffe641 -15479,0x95490787d075b428a4224100001f96f88c916b52 -14957,0x95896c4f10c4607860ec7d441120b6e2aa1f141e -14940,0x95b7d9d824aad0f45227043571f4e2cd6403a038 -15540,0x965391e9a103f363ca33f535009289cce5a63f21 -15390,0x970d50d09f3a656b43e11b0d45241a84e3a6e011 -15035,0x97dc97c8039bbeba4d4658f6864628911b870371 -14882,0x9813724faf14a35faafc6cd61723a99e97d77807 -15470,0x982ee057b633fce7fe7c8fc31bd556fccb81fb92 -15162,0x98f8c6b22abda9a54d3d4f0f6200b4e4d4c4080b -15379,0x99574f7c422cf960c923fad4446d8a05d3d6872e -15571,0x99601e024709ce31b59aeb5a80a9de2795d98351 -15352,0x99a8ccf9f1dd4920a34cfb6e6ad33d10d3d7483b -15217,0x9a03047b530a098d4f6f0fb1740e6d2a6ad1d7f7 -15210,0x9ac865d89f2036ccd5a50c341157771873bd3e89 -15498,0x9adecfc95e1322e87a92d6e107e33b7de750d90f -14918,0x9af655c4dbe940962f776b685d6700f538b90fcf -14947,0x9afce3d8f9255d014994d39e039c65f6d1aa7043 -15009,0x9b0823e002112fc44525c7d44967b6ec42a8820a -15458,0x9b201813d2d3857ad9dc69cc600fe7d1e4edb6dd -14935,0x9b5eec7c4e848892c16a5e76f43e8a943c7a9880 -15460,0x9bae36e0c2941f7168358501d8e43d1a9d174fae -15594,0x9bcf98a468d78357cb5c5e6c7e02be86da49bf12 -14970,0x9bd68df6617bcff5ee475554c08b511e09c33bbd -15489,0x9bf0e7212c4e1e993596107a21ee6960f5831674 -15577,0x9c2ac9557c75053e5290b8807319a42cebbe7c00 -15642,0x9c65423da6d9b481cd756eb9f9f79f902e08acfb -15021,0x9c7f5154b3cf261c7b6dcf61318652e000a38641 -15103,0x9c97eb920f88a927ef3b0a89650104744910a641 -15472,0x9ca172f2fcd156e6910c582d0e1e83807581abd7 -15339,0x9cfc8bd1f82dad684583c04076fd69c2b8c566e1 -15032,0x9d0090d3e0253674ecdaa6b62fd01f266bcde988 -15331,0x9d0af87026092aec6ed8279666275c09ea04d09c -15095,0x9d934d988a421271c1b42a7d7b04bacd61aa8a58 -15559,0x9d9df2740627f422d8b33afd0ccaeb6407c64ae6 -15626,0x9dfb89c4950751b8cef882355c12ab18c357b0fe -14927,0x9e23575d8a3aa875b5c56be9f59656fa2253dccc -15394,0x9e88f7cf6c9fc2895dfaa1b7c21d446ec1749f89 -15517,0x9ea35c918978d28b28325495b30e0c856abc14c0 -15640,0x9eb26cf262c3b3958eda273304576af312a5983e -14820,0x9f1fb964313a164ad9140547a27bbefed44daa8a -14920,0x9f764d00c3c75ec266380179c5e2a88fa304bdf2 -14965,0x9f8a9102cf7998eda22ce135d5ea4a9d33f9a47f -15004,0x9fa11255f91b3d5f25b2c6e4bdfeff23dcab8b8d -15528,0x9fd56b853c73888b0b3d1b41e6695c4eee12a344 -15075,0x9ff810d5dc4d1b462134b2eb24910c1ca7c5cb22 -15467,0xa0d09c7778eafc0e1752d8a6769a3d082c668685 -14812,0xa13d63dba8e164891c2653cc71b7606bbdf2110f -15160,0xa308cfe7fb52ff1985e3530c24749a9b2f7cd162 -14868,0xa348700745d249c3b49d2c2acac9a5ae8155f826 -15625,0xa375db1d58be889d128fbe15225cadb60d5c1485 -14833,0xa3936ecf31f6583d0e4d36ad10d10112b77468ec -14822,0xa3a03ac43c40110cfaeb3fad21089ebd7ee1b0df -15385,0xa3c35dcd2ae4abe412715b1283293527e35a9b59 -15188,0xa3d83a1c1f6328e6c7c6ba872e33c165465ef41b -15505,0xa3fc09afe488acf95a325d170c4f353d3a5136bd -15268,0xa457a6aed9679eb3018bed7ff7209192aa841084 -15532,0xa45df21a497e3e31a7e0fbde52c10d8158cb13e8 -15189,0xa484e394c9ae2af31f0c06089c30a9adca8666f0 -15476,0xa4afe691058704ece2fee3ae0ddaadc46226983d -15002,0xa4ddfe29f318665482718fdab0c67c5ce46c603d -15292,0xa4f9d2107e5a7a8a512cfb01170dd4d456adf0f1 -14916,0xa4fb7a26d46260695818eb8f813b29bc580351b6 -15220,0xa50eda3269e10368c89c10e3959066ce37813da2 -15206,0xa517b8da3bf2eeaf0bab43d2c9a56e09131e4e97 -14886,0xa5aa0ee5b202039635e876ed4e5ddb47dbbad0b0 -14998,0xa68b84754393b33c629859ffec6ba5f25a4c87bb -14810,0xa736222e970b27a6e05f3724fd377c78735670a6 -14857,0xa78be2fd2a7357961d950a235ff52ce0b8f31201 -15433,0xa7ced538467255e73bf1ed54d3d00980332c1b41 -15619,0xa8378515c3434bae28ec84ecbc0c479fb677f957 -14919,0xa8411f3061b19059def416e37498d54e5e829a09 -15405,0xa88f8c02ebde678de623c6bcfc886de82e18ad00 -15426,0xa89b74e9ced0875c905e5b40059de7b646103c11 -15267,0xa944c8c44b54b591e170bd543ba7483db488813a -15565,0xa98ca663cbeeb5758886e76d7e26f2ca369e5753 -15445,0xa9f619ea1665fed1f6105a058b732b5b0aca2cfe -15469,0xaa6cfd8a0ba4fa23f1a24a6ac72717ba977fa739 -14953,0xaa86c0f5f655ef5888551abdc7cc92096c9dc267 -15066,0xab1c9e022eb0aa2c946500d8c50727e5aa897fbf -15444,0xab386160e7047d048a83af419b7e0c2431d7f5fe -15448,0xab5d8a0bce2fa7e5bcd5753135c577154c10bbd5 -15358,0xab7924c9f50bec34703711f153a31c6017f879a9 -15172,0xac0d6c1ae37747322096f871f41cd92017d3d534 -15622,0xac51cd637e3749fdc9520fc1ef62cde98b6eacfa -15589,0xac57cd23a3dbf14a76a7d9ff06c5c0ce035a20ce -15168,0xad0456098e5f5f3ac758331e13ea661cea7fd02f -15520,0xad1164afd0753fc55fba4b46a5d338b7acad6060 -15040,0xad1c15fc9892dc71f0867fa31f00775ccccaf3d2 -15439,0xad2d812851dd3cc67b59a5e3ddf889d218ad63f6 -15227,0xae97dfd8e2112c5b526871a6d8eb497210e7e208 -15314,0xae9a73eb0a873b3e3952ab440624ebb7168930b3 -15495,0xaf51b1933782233c0151f1e8ccb5764b365880cb -15286,0xafbcb41c5d5a388440eb11fa59d45d09a5d3c212 -15024,0xb00238b729a4f33c2f0c95bf0987492845f89d03 -15101,0xb013be825f93d05616f9d71348584136101db09e -14870,0xb0685e039949f925e5314f0b32bb59d278584e4d -15393,0xb090afe2aac35778706bf447a52ad5c69604180f -15634,0xb0c5eb4bee6f70ff1c7166310311dabb8d56151c -15155,0xb133a411993e70544d9ffd1f8381b04f6048b040 -15073,0xb13dcae39ca5d961f2c2ea9b1758b4588be1bf08 -14856,0xb14b109d112b0adb8187418ffadd0e454fc7778a -15000,0xb1b736bfa00703661083a2be1784ab076a53a89a -15649,0xb21350049eeef54ffb1bcaf8c7848acc631e6d6f -15471,0xb25ce65105a349c6cbdc26903cda1f83c12ea84f -15060,0xb2ac124cbd47a1fd38a0ab1ef493291cc3bcb5f5 -15569,0xb2b723b902f2540ebb0868935cd1b8c525207fa1 -14806,0xb3285706c1b8b11fb5bc0de4be380066d2cb26d0 -15271,0xb37729e716b7cf2a72073474592f83d0dac3eaf9 -14878,0xb3f20ddc81b215b0fe1a7d11dbcd3e6b4575375b -15297,0xb4aa1de9b87cb65c31dc9711fcb400c20008e14e -15033,0xb5c81db01ce9ecc65a7ae242c3f9d66293f3a2b8 -15141,0xb6852ae2ab1e1bda1ca058e1e4fede41793dd2f3 -15074,0xb6d507f6a160d18a41479934238840777aced231 -15144,0xb6d95f904cdd31ad5e54a363d72f67cc5eed837c -15316,0xb6dee27f7cfd546a44a894b3d37cb668408a43c6 -15201,0xb7481aa50486df7c38a4f1e2c5b9c650e29bec0b -15366,0xb74b5c595d6e78a1e9cf0808d7edce2337cfc54d -15214,0xb79dd08ea68a908a97220c76d19a6aa9cbde4376 -15182,0xb7b7f78d572308414cbf55d1a83aa5f7be700531 -15197,0xb7b9addfcc137b536a8f4aa76ba409f5597b6609 -15187,0xb7ff28a21b9daf2f9f0136601098e66c49d32510 -15435,0xb8f671b84992e5506a8c383fa74851f1d22ee8df -14853,0xb8f7881ff3fb59a8b98d80d808f63e1d54ad4a6b -15231,0xb9197d89475b4e6f08a9cca627ab65e70be102be -15023,0xb92fd7abc8da44e122ed9b01b474d61678312037 -15086,0xb9a141545295c26d78e3c87e804befe69d029de5 -14948,0xb9f54e42bbf3b5f7b318bf90e8f8f755077bbd6a -15533,0xbac1b1c50ec975858ee467a006b21cf9dc8ecfce -14937,0xbadb1ab49f469f129ec1cfbfd05617f62a3aff29 -15272,0xbbc0f6f8b338ddc21b18a8be9b1c1b9a443b759d -15063,0xbbdd38a1e851e5764a50fc158b75955268048d9c -15374,0xbc094f1bad533eb55ac427c5fe065c90960de980 -14842,0xbc2a7af37bfaddd8be6a08982300c86a9f541742 -15142,0xbc4e13cc8da75958885b63a1b7b302585a15c57f -15233,0xbce871e9623f22d6d199a65dfda9516b5652be45 -15089,0xbd3e9f4e6d4fc307034cfc42855024062a3933df -15122,0xbd61afde2c0d5c022a2088f3d6933decbd51d6af -15647,0xbdd1a0ee987f118336e50054db761cebe47d626a -15125,0xbde93971cd89688661cf4ccd69262206c0f81eb0 -15215,0xbdf139cc161eb93e8ce95c58af98027478d803c7 -14938,0xbe1fe9debdeb82121adeee95b568a5a46daa2083 -14908,0xbe6d7b85788d56ba35669f8c7727b578594157d2 -15590,0xbe77573c2465371371c486514d42658b0fa09aab -15333,0xbed45d30b6a20d77621965e42c855e1060b4a7af -15388,0xbeed6129b61abab04204293a51272809be19c2f2 -15028,0xbf005a741bc8d9a92f39662c7a8fba59e80e836c -15176,0xbf00eb2cda0b57700efd3330e48650e69d1f5e79 -14862,0xbf148c69372a5aa3626fe65081437f12ff2e8151 -14934,0xbf21cbe292b40347b3691c3187645501fc0e5f97 -15154,0xbf6665b1c4d9752d727dfd11208350548e4a0131 -15321,0xbfbf474551e4252ee5bb23943e213201ff75ccd3 -15368,0xbfc499e8657700cabd80f5d60445644a0f193191 -14992,0xbfe471fd4dd7bfd96dbc55237c4213aedbb1521c -15257,0xc0f64a9f7467812b8bf56259d9e4aaf1a25b5b60 -15545,0xc0fe1918677f8b8350811a40c0fd0457d1b8b28a -15359,0xc1e10de2326e5ff93eaf0ef6d11e29d8751eddc9 -15065,0xc24c8dc3a40d92940fd9b9b0570589564fe755bb -15105,0xc24e60bbc4f7dbfd8076d8ac87896e14499a13ca -15438,0xc286dec6c0e1579f61eef21d35421cd8e0ddab0d -15136,0xc2de28aeac3d4ca04091468cfe5d6cab88977694 -15524,0xc328cfe603934d4796721f505dd4ae8b6791e0b0 -14854,0xc3368d934929c03727f32a1e8d5c39b241919cde -15265,0xc33d93abcb103edb8a3368d78b4739a9d0dbf4d8 -14942,0xc356e0c69bad729c9cc157ee3d833dfcace481dc -15451,0xc398f0927d261d467025e3506563b8808b1bbe02 -15457,0xc3e0d1adeaabe5df13c2f7768af485f14c66e87c -14871,0xc40b106c9d7b4fda8a13b018b8b57e3490294012 -15585,0xc43e10375573eef024863c72cec5e0e752bffc00 -15311,0xc4b4a2308b14704cb39c019c92e4cbf6b53315a6 -15039,0xc5ae29d85e8dbd357d4bb01d8463dcf6d0c129ce -15542,0xc5e02222e019727cc9b3cd1ec2c73d766e7eefc3 -14879,0xc62a6b6048657b9e86a2b3ff21197d079c8b85d8 -15566,0xc69f69c6165314b9d658e5345dadea7956145f02 -15212,0xc6e9179006c8e4a496f227639126a8fe77efb014 -15025,0xc72e7168bf2f5b3777c19323f29e5059b8c2a8c1 -15539,0xc780575ae0fbfaea6971d93fc025790e257b49b1 -14932,0xc816cc6a0841ce679d168ce6ad100d771d54433b -15069,0xc81ba5fba11c0249bdf3637acaebee4d0ea6dcff -14910,0xc8261dc93428f0d2dc04d675b7852cdcdc19d4fd -15579,0xc86c13b7ec3a812f30214c759646ccee5e368955 -15341,0xc8e1dd4804af07a59ef72559c628b9bb2de269da -14943,0xc9495a07e04cbbedb242b6d73514a3e96368ab14 -15113,0xc9accce002ba7f88d18e181f92b65b144d601573 -15500,0xc9c325cd34abaf4a46264fd33d40671e47e49602 -15350,0xca6320cdb54acda6eeb87e2dbee782892279dcfa -15179,0xca98d642e158c3016e1c75dbcc3d5ecfe3e02a19 -15110,0xcaaf507138ca294473018cd0182a3a534284aaa3 -14980,0xcb4d907c8cad49c5e9ad77014935839b3b9edb57 -15531,0xcc68d5fd4203397c92197d86f58dc476b53adeb7 -14924,0xcc6a442be9be304d974d724cec79a461b03db1b1 -15228,0xcca3bffed069771c7ed6dd3227c26b1800f71c69 -15485,0xccb9bc61794263748df0f73ae92465dd0ade8c6a -15305,0xcccc469689d49b52e40ff2bad0c6161dd612e75d -15607,0xcd05b020e43d4d32839096aa3faa3970eb09a4ff -14827,0xcd23ef9d3f47a49480abf2cb7e829c0bd0779497 -15029,0xcd3d786d8662c9058e5ce7a4bc8116e64d4643b9 -15150,0xcd635c55b04aa211b928b328d8c624086ef619f9 -14821,0xcdac414c104dd22b2f7157331f6b725168842aa5 -14936,0xcdd74b679d32a3d6209b79660bf238cdf1f52d9c -14840,0xce071209db0188989a9e5d58eca5493e67713eb7 -15362,0xce0ba42416a8f4eb051b6f85a2cb936e60c5b3a0 -14968,0xcea7c1f57c8a6ccb0d175f1e967a84e5648d3119 -15372,0xcec032a2ade4c4f7513cd23e8957f2b4c5abe762 -15414,0xcec91fc436dd80c3c4027d0bd9a1d25867d0e8aa -15455,0xced6f77e0e7e39b98e7b1747fd90286812644e90 -15055,0xcf02cf91b5ec8230d6bd26c48a8b762ce6081c0f -15513,0xcf4186302cf9eccdc66344362f77a9c0b41f262e -15128,0xcf8ec04d6b69cee2ea39695e475bd73253395938 -14819,0xd024a52da5524319ab77a7dbd86212429962d14c -15192,0xd0a3b90abf3d5ce3a9adb1aa810ebb66a1215845 -15006,0xd0d22a424d0d1220c5d31bb2fa9ff3e4439a2335 -15558,0xd159de275ebea6bbafec96f452389a33b573f017 -15343,0xd232bb8804e749da251ee4d509b1751cd062c2a2 -15183,0xd27a3640cdf245f97b739f20605e66a79160361b -15361,0xd286be98d4f49349ce2c619f58cce2dc9ce1ea76 -14814,0xd2be38b3d13b408bd75903eba89da7cea541afa0 -15080,0xd311541e2fa99f7587ca5850ffa2e8ae75696a74 -15633,0xd318fa7e6d6050b4ea1ecb34e4251c2ed4e643b4 -15516,0xd33aba7d93fd4c15bbade1b2f50fb4b6cedd0cea -15053,0xd3a12697c265daff2a365b17a3cf3e6aaca06dfa -15048,0xd3cc82085f34d9c500fcbcdb176edde17a82335c -15541,0xd3da26165a1d8a89f02a09d51940e9e8777b800a -15203,0xd45165e76132fc8e94921932775fd9519eded6da -14914,0xd48f25ec821b3ae3e11b53fe1795abdd2e2d1578 -15422,0xd4c3612dacc2f1c430c2b53d42e7da24f7acdace -15401,0xd4d441021e3f142cb6c6bcbcc8f27b0c3dd71e13 -14941,0xd50553944abf29bf5653083489b88dcffc73e14a -14959,0xd547ffb583934425d84829b8c73a3cacb4efd5f2 -14907,0xd67b38b1cdc724b6a8f8af76b383298306877367 -15042,0xd69fab1ff729aa352feb722bcae753ca84ca5827 -15506,0xd6cd67583aa76c0a13a4130f4988e8f808f8c8b2 -15538,0xd7accaf9037bd9898abb7320cbce7504daff028c -15139,0xd80de511048957413100c5390c5e5626972d0296 -15632,0xd84f92a334d2a73f43b60845bb1841ab3fe0b2fc -15349,0xd8b4ba2ccb203d72648932d997cde56869a8bc91 -14829,0xd95916b7bb470f666c4bda0f5afdaf3d89a2dac3 -15020,0xd976106896678ff615a9525e9954775e52f2fdf7 -15473,0xd9f5ffe0c1625fd973968457f1e192cb48dec832 -14861,0xda2b56c76daf7451b2fe143c0b5e38295991484a -15181,0xda55099ed344456eda02130d7b42a39162aa5c7f -15279,0xda8bb2a1c1b31fa6d75608685fcdfada1e134ae6 -15104,0xda9556c3cfc0d716181bc9545a23dcce85d19b9e -15353,0xdaa55021ecc34ba05f1c91fdf813e1eac27a9d43 -15582,0xdb29dec25f2e03cdd3408e74dc95c42fd17dbcb0 -15332,0xdb42ab9afba2e1fcf0d1b259b9c815a5387e3dcc -14912,0xdbab307f2f19a678f90ad309c8b34dad3da8d334 -15307,0xdc4baa0b7c14ea0a4bb4923290c499f85bcd1ad5 -15134,0xdc82184d8cba9389daec232a8dc37af7915bfcf0 -15601,0xdcbf5a96452638488edbda18449b6245b065ebea -15087,0xdd1bf80d88076c51ff56d2ac19cf93243aeef111 -15629,0xdd2e9873e4c1402fd81c62b69aacd4a28112ed5a -15284,0xdd6096d2caed9aef96d7e7a96d8bcee327894c69 -15299,0xde13eba3109336e43533cbca0f3817928f40734e -14875,0xde34852c58304a09497d2f59064d76ea60385d28 -15644,0xde682e72c52b726e27e2c65ee90a79563780d33e -15097,0xde7d6ee773a8a44c7a6779b40103e50cd847efff -15639,0xdef3cbe99a8a859c84a24bbdbdd988f1cfdaef46 -15200,0xdfa3b20211ade73002aff9d94fef4c811df9351f -15239,0xdfdb274765b569c243c627c9d8b0a514844ec64d -15170,0xdfe7686f072013f78f94709dbbe528bfc864009c -14967,0xdfee4405da9722ed0acc0c870c850ba71442ce94 -14896,0xe01c261501301b925379b222da603f54a64c634f -15615,0xe04d714381762db13c59d0cad873fb2db3a964fd -15070,0xe07d376e0f132cd1a72e5125b6dace7f2a0ba106 -15609,0xe08ae530d174dfe4cac516e81b37d7967678c500 -14989,0xe1242a91ca2ac87b2cf1c23308c3d4c0b5115bd6 -15293,0xe1487c93c84054a5052760a0ab29a51582c63035 -15432,0xe18ccd947a8c6308c2b5fbaf3ae25cae345c2cde -15107,0xe1daaa581da3ae12adf950c5fedab833e4c7baa5 -15251,0xe1e36e93d31702019d38d2b0f6ab926f15008409 -15529,0xe23bec5b8c411d64e7af7d6cf95e5d66fc03eaa5 -14837,0xe2fe8783cdc724ec021ff9052ee8ebed00e6248e -14895,0xe390c2750b32641693da25dc1739d39e80f97190 -15406,0xe3ad82e330bd51b4ade60bc772b13fc9a879d66b -15551,0xe3f0aeff2a1829664fc3608d38a8dcf25199ff92 -15501,0xe40b0c5c7d13c63a85711767372cdbdaf4eb426c -15650,0xe46c41d78d2115a61b0a898175dc25db8210b8ec -14816,0xe4e59fc6a475cdbf12299513461f0f52644ee16d -15244,0xe573a773948c815213de66e9fb607c159ec18100 -15512,0xe58fb719ef52e1a502116d201c3e86bfbafd182e -15328,0xe5972ba90896c814d45be59e68dc34cb28a91f13 -15068,0xe69ab306134dfab8d434b1100ea4cefd2ba4f00b -15001,0xe75fb0556c904c1463978d260584d69772cce4fd -15148,0xe7f6ddb30e387269cb7dc55e9a44edfdbfc7fd39 -14852,0xe80772eaf6e2e18b651f160bc9158b2a5cafca65 -15046,0xe833e9fab8a0e7313a4234ba2c2ca2e2fd8d7092 -14984,0xe8deea3769f4dbc6046276c7d6076c33ff56442d -15108,0xe9500c0a6ac18776c3d68be8ecf7b00d60c89f04 -15056,0xe965f51008b0a1fbeb2b82d407179696c4716830 -15262,0xe982a4cc1fb328654e2b3347e00a8c55e297d8d6 -15115,0xe9c41417cd625a8a4336d17c6034a110f171ac97 -14817,0xea1b7153af7d63e0574cb6b63ccf52428fb33f83 -15174,0xea250cbf97b47522fda27a2875868491509ca393 -14808,0xea39c4961db40f2272bd2f3a2f9898aadb419d11 -15463,0xea7d84a8580dc527234dfdb990adb37b5d574461 -15459,0xeacd5888a238986f94fcc92808ff52ffea3ac97c -15564,0xeacf28e951714cc53fbe5860adb9e9bb9fd2b643 -15576,0xead44c29d90e33e849e9c621d81ab48fb70cb9be -15580,0xeb30a23b89a76c98f10fb0790ba620c91ccb8ea0 -14901,0xeb3c3e8658f559daa90e8ba51e78d20c99fe5fbb -15425,0xeb52d245a73fb9f6a5ec56fff6cbb0ef58189ca7 -15178,0xebcad4cc27bc0038cd096259cbb6c6d74e711277 -15219,0xecb6af112fbdb1fac6b83b0de5d5bbb42bc892a8 -14958,0xecf6309e0b47c6e9a3e8b5f7d7d9f6126c5dce3a -15263,0xecf86af2359954d5163636a944ae7e9c625f5b4a -15618,0xed36a68a58ac53c3af26ba1d7364c8b6633d233d -15211,0xed92f2f25c5ffe7bd1c7b9911cb89ed79e229a41 -15387,0xedcaa8fc4407a062a8fd490117f8174c3e97d8d3 -15207,0xeed95da049b089657f0c15944b70ea67ef828d83 -15277,0xeef6d3e6afcf3645693a81277b6649f3d5232d6b -15016,0xef5783eda995560b72baf3852105499dd64eb9eb -15376,0xf02d7e07908824304a78333d3946d2d6c9d2af8e -14985,0xf06fdbf0793fee3728e01c5119bf4a67f02869e9 -15418,0xf0c7c75b61ed7029319bf7273dd2b690d6302c0b -14887,0xf0f827598a064759501656f4740a7e1f611c76e1 -15213,0xf11b3b868ecd0d40797483b4ef987164c41c1c38 -15324,0xf15319e054f01839cfd1abdb9ebdf12197635c24 -14906,0xf1e736bd81a64f7bbb42882628cab7754b11d956 -15198,0xf298d5e3d14dc1c20f86a7073983ae851ed087f4 -15238,0xf2ad4dfb604c06eb8e5fe7fe374ea20da585b5cb -15322,0xf3876b4bbc6e1be6a54d13a8beffcfc8cea6697c -15254,0xf3a75b6dd1a60526755a370afab48ff3501e0d15 -15137,0xf3ba4884591fd04263b3015132bf675c7ea510a4 -14891,0xf485d67dd63874eb609ffd0a151b5dd693cb99ad -15062,0xf4a504b0e5a29569c9dfca4dde618a21cc54c6f7 -15347,0xf4c58b78ab5da37f9cd8556875720569720e2fb3 -15510,0xf4dda95e019be2fab6ceed4156c8ec6e1f2690fc -15098,0xf58cef9dbb853da61d5ca8481556cdc24faddce8 -14841,0xf59c9a5110852a82434e02d1a43169b5fd9897be -15478,0xf5b4564205330633f4277d8b535dc271ebb4252c -15290,0xf6278658b42061a8adbd8f9ca5e16c7931324026 -15523,0xf6ea041f94a369a640d9288f4ed5569655743256 -15041,0xf71121b4d0692a1f9d90eb1bc44b4ae917d4f2f1 -15191,0xf73b0d870836b0914b7fa24d44231f5a7f54c304 -15563,0xf7b085aadbfb2c0321a438ec750e413cf6c57666 -14845,0xf7c27599353cdefdd0dd452dd9647bda5d47806b -15180,0xf7d693ce960e70721f0353f967360046ba7d4efa -15175,0xf81fdcc2c3db55e52bdce7147737619c8af0f8fe -15402,0xf845cc752b7e7fea359585a3ec9c0cfe525bda8a -14823,0xf846e790dff0ebfd714e2b57fbb3d8c9a0e07601 -15145,0xf8828714a3a17033b1f916a9addf792f37c28a45 -15205,0xf8928f9118f9719d5f1b2aa5e7697bee6381fc7d -15354,0xf9233509c07246a7d012f9ffc97c4e488093106f -15534,0xf967addbb57143d92dc08d72660bf00dfbc70eae -14995,0xfa2e545c5613e3531b950ab837a1285dcf5568f3 -15507,0xfa5328a029575f460d9fb499b1cdce25b69b1038 -15612,0xfa53dd9ccb7d577d9d62402dee3ddab383e3d088 -14905,0xfa574ec918427632d74978e554aebc5aea6e96e9 -14834,0xfac722133a19d38833cc105b1349715717cf050e -15152,0xfae5ebfc4a97b17a16c6666966ad2a27b4037211 -15480,0xfaf6ed97787a9ae3b966bbaee97c0a530d176c15 -15327,0xfb1af7ba00b64b232f8cb25782006f2dfddb6475 -15156,0xfb58b242b9fac4beeb2f0ec4225fd5d015af6119 -15491,0xfb6a84c28a46a38b5d425ad267ffe3e7f5c34182 -14889,0xfbfba4b046feea4638555aa71d8086b8d3843ded -15135,0xfc9f6a01f0e8b26c8f12e214be7e6311153eb9f3 -15552,0xfcce4611d810e93d823b351c53a01b9d27923a82 -15427,0xfde73d45cb60d14e1deb3b1bb9b67d9b7e9cc873 -15196,0xfe1ddd9b89e5ec5edf14ce2bba861774cf70c53e -14858,0xfe231c923dd732921f81ce700daee7051f8af3e5 -15237,0xfe535758d35da4d38eba2d365080432ef5f42db0 -15282,0xfed8518be98bac69a525a7f585051e77537f1908 -14949,0xff34aad62aea14e1da04e90967b36c188ac9a770 -15415,0xff351557c9d82c5e7b5154a9d8af047efe1296b9 -15007,0xff4c6616ec0be8238f2055a447cb8de3dfc4464e -15124,0xff66c6a4b3910b62bbcf4cfde61562ef3e96a5ed -15715,0xd9d74a29307cc6fc8bf424ee4217f1a587fbc8dc -16129,0x4200000000000000000000000000000000000042 -16131,0x724604db3c8d86c906a27b610703fd0296eb26d5 -16130,0xfedfaf1a10335448b7fa0268f56d2b44dbd357de -16210,0x0030e8142364ea1165d6872d2d4fd87a7a9eb2bc -16240,0x075314b3bfdeaade554c39e70c1e8ab807618913 -16215,0x0b56dee153a67c14ea36a396a4c87bc16b59a92b -16245,0x1171783bbfca65482868fb35ab1544182f9ae3eb -16216,0x1217101c5b117327a79fd258821703297bda08a0 -16236,0x12780c260c9cf5e4cfe51af84d091629c346aa2c -16263,0x16c90e70e99f452e263067fdcc7d74467964bf70 -16261,0x1a55f20cf416577356ac64c9416358c8de60ced8 -16256,0x1e72a771e3799898872a2b21f7d6f2adf8cba659 -16234,0x20c285c8738f84abbf09fb0c867babd1713e403e -16254,0x260a22fe111940436657e1672edf005bcf9a7cab -16212,0x28dfca5db11595ff7aee389e22f9f7ce52f1d816 -16235,0x2eff4f31f5013a25892f885140be4e66988c0fc8 -16242,0x30f104bef5fd3008d7d37b784adda548f5f814bd -16207,0x3635142456a9962c0c7fa0b4387c17749ef79995 -16243,0x37a23c498e930af6809b23485a20a32f8f31c589 -16267,0x3dcef56325b6c2bec7a059cfe92cdbbe3df013c7 -16258,0x3eaec4b4fa0cfa49481bcd23bb154a1465f3c73f -16219,0x489bbc707c723afe2522960a9ac669d710df6e53 -16251,0x4e21ecddcff1e8c0bee0e7c0cddcf83b277f1766 -16259,0x4f325fcbcea979a3f8fbcc619f202be9b8e38b25 -16221,0x5261b9a19632aa530a3c3875c70be530d48c5049 -16246,0x526dc0cf2ef84b7ae69f8cd5ab2241af2f1656f7 -16237,0x5ccec7cbdb428e3e19687d903a1adce3c351afdd -16252,0x5ee0753e3ab9a991849200ff70b342053b61ef6d -16209,0x6b7240aaaeb6b4223c4737f3d47fcbf5b3d95c81 -16213,0x6db1aaa9185d5e226c3559ad1bc9e21b31ef5afb -16211,0x73373ac6685c77c36cfdc4ca80b32e52688852e6 -16247,0x7523523720be47c37de7340c764a4e3f37ff1930 -16205,0x765b1ee0c4a0afb4c8f30f4f0d0145e9cf0a11bf -16208,0x78061326b02d5328b60f261db8415b0d98472bd4 -16244,0x7bd1bd1dce0afb5b103439779f4f325589e11afb -16264,0x85ab3b59a7876a9bbc9fa2f2361f839a9681c0c1 -16233,0x89557e29812f1967dd40e087a9f8ba0073b5dd8a -16220,0x8997d7b9e496b927300ae453ac8925ac09171e30 -16248,0x8fa3ac91b0c20ec8e2af828c445b3dd463850bfe -16225,0x91d3388040f00a7cb11a4cf9be2acdc46d772a8f -16214,0x9bc728e8679d7e8c59056d06c5fd61fc3f26adac -16239,0xaa3e08cc6629361d9ceb012c5e4e4a33827299d4 -16231,0xaaa306a77ba020a52411e98de3fd9ce9bb9a95e6 -16253,0xacc295677e35fa18bd47a10ebed22a85fa6f4afb -16218,0xaf598ea2c3daa9d40d19aed52a30ab59644c6414 -16262,0xb7d342565d8a22c07c793b5ee57187952050c81c -16224,0xb868a66e1bfe53d9a23830d957ead5912e380504 -16257,0xb9fd670e32b51188b9c44c2e2226ec345f4debdc -16249,0xbb7b514673dec189dd0a753979d9f63cf57136e3 -16206,0xbbbec320a9d4b2dc0f76be7544bad34cd59ed261 -16230,0xbdf04fcb4447db243f06194bdfce3224c32e5c3f -16255,0xc39df58fbcec75d89e516cfa9c89c431e59f9a16 -16204,0xc3e5a8a1c3b0b4c81f85ec322554187b3575c478 -16250,0xd12787076b33ffcf32a396e4ad6e415c40bd8ecd -16241,0xd685d399754aa20b4766759ee50ee180c5163151 -16229,0xd7f0c55038683b8eb998a9588e2bf3cde712b33a -16227,0xdd51621d801832cdfb4aa3c2fddaeac8a26601ec -16217,0xdddb499b74c4a5b8f126dec403c3f86ec57b949a -16222,0xde0f6167cec79a127e6973daa0923ad1e87b8bcc -16265,0xdf39145a4c8f24631ea945781ed7181bfc16148d -16238,0xe373abef6f3382dc96fba2f4e1c22330ba6f1dbc -16228,0xe523cc4db850045d1e53587a66c447b8df17edd2 -16232,0xee5ce8b7fc116faa5cb1ee8cdd445668978f61db -16223,0xf2289254b4579b049962225d492f3d0ebf286398 -16260,0xf50e96c095d7d5f7185080200d59b133707d0678 -16266,0xf5696de2efc5fae5c860ff52ecf9deaa02edaed2 -16226,0xf602daadb9860fc4c9dd0b3d76b8f5cb1cdcf30d -16341,0x008fd50090e94c7a54a2e3ff66f2672562fcd958 -16327,0x0917923af85ab3adab081e3b8825fcceddbb889c -16339,0x092d731fdc945cf1532d161e45245ad5382ddd92 -16309,0x0ba1195d53465057d137cf3c62e702c1a518bac6 -16332,0x0f4aedfb8da8af176deff282da86ebbe3a0ea19e -16325,0x19370be0d726a88d3e6861301418f3daae3d798e -16344,0x1ac958477e0873d7039b2b71ae89b51d3248fa4f -16329,0x26bda683f874e7ae3e3a5d3fad44bcb82a7c107c -16316,0x2967e7bb9daa5711ac332caf874bd47ef99b3820 -16354,0x3177d291386899f0674ef494015e132e54b7a973 -16313,0x3dd7b63a9b52761f89bd1bbbd31324242a0dae01 -16323,0x472f1c1b51b1e2dc022e9eb9673c1f2f23df6e72 -16321,0x4adfb6aafa6257d896e7baa5026e709f71060ce8 -16343,0x4af9be5a3464afdefc80700b41fcc4d9713e7449 -16346,0x66619cb2a51fbd9ff8eda47be3299bf781a4b899 -16338,0x66f440252fe99454df8f8e1eb7743ea08fe7d8e2 -16330,0x68aecd6f5b0df5b85c55be8b0563f788dc8b9af0 -16318,0x6b23875dd0d74c767c329c4a927b4408337b65d8 -16345,0x73bdb15aaa8797a37cdfa236ca4fc9d6e5a223ad -16347,0x81278dfc4b2e2dd39c1d2e89aab1eebd708be99f -16328,0x85ef04b9d1a95e897bc8aac215c8cf3ca544b45c -16337,0x86e5be03bafb585e3e8295fca717728bb7465898 -16350,0x8f3ff39fe8565b787453f3e5735bf6f0863853ac -16319,0x9ea0a053746c510006050cfce4a669568389a68e -16320,0x9feabe5a43c182b76eff480f848c0204f24158ce -16326,0xa7487a536968be0d563901aeb3fc07b099e2fb04 -16315,0xa9f63c7d66c4916848a7f9bdfdbacff27ed013e0 -16355,0xab937e03354a941d723feb715fb73c28f2e30fbe -16352,0xac7dce61faa5b130b4d3396530edce4bf9c68519 -16340,0xad85421c47e9b830602deb83fcf149c2a1f0e8a9 -16334,0xb0a1656c29437dbc2a9f463482a58cc88fdaf72f -16351,0xb333edc3188a241668f9231ceecb2f60b02e89a0 -16317,0xb3a814a14b9c2a982576fb2e1f233bbc27badb10 -16349,0xb4011a52c1f49a8b51161485d39ba1e70906be38 -16335,0xba661e8482d62d3b33751961780df04aab373ea5 -16356,0xbd1930dd4cd4e18737c7bcdacacfe450f1d231ba -16353,0xbebaf513a35c45141dd7fe7ed88c9bb145c91e4f -16333,0xcaa62b837788c0a0fbfc0c5fd8adfd74bc341423 -16322,0xcc6856099525eec28109fd5041f68f9e588a3b10 -16342,0xd3588acd2a3b69e84a127f92da3b5f8d81951c16 -16348,0xd4f0968c718e2b3f6fc2c9da3341c5a0c4720d68 -16331,0xd64715afcc9015eca711d3915b3d11a5e97904e7 -16314,0xd7ec4e3dac58e537eb24fef4c3f7b011aea50f30 -16310,0xd83abdff88b99eea6ab706e513986478f3e163bb -16311,0xd8dec96b9e22a99f68d48cfb20cd6868a3dcc56b -16324,0xdae4db475aca613a5e2dc5fc304da7e962b3cead -16312,0xdba05e1bf0c027ab59ea0f166c941c2d55ae8440 -16336,0xe4d1ebf03742a8238b546980261507fde0113d37 -16357,0xe56a4ccfeadb140333b91f561c6b0cf308450ac5 -16402,0x6407e4b12089d8eca3c85616df0440f769c1da4a -16400,0x95176b826cfbc16fb6e3beade8c885c60e19d3b3 -16401,0xf7d50c5a75514c6a2cbf6dd03a80ee35c31c8f1a -16607,0x12682669700109ae1f3b326d74f2a5bdb63549e3 -16594,0x1280266b2bcab5b08441d37153b1cad5faad4f27 -16587,0x28775721763c9a5aaa348145831f9379d20884a1 -16605,0x3a5292101a26c1dc75f97965b9091d4761a5d1e3 -16606,0x44d77f6f6f64e402aa795e8131bd60aace187dd7 -16595,0x498ec5659bb72acb8c90a9eced7de243c03f6113 -16589,0x4af3246adca72ccdfb5afc31bdf7f17f91fa712c -16585,0x4c66a27ecec5af2e87ad8c8ce5fb4cfc30437ec3 -16601,0x59414459c6c9883480983ef9888334d9376431f3 -16609,0x5ae7ff97f40df101edaba31d5f89b70f600d9820 -16583,0x5d60d8fecd3343d836506fe861a2207446b22d1a -16598,0x6209d43b4555e17fdd2167c89aae559a3b1ffd89 -16586,0x78abc57267acd53c42b9780fb6a7e8bab2da99a1 -16584,0x8a05dc902d15aea923f2c722292f5561c3496317 -16591,0x8a2ae0cf4674e8f83a1d823f57dc5735c5677c21 -16600,0x8c140cb521f09af2ef776d3191f350c661171958 -16590,0x8ebc788752a3df9e03dfe2fe605cfb89f46946b9 -16608,0x985f9ef7dc52f16a4b51abea986f855dc2a4f079 -16593,0xa05b26ebbee2a2ccb7c30be499d26a3686e9f03c -16596,0xb3b0a476cccaa41aa499a08b48c14ee668e8588a -16603,0xbd1d2caacf79f0a725d5fe44f201bbb838e1f75c -16604,0xbf19e64269ac14dc8786c33d1c9ace7dfd7fe9fa -16610,0xc0b69439815f2473f543a71f68a8aebd08982d7d -16597,0xc39b6d561cb3c3e6b9e95a7b455073da2a9ff122 -16599,0xca2462c4203259e0ae1c21c0849e3fed71be6cc6 -16602,0xd5ca47bf3e075b5446401a56d04492425e798e82 -16581,0xe0e10ef7971d78f8ce1c6b16dbc1b8d64add3672 -16588,0xe77ca468659c92f6deb63dd46720a30ea48ebcea -16582,0xf6439037b15df4c8019ba47338d8cfeb4c435089 -16592,0xf7a9fe22149ad2a077eb40a90f316a8a47525ec3 -16687,0x0057ccef0c8e8a40b620be00b20a62179760f8d2 -16666,0x06d218f8a62325325981b235000cb4618e403b6a -16683,0x0c8626197be1c155960e470b1588e8b4761ba8cd -16653,0x1c48a55c8ece8057e734d0516114f96a61dcf487 -16654,0x20e04b83934e8f2109c18454f4202c0ac5c4c6f2 -16698,0x27d7f06efe9436575c348c22929a45bcce765511 -16652,0x292055a9b947646d49006eb051640717b84725e5 -16651,0x2e8c9aa65d51e40581833cf2fb003463bde3d751 -16692,0x2f30729123095bee8d55258253ea6a845127bba0 -16699,0x2f7e020326c74494db58e1a69fb9efc76060dfc3 -16677,0x32f51bca3704c0c9359300eb2a068eb7a7d5569a -16682,0x370a695f879b665db5745de917105208a1dc61fd -16693,0x3b549e1af8ffb2120fef8ed49189c282a3f432b5 -16707,0x3f90a1e2df7edeebcbdf5d064cf3b8e52eca6645 -16704,0x453f988898ba94cc6f7cbb834506a9882b6b2c68 -16658,0x4ea3a0728f4e44c6f0ca145c0e0367c501c6b68e -16703,0x4f5f9abc5981f43fa24c066051bb3e67af454063 -16678,0x4f63a60941f86d32bfa72936de3e56e641518722 -16670,0x572f0c4fc62fad0f0d3eca1fea76251c5bbd899b -16656,0x590c2fc9928cdfc98869c47a69015c4b908a5c49 -16668,0x604f5afcd070b7de6c5a402004df9a897ecc3fef -16664,0x60db359cad86776f213e904c4e303df163fee4b3 -16706,0x64ee6a9aeeb6e8ba7be542a9e90c338204d26a74 -16697,0x69273325ef31012ae67e8ccad8e6d5e62f836dc4 -16676,0x6d183f4a271e5e068fcc09e93dad92f3c5e325f1 -16674,0x7be941fbf8c1b2aca38818ecbbef6ccbb5c975d9 -16661,0x7d1d6000f9528375092771450743fe72de3bc1b5 -16669,0x89fb8b0d1d45e3202cbe523bc599ade3146346a2 -16657,0x8b0fd871b60def160ddcecc9864dbe96c2023242 -16650,0x8d573e1d8c6aeabdc78dba4bde27d866cecf1a36 -16702,0x8e3398b1d76ddc05d4e4de9cf00ac1fadf164764 -16684,0x8fea87c402d03c59f3a8686f05fe626811879542 -16691,0x91255655172686692dbb23742acddada7805188d -16700,0x91711c408cc3248e6f25d70a862a87a5e8387291 -16663,0x951d77b7ce4caa3e48c3c3bcd694e5a909006d4d -16685,0xa15c441c775144aa99f0398128d208f183878d95 -16655,0xa4f280e3be2dd6eeb9903c82e8b63070e4923f1c -16696,0xab14ad7ed56b9dd0b613aec091c48c5136651c4e -16660,0xafe29095e6a9648ac7a0f211a5490420a20b5cff -16694,0xb21e5e9f40d0eda3098179f3199a68e2e53a2e61 -16679,0xc25861ab244482b739777ff4766b446cc6261f68 -16681,0xc6bf3eadc620bdb902d3d536b57ffe071c68ebef -16688,0xc87b21f01652769f00309d59420d5e1663f5ef24 -16675,0xcabe7f84339ad635a6fcaf6e297ee04a4d5eb6d1 -16680,0xcf1b7131da460d7f1fda8605552df995311cab31 -16695,0xd1db2b4080aa1163f2526705fd08f49b7e7dacd6 -16689,0xdd10c1106e108f963462b6f7ab9fe36985f4efd1 -16667,0xdd5b9977ddfce8a739a285d601489f81481accec -16665,0xddda81cb8277daa757b8bddbd2d92cd77a32c313 -16673,0xe7c0ac93420fd84f3335dc5270f78b03e0b07a61 -16662,0xe91e96eabaf07e7794be56fdb235ad323f13e5dc -16671,0xeb739b708456f62149bf8399b37deaccd96e4d69 -16705,0xef5565737ae21059e6a70da1ba3c9f5fa1d59942 -16686,0xf09ddd5f62b04a545ff5df1f201cc85f17cc7e8b -16672,0xf48250d5f65a71d3229a03c73a7d9047954cab81 -16701,0xf7c3ba0f32940680df61f794fc71aa180851fe0e -16659,0xf8cb702d8eba5f130e22c7246fa1a8e7749e26fd -16883,0x010bf477da15b9b59cc1cecb0718523be5a0b2e0 -16868,0x0a24cfff8c8627cb54f84d93d32072550cf8fd17 -16863,0x168cf7855cd51140df3ed5ffe8d543834424716c -16882,0x18d33cd0dd35723a7b573ade373ae9de7166dc70 -16877,0x2a5ffa48971b85909034fb721302c10a80b78d76 -16864,0x46cc761e3caf4e2c6f3f7ec08dcfb8adb5f7f4c5 -16862,0x5827094484b93989d1b75b12a57989f49e3b88b0 -16871,0x59abafba0ec327bec39ae7eb08149a3f545e741c -16880,0x667565fcfe577a7a9fb322ce0938d2b37de968d0 -16872,0x6765a69c47b3c675329317acb415dd90fe368014 -16869,0x6dc08ccffca5e7d21d80a533e89491a40fe7e2c7 -16881,0x6e42b4c8396584488e6449d65591e757f2730863 -16866,0x6f078e7358411f3aff37819ea1cf16b6d31b0b10 -16861,0x713e400b032b89db9f68105e501ff13260398490 -16876,0x7f74479576b2589a0c369efd3ca417d9e5961220 -16879,0x7f988764fa78c86dc9df157c12039741210a820e -16860,0x7fdb3a3a97d3f0ea969b14fe80666bdc43241da5 -16865,0x8d826ae53d0d7acfa97db71c00cb26aa4ce44e52 -16870,0x9964aadd09451170d0e229839db90be2dbb202ba -16873,0xa13ce1130e34fd9e96cbe5c3106bb9e765e76f7c -16884,0xa2700565e6fb4ff77fb88a4fbaf778569567280a -16874,0xc8bc4156abb364684e8ff232f221ca057d83aa64 -16867,0xe381bc8c7c342b568f476e6b39698c2edf468f5f -16875,0xe907afab6dc3f0e9b393ec037fecfd2aa14f077f -16878,0xf86e8510c9f73f7a8f1f1f31024a71aa82d734d4 -17119,0x0fff7f99d2b32849848e31cb48090c5268e06f65 -17143,0x13e98f9a3449c7372fc4538e531dca17b0ac2e0c -17141,0x150fb0cfa5bf3d4023ba198c725b6dcbc1577f21 -17131,0x1734a5eab695d9b7c678adaa9a479dbb88897660 -17126,0x27f80488078bc485c9cf5a989fcbab27d561ced9 -17127,0x2b2eda765282f6265101c0f72157d50f8f0eb815 -17118,0x3a506575bf5202ef846ce4c88eea8adfba63f760 -17144,0x3d8a79ed1e21e6295a24dc4fe5462e8473666259 -17128,0x3ee96e771d5e56b34245b023e8b31ffdf36dfafd -17130,0x40c6e25b2b7cf95afd323905ecb4901eed31043a -17142,0x449d088c9f184af598fe72d26742a58a11c5200f -17125,0x44a9fe115ef9d718821b62ece50607bcb334d29a -17140,0x61634a46e6e58581253452844bc0c82bda7d46a5 -17113,0x6692de0c00dbb510ecb569700a423f2668f308a4 -17114,0x6bdc58d3287176b40ad2243ec0cf35f33f2119da -17117,0x743fdf479b8894fc6dd24f92823659934dd30d3f -17135,0x76bc5affbc9dacbc1e6a18d7135def47605bae8e -17139,0x86027f965539279cc3ac752ca5940b6cac6ebc99 -17133,0x9a065e500cdcd01c0a506b0eb1a8b060b0ce1379 -17134,0x9cf434dc3d2ac5f8d203e56c1320fd7b098de5dc -17132,0xa5c5aeae9fb5424f9ab6d2e4c1c108f988ae3f6e -17136,0xa91be36cc6ef0013046b0b0fd65fed842b11edf1 -17122,0xae5fab55c6aa6bba24eef3fb98c48a0b616b163d -17120,0xbd847b0bd0f78f46176dbab7b187ce2934ad6171 -17115,0xd75af988b2282c1b466c7640b01e4b89239a72d8 -17138,0xd87c56d1698e52f6e3363b9ffd09b0afd46dda8e -17129,0xdfcfe6ad28c18f4078b9f0d52017c406ec033064 -17124,0xe2271de54462d97551ab933c546a54a903eb02fa -17121,0xe8ec1a3a637c47d2a17378eb199b523bc2742272 -17116,0xf81072908070a0f290ef149e3a93187ad1daf449 -17137,0xf84d9a7ed9a885f5d81d6de6476b58682fc9a6d2 -17123,0xf8639f6d597e1ae2b697e026218ed9bf47aefdd8 -17236,0x0ab49bcd888df8bad07a4f8f924a716bf5cf5f0e -17260,0x0eb8645e28d547e258932fdfc65e5136cc5bd3a3 -17229,0x109962e788aad540be3e35a7c14460707b07f037 -17256,0x238ad16ea6011d6481c1d38e35b0b5541e5a513f -17230,0x29b834b4b22224b18f1df3139e11dcc348cbc90b -17244,0x39d653884b611e0a8dbdb9720ad5d75642fd544b -17251,0x3a5fc9abaab71d0331850e9b4220ecd205e5c4a5 -17250,0x43bf4da10702fd5b11f575b284a245502fb2b236 -17246,0x497c0d498efe06d99e7cd3334c7a2865e63c5c7c -17248,0x49bb201a032f75465bbab5a5e24df928c138ac26 -17240,0x572e9467b2585c3ab6d9cbeeed9619fd168254d5 -17243,0x68e13c91fc1b8dca877d88d5d48e7b4bc9d6afc3 -17245,0x69bc68d618c7012a764821f4581c8caa6832458f -17249,0x6af2b0b1867b5e654e1bb86d97f0ff649dddb353 -17238,0x6d0a554987b89985273e811a64f37967946ced53 -17237,0x6fde9892fd5302ac3c68688085bd5b031a63bc9d -17252,0x72e6fbf88a8c0b98a28219a573d7e8ac4a741846 -17242,0x76993e4e6b32a35053bc07912d82a0764009a529 -17239,0x7e647af347da9122684e39a456562ccf6b09b391 -17253,0x7f58f374916a612858eba1b37d7300e94785d6a0 -17259,0x8645f18a64db40660637fb362d7da7cc9d422c49 -17258,0x86c90fc464a668469a93ca08d8b9872bdb16b356 -17232,0x9a383f154ee344d51381727395a6c9004c6494b6 -17254,0xa08794b19d231a0d8f8f3e09a9b7b9d11322b491 -17231,0xb3568690bfa5ed29da99ec2f395ff1859cc90b9e -17261,0xb362b8cc8c00c04447f1004f3da7f22d87987cd2 -17235,0xbdf05217b132e82ba44c61052fe0551eee043e50 -17234,0xc28c0267b6b47c5ff47366b6d374522875b8972e -17233,0xc6bd76fa1e9e789345e003b361e4a0037dfb7260 -17247,0xc9296e12e2fe55605d9f6db5412eaa1938f0b404 -17257,0xdf47a8c7a789dceb51c2dcc9b76d2f0d8357b57d -17255,0xdf88fe94ef674d8c1ab1743ad88717e7ae893a44 -17241,0xfec3704f4a02cb0ee6c7d52cbf72b11e0441e9d5 -17300,0x01e77288b38b416f972428d562454fb329350bac -17297,0x01e9b35785ef3f7ef2677c371442976bd550f320 -17293,0x04f23404553fcc388ec73110a0206dd2e76a6d95 -17305,0x064b59619727b5e712155eb27504e9c9a62dec27 -17317,0x08dfcfb0bd6e7eaf828f60021c77d273de56b1c5 -17286,0x0ce61aaf89500e4f007884fbbf62642618def5dd -17278,0x0d8393cea30df4fafa7f00f333a62dee451935c1 -17313,0x0ee3fd031d45bdd0e8ef58d5514e2312f5a74a0f -17280,0x198634d01a8e1646fae8676904343c33b1d2c6b9 -17343,0x27f59c92e26ea651370a4f18ab6da5b7c3458586 -17321,0x28cfd8e1e83bec8ee879d21634e7dc3d93102ced -17336,0x2a3e489f713ab6f652af930555b5bb3422711ac1 -17315,0x2aef1bc2f13118009df8663705f736f77efafd5c -17296,0x2d270f66fee6ac9e27ff6551af5a8cfb5c8a7493 -17319,0x304951d7172bcada54ccac1e4674862b3d5b3d5b -17330,0x3662875bf9c4614bc5d2a615f19517c2d0039f04 -17311,0x385719545ef34d457a88e723504544a53f0ad9bc -17344,0x3b2732c1e5a248bbdd0315e9e8845c64f5a6faf5 -17275,0x3c5764cfe68d2b8b76971c8c2211e59f6c5746ab -17290,0x41cd8cafc24a771031b9eb9c57cfc94d86045eb6 -17287,0x4c4d81aa29fbb7fe90172ad70a72f00fb1ee46c6 -17310,0x4f697dedf2b3862ca43493c33589f42cfef66026 -17274,0x508a85c04351a3c1870ac5b76555dfd192938f0c -17304,0x511a4cef565fb8094fb71e7ce735bc578cb57b86 -17323,0x52cc60893d3bd8508baab835620cbf9ddfa0a13c -17288,0x57f67286b6d1ce0623766022bcc2fafe80b32c9a -17333,0x5f57143604fe1a940deb5f95bf1c78a252791437 -17303,0x641ff5d17178c3b9498133ebc264bb4170a04668 -17314,0x65dcb38637b526305be55f14b24a4ab2bd177780 -17282,0x676d9fd3908112dca14b89503d6d95bf25d85831 -17325,0x68d1ca32aee9a73534429d8376743bf222ff1870 -17340,0x702488750a4efda4f18912291fd4e65e51135df8 -17307,0x727db8fa7861340d49d13ea78321d0c9a1a79cd5 -17283,0x766257b7ce3a5e3ad5876200ab1671d31289bc75 -17326,0x7b26207457a9f8ff4fd21a7a0434066935f1d8e7 -17328,0x7b9e962dd8aed0db9a1d8a2d7a962ad8b871ce4f -17327,0x8082b086a8cd851a9690a03070e82737f37a548a -17338,0x88dee1b0ae1c9f93fd4cfb3b2f02fa54d2e09d2d -17281,0x8ad880a11172235c6800ae882cd699ffe0172fba -17292,0x8cbc45d772d2f127693abb8942d46fadef198b4a -17334,0x9032aed8c1f2139e04c1ad6d9f75bdf1d6e5cf5c -17318,0x96a0f50db7b08bdaf2d7af3842f007a21e427126 -17347,0x99901e6e6ac72b9f3da4c82af1f86d8be51d09d1 -17337,0xa0207802b0953b17421bf9cb10b3acf5823496ab -17342,0xa1a65d3639a1efbfb18c82003330a4b1fb620c5a -17324,0xa25d432ae720e57e85778905935a9a0b9284114a -17299,0xa28d4c3709136eb4d07058b321a000c7ff634f40 -17346,0xa30b1c3c8ede3841de05f9cdd4d0e097ac4c6d92 -17291,0xa48996d94322f740dcc5b182827cc66a9faae9da -17295,0xa6d7d0e650aa40ffa42d845a354c12c2bc0ab15f -17298,0xaca582a0422881cd3aa8bc371e8817a5cbd0161a -17329,0xad96d9736484e35985773bdf7dba5750dc830042 -17302,0xb280d03909cc18640621955cb3bc30948df2fa9a -17312,0xb2823674bb50a370d22dd8b26663f61e38822d13 -17345,0xb2a477c6ba5e96f6decbced836cb7d3d32ef9ecd -17276,0xb54abe3fec8e1ae64620185a1e111aa0c3a81542 -17277,0xbc34f24c675a3dafd181d1d4243cbad5d9134c84 -17309,0xc1d4e4743dde709831e1cb485dcdbd44def893b0 -17301,0xc2c5968e16ec9fabc39e27f9abfc07c8cfba6f16 -17284,0xc37ad9d78fb001a2429b2401e91c2dd849595798 -17308,0xc504cb49bcadfc8fd56f1de5674ba216aeafef01 -17279,0xc5129208cb1dc2b3c916011c9d94632e602b9811 -17306,0xc592b3add24ea30c030978e5e7f5d02e123b4263 -17341,0xcab2c0a41556149330f4223c9b76d93c610dafe6 -17285,0xcebc8a8b66de9b3f4393966eab86fc9de26b2529 -17332,0xd451ffd204906c1a725c65efe570ad8a6e2b08bd -17331,0xd94452957bc47bc59985013bc087ad5a55f251ac -17335,0xe149164d8eca659e8912dbdec35e3f7e71fb5789 -17322,0xe717a032f5400731a42540a958d2e3177ae9fe4a -17289,0xf75d3c6f20fb65cc0b8f84687e5cbac3d7f4221e -17294,0xf90bbc59dc4698be69a7b98bbb4bc1628bcec4b7 -17339,0xfa8e13441ad7fc9f0c7fc8fccc5513ce33d27ef1 -17316,0xfdc9b5be032216315bbe8c06b1c4f563d1689b85 -17320,0xffb69477fee0daeb64e7de89b57846afa990e99c -17477,0x004d8438f4016a96f2d6ddc17808f4e40b47cde6 -17493,0x1365e01c8ab4c6cd8db2fe9ea640cd64c2239087 -17488,0x1648323b9526367fa7d3dbadae19f2e3ececbd14 -17502,0x1c1b5e12a8a360a6eb29302358879eed8954c9f6 -17484,0x25d83cd5205b76b7b1407f8b18bcb9bded3928d2 -17479,0x2c4a94874e4dd52153ce2f2a4574b858928c1804 -17489,0x33ce254f40ca40fad2ce3d8c78a0b1660cc4bbb0 -17491,0x3cbdf3f47cbc1a6a2e64eafd1712bb5e5c186df0 -17498,0x3f2408693cc2e0c8e0bb68f039ceb6deac0ec072 -17494,0x4bc186b659a50b6aa09d853988c20d97dd899753 -17480,0x5103ca9c34277570ed7c8f863a07db7868d4f278 -17503,0x517bab7661c315c63c6465eed1b4248e6f7fe183 -17468,0x53d4f11bba4350a904c52fe9357b70ec18193901 -17475,0x55dcbb4e42e0662e0a8b9c39a8eaf7a36f34ae77 -17470,0x5c61fa95b6ba81ead454e579c3809aec324ee138 -17482,0x680f5da83cc5327203ef6ddec4b941363fa37086 -17483,0x6a4e77905d107bfc8bac70afddb14f9a9d41cfc4 -17497,0x6b9d932c38c84904b5f97a514fc1fc7a1c1b5e0b -17472,0x6da5f047b1ec9e822f132efdf02e76eddf29dbf5 -17492,0x72906247d4aa8a9a12895362e728554fd96d0eb0 -17485,0x73a31b27ed067b3612d941973407306207d67bc4 -17471,0x7b44d15c256b6c60d60a8d7c858a883a01bfd4d4 -17473,0x873f43085aec1a18f253edb7398b77cc361d8d14 -17500,0x8edc2cfca57c92133450b8f34e14d50260c39d38 -17474,0x96d61648dbc12c3d1c3f0f0e1d53f14eeb504ea1 -17496,0x99dfbf22d2d6d49086bbabeb79a9c3b635020054 -17487,0xa698713a3bc386970cdc95a720b5754cc0f96931 -17499,0xb746c7cb1f1394a904e7df55387897aff475fefd -17505,0xb962eb5436076d0b092704c8db0332b0aa6d737b -17476,0xc0ec9535fd3eda2a868c631d77721b258c2802bf -17478,0xcbb91676a6e26f3a20b6ce8dfc91fc42d6bdf64f -17486,0xcef74aedde1a8e30f5485380d4cadb0ed7e46697 -17490,0xeb53cb07320ed7def5d6e1664df7fb8841062377 -17469,0xec6a98c5605ac12603b178f1b6fff2ecc0d98124 -17481,0xef566f012080371d0facb933b81a2dbc9a598396 -17495,0xfd7c7f0b10acdd4f2ee2ea5111767b98d42d0a07 -17501,0xfd8077f228e5cd9ded1b558ac21f98ecf18f1a28 -17690,0x0557b0a016166fa113cc090869c3d98bbfe911fa -17675,0x084a7d7eb284dd74b1e29940a3745c8b59db9a19 -17685,0x119b9ce7b2087153d02c8aa8f7dc914bb7e5fb5f -17691,0x12e4844258f76c07c6e4437d6c9ba3df7d81c723 -17671,0x1610e3c85dd44af31ed7f33a63642012dca0c5a5 -17681,0x1e6039574bbf6b1f65650bc50b2bca8911fd9b27 -17676,0x2355c18b6bb0601945f42feb2f13287af8baf74b -17659,0x25dcb9f75b4e401606ccdbc8db121e444f805f19 -17682,0x25ee6ea9353e0ffa3155655f3df9140684671f36 -17683,0x293aac1fef48b2ebf95d0cb3a31a7b219e8ece9e -17673,0x33bca143d9b41322479e8d26072a00a352404721 -17687,0x340972195f008deec31c70700a45d6e28818cf52 -17655,0x39f7fc2ff177057bbb5b680a443e4118cc28af3d -17663,0x4c6bf87b7fc1c8db85877151c6ede38ed27c34f6 -17680,0x4e71790712424f246358d08a4de6c9896482de64 -17679,0x564baa321227abf6b2e88a38557b6517077aad32 -17672,0x5a962457060445c1e60299d735c8539d61b4ba54 -17678,0x5c18f45c4c62b0687425598579b026b90785c28e -17693,0x62316768ef954734445aab3273fd6077005ffabf -17660,0x6394152946dc3e0babaa474ee9d366ef31f959c0 -17668,0x6b53c16b94c1502c661140073ed522ac7dbc5e5e -17684,0x6dd3bdfb588df6f37ed9cba3bd17b43d1208707c -17665,0x76d20a609a9dc010b9e6d479c360458b19bf3256 -17674,0x77256d49ab301c608f8ffa466936ccf84d07a41c -17669,0x82a9c9f5ffe06f2e5f403b8f90e13388c6c07085 -17664,0x8d8af8c47d9277ea8d3c8f5b0873a0aaf6f7f9d3 -17666,0x9dabae7274d28a45f0b65bf8ed201a5731492ca0 -17688,0xabf27b8e4da617fff2e666f71c137d71cf75b5f6 -17670,0xb55ced4d5f7346a6601ebebddc98d0415c94095a -17656,0xbd09366bc8fba86fa5391d5017806b49b4f1710b -17661,0xbfeb32a40a0678208c28cfe4438bcbba96bbc4c1 -17662,0xc070472392fb85938368d3caeb9ba12f43c495dd -17677,0xd2e32323686de92411639d446396afa5e6149c28 -17658,0xe7c65eaeb1ca920f0db73cdfb4915dd31472a6a1 -17686,0xebe91f52766dd236b6e8c1951f6a4a8bcc47a71e -17689,0xece3ee44388d4957782445a90290ba41ca3e32c0 -17692,0xf02480fcfab8b6c9a37236f71d5358b4c88a2e7f -17667,0xf538f18c3cc1cdb70aa054cc3d0c2c2262ba95e6 -17657,0xfc0c88ee4f41e15499808a48965eb541b4d57f17 -17964,0x02959fc5ddec66bf43cc09fc23ae65904a3dc138 -17950,0x060cb853027504f3df5f397459e6a98906b7dc3b -17974,0x087d4b1b3c44c1fac25a1fd693fa292f7adb3e18 -17985,0x0a4b586d26ad35423fb1e118e2c0b53d6931beaa -17966,0x0bb5ce6623ab71b4ee867a5d3928de42acb31acd -17957,0x1108db097e1515445e205d465be9ecb33a0577e4 -17945,0x1511133657d9629e14ec25953289220987cd62fd -17948,0x17e32a73e1fefe7e0a066148e48144c8382d8a79 -17972,0x1b7d71a4c025e4d2fe9db957073284eb9d321856 -17965,0x1ba87ba3bab69b62c193a14a772e05ea84e8eb0c -17947,0x2295c5f100bb23085bee312be1d33d6f7f02594a -17977,0x24682cfdc060316355c26c420d0748f289502e83 -17954,0x26e9588c5715141a95e8471f050a3c9770551921 -17975,0x332d5216a29a856127f290f20dd774f584e819ef -17944,0x3e5d9d8a63cc8a88748f229999cf59487e90721e -17991,0x463f3d041bd82cdd7815a3ecc7fc31e9a91e3514 -17978,0x469cc7af1696b5e4e6151796dc3b3fef39b34f39 -17955,0x474e3f554bf8c1f073c8804eff6a16a7094b98f5 -17971,0x48c73b7b5c41a5c8f0c3f0e8e098886ed8b63da0 -17983,0x4923ec641bbb6ca5cc46af829a90639371e28398 -17984,0x4d358448863a6a3ee228fb8e1d68697747b7e845 -17976,0x5b7c5daa5f4bb37c457da468da1cdaa6219892a1 -17970,0x5d043cf6e76657191323c597c761591b23b1a914 -17949,0x5e096b0b4b8735f6a204199664fdbcdf3b351cc1 -17988,0x6d3cf2b5231a9e7b2926b512c60ceee5d53cf2bd -17980,0x787c68f6bcab352ec871c522d038bc7a30268020 -17981,0x79fd3dcb2fffb4a0ebf6fa2fe4ed0e295a1ae6e8 -17973,0x8400267e054e891072856507e58fbe8aa75ae7c1 -17958,0x85ec232ae615ffc0523655a3af99257b688672b8 -17962,0x96cbcfaffbb0885f78627a451f52ef9fd4cb6821 -17961,0x999a69c9bd8208736de69fffc2c2ff351e982e56 -17963,0xa43bebee413f3d9b35d080c86b9c0a4bc6f5e26f -17986,0xb3b8944268c651da419dc0cf94a4f012e0189e81 -17953,0xb4c6e1b5830a37a168dc469481f9b53ec96507b4 -17956,0xb7ab50b456333cf561eb7d031837d680c9e25419 -17959,0xb854a17f5d64ff0f5e3194a15bb9a11d3f153adb -17946,0xc2113e72eb78b2e025c6fc81809f5d4f5ab81433 -17969,0xc255756020c43a41f039e1493e6a8efe2e5ad721 -17990,0xc5bbe6c7e97fca74987c5dedcf7bf9ef651fe76b -17979,0xc9394748d5f633152ad3f8f557a9b7743148db1b -17960,0xc9e6a4f5df4d1f0db088fc469081e40d66e2b982 -17989,0xd181819e5670e4367355ae42e92c7e0e4b245f59 -17968,0xd8bb8167d66a12053710c9294b27f7fac33d9a75 -17952,0xe7f4820b8e9134e5fc58ebbe19197047f8c9a9d0 -17982,0xe86020827a65036705c8b09690ff4750c62ebce2 -17967,0xe9c3156ad523437949b64ab78fe5f75d8ec90500 -17951,0xed7ad143d99313a1af1bf6761ba063a2a2a145db -17987,0xf106d078ee2b861d1b07e117e91394e9a2914467 -18141,0x03650533d2a35847c007f25bc630f468905f6241 -18123,0x14eda572634627389226153f9fa5366a90a660db -18131,0x2c42d3dbb5a6e54d52f194546c464bb5d47925df -18124,0x328330f2c17a55e7e36c78972947e22f2f43cbb5 -18128,0x395c38f055522c7eebd85e77dc59508a0e4115aa -18133,0x4af60d4478e2e4bf64a57fa4cef050c526980931 -18138,0x4df5f7c6d69a24aa1a87beee5a374a40c8f9afd3 -18122,0x529c2945c257cc737a15733d43a1fd1cbbe79c67 -18134,0x53be31942c2113633bdfe34626bdc60c7f1d12b7 -18125,0x5947b60a2e22939fd5f21efdb5e8e72e1f95c8a6 -18135,0x59b38e68423f4b566600f632248ec0cf13c79ade -18129,0x5b00e2a79d721fc22c6534273419e47a1159feae -18139,0x652e24c7014bb1563aea3e82c03c4ba0afec594d -18136,0x781d50ea025751162b909897ac95fa05c30e2c7c -18127,0x7ee5866de93f2de8e435715666a9e8f0a63f2319 -18132,0x7ef8f2a8048948d43642e0358a183147e154550a -18130,0xa84442f3853ce0d57f8a89ec0675044e502b8af7 -18121,0xac937ad539bb9737892ca9ffacff00d40311c0ca -18140,0xdd3d867db5ca13e5f055dd4484648fdddf3248eb -18126,0xe7fb36b4a3787cfa32fde7affded0cfb6825f255 -18137,0xeee21c29c67a4dc177c3371296dbb2c45911c487 -18213,0x0100a1c3020fe97051cbb18636c0f91abcc603ce -18216,0x09ade44d2e60fca2270ff32af5a189f40d29837b -18233,0x19bb8c1130649bd2a114c2f2d4c3a6afa3bd4944 -18225,0x1ed2957ba1f14c17e01424dd93a258dd2e04caac -18214,0x230c63702d1b5034461ab2ca889a30e343d81349 -18223,0x24f85583faa9f8bd0b8aa7b1d1f4f53f0f450038 -18232,0x285383bf3ef9c8a30b3ed40f3c9e1ab63c970cae -18231,0x2dfb2c5c013826a0728440d8036305b254ad9cce -18220,0x38d504062dc7059b11e87bf818ef573a4eccaede -18221,0x4afd06674e0701f3bfd6f026e4ec785f5ec3e29f -18222,0x507eb4c22a5088645e7b610ef385bf1c5452a539 -18228,0x579280acf998a031922421a2f6c62ba609be44f7 -18219,0x615dccb17f762cae61a36a336a1340afce764c71 -18217,0x70ee0aa330e3590f6348fd54e71fd18ad3bc914f -18212,0x749fc5a81b2fe1470e54e1bf452b71a4fb0e1bbf -18226,0x83d0e7265cab5c14d9306322262cdc477ff10db1 -18218,0x8847480c34c7dceef73647c57766f28c3a07596b -18224,0x96ae36292c522f375d8800832b81cc5fb6667b1a -18215,0xb4edfb45446c6a207643ea846bfa42021ce5ae11 -18229,0xc43b70b96b7d8d0faf4c8dd751e4935d25c33e71 -18227,0xe0f0eea2bdafcb913a2b2b7938c0fce1a39f5754 -18268,0xeeda95f4513f950957ae84e4da221ee260fa2f40 -18230,0xff26a319f125291abc37eda4d6ff06850e732689 -18303,0x0e8bd9f16728912c441bb42f075bdc5593a1cce3 -18311,0x1b36291ff8f503cfb4e3babe198a40398bcf54ad -18305,0x28a72d7e0f1fab1d78f3c4cdd7880c3b01b8a519 -18304,0x64271fbe13b8eb41cfdc063ca92bbc1105441b10 -18307,0x664649b2f36506512b3a75dd4a1bac7c883a3203 -18306,0x6a752159fb2d4450b2cbe003ad108f21299aa642 -18308,0xc027ce27fe1505bccfaf23c5802d4cd2fc607e76 -18309,0xd23d6f5d5386cb6b6521f50b7a8d40abb6d9e4c3 -19086,0x006e1a3bea6637ada975601d030456b76276770b -19076,0x0104eaea36d80d29c43b2480de7e8f4eda94a40c -18813,0x019f0233c0277b9422fcdb1213b09c86f5f27d87 -18974,0x024228180623454be6c6cd0e6588c3e604c730b7 -18842,0x0474be53472280f96027b901c0367d2ac7ad539f -19041,0x055485db0cda2832060cb66dcbfb679466e194c2 -19023,0x08525e12a2f4022ea780367bdc9e73b7358b153f -18976,0x08cd215f6b37981c844543aeaafededf48eaaa05 -18815,0x0a486d20d81daf2926519542941f3cab9d3b8f26 -19030,0x0a6d9a32565b131c68f4fbb4a6626c5eec3d3c3e -18754,0x0bfb21f64e414ff616ac54853e52679eedb22dd2 -18694,0x0c259dd157a8224a480e1e7d783fa25e45bd3d01 -18947,0x0c288a692a7ccdd765fa8c163959899f9cb49fe4 -18839,0x0d1a91354a387a1e9e8fcd8f576670c4c3b723ca -18915,0x0eba1d55a75167a4c1c6f6d4ea943bce8d707956 -18866,0x0ecd5dcbf78511e6009e3f214bfd6923a208d655 -18997,0x0f2edc4fa49e44a5bbe9113b24e16311f186fc12 -18948,0x0fed189bcd4a680e05b153dc7c3dc87004e162fb -18998,0x103b154097d9bde890d82afb9b473de595ce8499 -18680,0x1114bcebf75ccff06112d9951d2ab30a35f4edfc -18935,0x12a4fd54aa321eb16b45310ccb177bd87c6ae447 -18923,0x136d92f1d103ba5267c85555b28787ae53ee3cef -18768,0x14aee15e04d5257aa3015360d5daa7926eb3bf4a -18925,0x17a465a138b35c0f1e52fa44d27d0b34fa0ad2f9 -18836,0x180274b8d9e1f933261f743b53d0bfebe475d1bd -18885,0x19f8f57fc4d3fb9201d4ea93ea598b8c5c9958f2 -18722,0x1c6c6fff766819ac1531476f65703c528a8e3858 -18705,0x1d42a98848e022908069c2c545ae44cc78509bc8 -18681,0x1d851e3bc5e4b756810560dcb18b959b9d3ea212 -19007,0x1e66dea8c700af758f0f1bf94009018d4e524f58 -18898,0x1fa4e370855f099861e797dce8352a3afd704bc8 -18709,0x1fc498f76c4f16931d983baa2f32dfcf1eb54922 -18944,0x211652aa6a767600a0734432b20f7edccd4fa878 -18682,0x2156b225c370cb3b9e0459f8f6d54f38461c4129 -18699,0x225cd217def1a028a2a5af7729293792c3f760a2 -18853,0x2348aedb554f1dedb5d2b632dbf6c14678c306f4 -19006,0x2375a13774966404effdc4e682831f64b94b193a -18868,0x237cc34c07fea35f4adfb52d5b4ffc64d040f0db -18708,0x239ce352c63ee374a8eabbfe76be2ef47125dbcd -18920,0x23b9fd22ae32f81b8a20fb2187a7e4a87b180ed3 -18834,0x23fd8a6e5ed129104e24ce89335e1faf21d8fc4c -18979,0x2562a4340b5a76aa3ca68fa80847c5d16e02d20f -18982,0x259b792cbf1d03cc5cd4acd3bd04c7542526abbb -18862,0x26379130163bf1e2e2cb7daff1914fbe72d3c180 -18876,0x26b734069652392f2dd0deff61407917bce39985 -18751,0x26cf967e466d9fd60af7d1b78a01c43e75e03b32 -18989,0x26ee698ef56ad44ffcbbcd21681a732ddb45158e -18808,0x2767fff2220fd7ad4f87b2e441f275d8f895e85f -18952,0x2772fea18737f363cf5594f6500ed67ee722d47a -18882,0x28a0f8c67201ef2312e1be980bb80e62cfcde44e -19033,0x292a5929bd150d28eda3c17d9b7c754968b2899d -18965,0x29621d6f64fbadade1561a19029724b81eb88ce7 -18929,0x29a90650c063c05a11087b70ec4d4271c302f7fb -18946,0x2a21bfca834d122769cdf9d50b609ca6b210b7f0 -19047,0x2b022cf1fa3423060ba343adc2e587341be36c46 -19057,0x2b1df9a55ceb1bba7d830c1a6731ff37383c4a53 -18838,0x2bb55956f859fe12acb73f14975b9c86a6c08666 -18975,0x2bc1167b2419f66211eff7c380c09b4d33a770f1 -18962,0x2de3e40add67e90dca00030e46a631cf05c8cd0f -18999,0x31e34082fdc93aa4e1abe66853e1a60b8c0ec040 -18934,0x329dda02831c87b8d413498ace7b4d12a8d9a637 -18907,0x32c29ce7c09e1a21c97403776d5b75e3047c3089 -18888,0x3423825787d3c8783c6cb95bc0203adcc88a685f -19078,0x349f7d3fed51a3e49722c87981995cc33521ad2b -18904,0x35e42acf2e04286699b16da4004ed67a8a998eed -18799,0x39032bd0315f595276f4b612eb29251df739de44 -18715,0x3904bd7c310591a04ab0cecc3664707bb35381a5 -18939,0x3916947307263e5fd1b3e1a9f6a5aa02b96c187a -19032,0x3a75175020ef28fb7d62161b90340d73873ab71e -18889,0x3b074730681f9c2469226d56d08689253dc2b02f -19079,0x3bce802bc923aea7e5bc46cf8c2c1c968011be96 -18894,0x3c271d441e826e1be1b9d65a02acebe69ae1c441 -19044,0x3c3ffe040016d31a81bec1f7c21803a56942eac4 -18850,0x3c73cd65d708a5c951f0cc19a4d0bb6559ae20c5 -18837,0x3cca343cbddc9923a19212ff5b3ee3f8d989dd47 -18847,0x3cee1d7a35e8b961db2bbae6636cc54ca10a850d -18721,0x3d96418b63749df7d6c31ad66ecbee210b1456b4 -18928,0x3d9bbe5b526d03fbd7f4de5f7d4d99cf8be56e98 -18683,0x3dcc1c7c4d8a28f34c4d592f701a5276202a96a5 -19035,0x3df4dfcfe9df655617ce6d16732553541a33cc09 -18785,0x3e86b53e1d7da7edba225c3a218d0b5a7544fdfd -18860,0x3ec2a74bd980e79d85e7997d71685fca08a7f45a -18763,0x409f9a1ee61e94b91b11e3696df2108efc7c3ef5 -18864,0x40ce017206e05b20959bddc44a98cebcdd7b3d24 -19018,0x40da1654a7c6e6c88b56a865e0c02f84a0a902f7 -18826,0x422db0a9aef5107637ae809b2709168dd7934dfd -19071,0x4266dfa5a8a901c98f40ed2f9d349ce3a1137902 -18865,0x426d95e02deb60c26d8e152b81280503c8b92d88 -18902,0x42899f01b68ac144a4d6ccc71bc016282e926d06 -19082,0x42998109b3ff1216766ea613557fd7e551e651de -19036,0x438ed56c19a1fe28899eb03361640153027b815b -18718,0x43bc8ab493af3dc2c8ebfdb3df74eb4dea80c682 -18782,0x43d695438c01075e33543b2ef2a5fc35b7c8a827 -18820,0x442419d940678fb714e22af573ffbab76ee26c66 -19063,0x453b8bd47cc2bf07b30e57d18391c7464ab7f958 -19064,0x4543c7b5344296046370e3e478366b8a6089b0c4 -18805,0x45a388af13c23bfc1bcd85c94572616a40b51504 -18991,0x466e84ef6bd25e226815ef975e39e9cbf66c08ef -18750,0x46bca9208b95886724b11799a41f9125ef7f057c -18910,0x4851c5f474e13b41f6b62534e586ab80ed33ac55 -18812,0x486f38d8429363cee8d757a33796fb178fa07e43 -18922,0x4a3448d86d544b8daca6bd54337003a8b964301f -18893,0x4b23839e0cd4b485ef2f1ee468cca02392b37546 -18917,0x4b3cda1438e75cf4f01932f9594bf8cdd3c2c702 -18724,0x4b94d17cee6e434bc6a6c193f140bdde6666ecc5 -18964,0x4b9bf0858f74c1caef026b4b558f68897cfdf309 -19048,0x4cbf6ccd7a96d2198a5bedbfbfe5e6aa1043c348 -19014,0x4dedc1fad6788cdba4677e2304d18531ed3ef501 -18861,0x4df435855f1ed08a83f5a86ddc3d3ff249e82381 -19066,0x4e347ec38d85564a856c1e31d7e1e4e14163df6a -19051,0x4f39bb97bb80d23b9d271e2206c43f9c4216a378 -18992,0x50e17d0d2b81ab7a84ba695ac84beb4819452346 -19037,0x5151a2aae3cafd8631218bc6b58de8a838c30c4a -19022,0x51705eef573ab874367b23e64aff2e2d196a7160 -19075,0x531dde5ea5443d3450b1290268bc416cc1c8ea56 -18897,0x54155995b31dd1f983723aaa1273ba29be54cb70 -18958,0x54165f98a950060b3041d7e2e674c711c8036839 -19058,0x54e401dc9fc6e496062cb3a1c882f10109d9e66a -18832,0x5569f537ac368fd499ca6ee54627ce58a1da04fe -19094,0x56360201071598d1a85554f7027815fafd216805 -18685,0x576e081376ef0171152297020976cf35e47a5fbf -19000,0x586fc438b7bb94347c41c37e04451ac5b2caa7ad -18971,0x5913e76655bf6c026b28c81648baf39bc2a5d0ab -19045,0x59c671b1a1f261fb2192974b43ce1608aefd328e -19080,0x5adac40459a37368ab39ec40ab8098f3354be4da -18759,0x5adacfe0f7a0a4e69ba5cd25f13c37cdfba13e32 -18672,0x5c665b488566dfecb3641a7a9b5730a5c0bc1f9f -18927,0x5c8e217a367822d17369c1ce5fba06ea8a2cc235 -18704,0x5cdbd5b2b6d66d37cc9620f3047f90e5902d54f1 -18811,0x5db73886c4730dbf3c562ebf8044e19e8c93843e -18895,0x5e1b6cfb26637a3aea3aa4fbf0ecd5c020fbe83d -18960,0x5ecaa65b448fd06579418ce79a75b9568d09be97 -18675,0x60a5159bafb2198b967021ac77e26c1417081477 -18697,0x61c9e8138e2a810bd6723f9c1893d4275b8e7c5f -18802,0x64aa025819321e97cd829b9b6c45a1424ef9a80b -18932,0x64d2fdcb9572d171157b39d11ba108e222dba300 -18918,0x64e4ae29a1dcdb95740fe3146429a9d20881496e -18783,0x6555c4bebc5a7b2b2dac66bb621ce86e7e92e53f -19054,0x66632e226b16532df57aa3da9f7e24c2295eebb5 -19026,0x66f06d1e6035bee46f2bf862a290559eaa3d62ec -18863,0x676d626dcfc399fc1ed4e31ff45cb2d6d63de5d4 -18931,0x67d269eb0fad04195189ffb9bc14729dab1de8f8 -19010,0x686dae6e4be3410f3849c152be9b7a14b187f7ef -19055,0x687d47506e6ba7a0ddbc5e754ef9b4336dfd2fe3 -18870,0x696a2a08ccfb908bc5ab371a5622bd7c9eb5323f -18945,0x6a2e646c5caf92820c00f501e04fe1a0ec9f37bd -19031,0x6b504f0e501499a0ed86c03d5e60b717a7004c15 -18771,0x6ba3bc09f00de197ab99e7cde834dfd9a8e0b99c -19008,0x6c310b3aa528ab752c2fdc03e02b88a9f3e16acb -18877,0x6c846f1a78e100e45921769811b104597aab8203 -18736,0x6cd5af44b0009756080d10278c5a84ed8be4ddde -18959,0x6d6011c4de363e1b7c5f916178891c5ff49d036f -18967,0x6dc31c83e74658cbc7bfd85b1b19f009ffd7d3c9 -18760,0x6de34705d2ad2998749532bc13ebb469c68cea15 -19074,0x6eb768d1838931a57f6f2af2ae91baa90059f747 -18801,0x6f2ca9d14d287d076bdd71805fdef9cb9d974799 -18977,0x6f5ca69add7316f9e23dc532e64841640ae5689b -19072,0x6fa8a1caa29a2297502dd2dc0aa61055dc5ce674 -18955,0x7182e0357fdfbf0cacbd462b93309d067410c79f -19043,0x7235a7c6b640bdd94aecff4240d791928e0a2bad -18843,0x73b161f1bcf37048a5173619cda53aaa56a28be0 -19049,0x7457bd3625c1d0e51d5c6360fb7c6a1974462620 -18732,0x75149abbebb874013b8ed92d21bb36e4198fcc08 -19017,0x75db0a2b3e08f22a42d0c08a96fb0cedcd3bea1b -18827,0x7646abf0659c2811c1784d644926a37039aa5f2e -18940,0x78da58fb504b53f0a14b048812a35de7ab89a961 -19050,0x7c2d4bed1439400194264eb1fd43e32e3dbe1e46 -18840,0x7c7abddbcb6c731237f7546d3e4c5165531fb0c1 -19019,0x7c892185dc9db27cc24cc37ae823664941c6ba25 -19009,0x7d8c37e700e60c23cc75ceaaaa3d07d191624c9c -19053,0x7e0742222ac12e4f6deb1746f6cfc77beae25672 -18987,0x7e440fb247770b51175ab42bbdd5bcfd34a678f8 -18833,0x7ed03f599b69965692a1225da1b1320f60348450 -18701,0x7f3bd88c39a03edd6e4c421a7380b58245e2bfd7 -18757,0x7f58af3d557ffd0f9ce62e6bb8a1b3b3fc2a5098 -18886,0x8091cac0d57b4116212fa2ea31dadda44ba6086b -19061,0x80a7d526bd06c6c948b9a41a57ea0df1763b1a66 -18903,0x828e214af3f7a03c51b1954f0729680bf49f9d39 -18746,0x8337c76bbccd65d7dd566c50e01252f6122d9ec9 -19011,0x83de03c34c74fffb4b0216389b13f5e6dc1065f2 -18983,0x83e2f88507d0e99c5885255bedaf6ffaa74ec767 -18891,0x8438ff411922846cd67a78f93b61a98de530994b -18993,0x8512028339bb67aee47c06a298031d91bb7d15ba -18854,0x855b261c1d25b5751dfbc2fc4d19202741a0f695 -19029,0x85b24acbb02a190a00dfab62a6ad2a429eeb5b7c -18957,0x8607493e0b660e5553cbce1bf02d724222a8ba60 -18758,0x86bae98f21172ee1065ef5111640b568cfe61686 -19090,0x86cd138bc2ca336a63f37daac5a711655c5667e8 -18752,0x876de192037227accdc7ff59e07da864267afeeb -18995,0x8a65b166c198c4854e019db5dd2e0a68608ff19c -18765,0x8a92e520ba9dfba024de3bd7e0926bdcc4911fcc -18938,0x8b825f85fe8b0b2fb5e4e357cd7f21fe0e6ec97c -19052,0x8cbc46b072456a6b2ef249f3457bdd1bdb6e1fe8 -19028,0x8d111fa3258da81f1c17ae20604079a89bb8efab -19092,0x8d2fceb124a6e78eafaeca97c2f70247dfc8031b -19046,0x8dfdb360fb679ffe371a4a01234e4c70456b480a -18786,0x8e1ca02b7147aff9c964eb698c7c7d7cd62a188f -19093,0x9091eacac63439d4e0fab1c8c960e2ee20eb8ed8 -19015,0x91a43e7d0a76b05b99a3667c04ca45f3814e9f4d -18941,0x920e88a9d206df7ef4913092c05bf9b261aee7de -18798,0x9306f863062518c8e2493ed34ca94e86bd61d96b -18804,0x9357eb9c4627f7cddbdff4f648df5c2fb42cb69f -18950,0x93b359b9fe6b6549f1231e6ae5c9e9c6b758a24a -18883,0x93bc64b45774f75e1b6e79522d54d273ad54f193 -19012,0x95103ad4a4b08550c6dfe4ebf2131d5b648e51aa -18924,0x95a3c18f3a8db94ed57e0160a6e5a52e3329281f -18790,0x95fe3e7411f295a0da6ab14b4cd91f666b9f8b8a -18905,0x9607c514d139750218ff7b783b506c6e03ad0a30 -18819,0x97a16f10be8a74957f984deb66d7016848a67686 -18711,0x9847527860f8a3b3b8916781ccf71f453ab69136 -18988,0x98860fbd271ef720648b2e9e99f9bc4e5e3a55e6 -18740,0x988d525dc8739d68903013374556a39731c7f7a8 -18908,0x99eb5b946f371a764c9b4b5fdde0bda6acfff2f4 -19095,0x9a1097aef9bc18eb4fcc6f9e782f51acae7cf980 -19002,0x9acecc166b313dc5d9d0e05c03cbd5a62dc93198 -18986,0x9c2a59030600fa4ccb7ec88135017185de0f3d99 -19020,0x9d5134ecdbffb7a5aca55b78a4847d02744339b1 -19003,0x9ea9cff9377f5a0dab8607e2e0526ed7061f49b8 -19024,0xa0cc0106e9ad2f79d2775c778159da3044141797 -18855,0xa14542043a4d6e28cf7aff3624f40e6090bc5a32 -19001,0xa1fdc69a408774c61d2c2e5f8d25047393ddd28f -18892,0xa2d6ffa6c2c6aba2dde84efe3c52a446447b44ec -18797,0xa33c1963d74d203df6bffdfda3bff39a1d76e1d0 -18695,0xa3562cac1c39f4d4166ce31005fc080ab41120ac -18749,0xa4037372aad14526960f1a2062510d97df8a278f -19070,0xa4687ada981203566060c29f4c3cddc011184043 -18936,0xa48c5363698cef655d374675faf810137a1b2ec0 -18984,0xa49f2ea43b445f9a2467b7279cfa1f6a0c2e3f4f -18795,0xa5ce396616c7d14f61b5b9bba3a57388db885b2e -18949,0xa600f243cb35846f00c58bf52deadfeb56a8d1fd -19068,0xa60f8ce8eda61cefd33157275d8aa5e33a42afb9 -18878,0xa6f13d8dd52c1cacaac948bc741f67fc723bb323 -18881,0xa837be9471521838865596598ac82e2a2e0082ec -18969,0xa937196f8a2595fe8852ddf27b7e9b6a37ff6e01 -18916,0xa95c6d6a2765627a854960e9ee96f607b857385a -18990,0xaa2487fcbf6fe8057cc0882a95fc69d340b47b25 -18926,0xaa3b597c9d21f572422fd7206246ff115a5ba5c2 -18968,0xaa530d863785ca0a83ebf8540b3560a8940367e9 -18684,0xaa644fe9fce214d17aba12ff3dd3b885d2c35357 -18764,0xaaca5c96e6d14d9e0a00c8eaf2ed5e8e939ac611 -18867,0xaacd3577e672eb544b41e603827cdd28ffcf0d41 -18725,0xab8df5c84c6aeef86b241a91d86a11147b6240e8 -19081,0xabae973f646ccd48d55c495cfc511ff70f2cbd13 -19004,0xabf2da1f4082f013eaef107da71acddf07b8e2b4 -18869,0xac43ee1e50b3de4e3c0412bd685a46011f435d39 -18890,0xacacff03241256304e841e89c13319eae09f14b3 -19027,0xacf48875fe09d4f93a380e248b5de924d38eb1ad -18880,0xae5168c20653bada9ac3b956bac7a1152115f817 -19073,0xaedd173d11f29a9e93d722526ff01e3b8147d105 -18770,0xb016a673ad68b3456da1ba3c17f9b7f4d65f063f -18689,0xb02e538a08cfa00e9900cf94e33b161323d8d162 -18810,0xb0b06f0440ce13d03d41c5399c55aefea1b372ce -19069,0xb133a29c9dc193f49f8d971d4c6e4bd3b7301cd8 -18679,0xb2bbb093699d3734ec24386837723259501ef150 -18985,0xb2edf57e890c17fe0c8d1a8dd2dd8fdbd81b7993 -18716,0xb4c09a14290d90c5064e40877a7507719ec04217 -18841,0xb597351253bdb71716d49f5ad47c9a4ad518eccc -18852,0xb5adf58391efec58a1497332fbc5233f93a28d65 -18727,0xb5ee566f3738355a59bb5821ff7c7b5b956f0dab -18831,0xb623cb21b54a2f9a2393eee90054c2506bd4810b -18899,0xb63ed21cea59ec125b4536921096100d5b69f672 -19016,0xb6588c600cdcf4df0ec4fb0874841474d53956a0 -18687,0xb6a45238b043b60f0f209dd2facef623cc230ccb -18835,0xb6d2adb848d750336b85f4039eb62f939dfa3401 -19021,0xb790812999f6930099a9fb3fef6bf212fd0d1741 -19087,0xb8758862f3fbf216b4a532dd75307a1de63e7558 -19060,0xb8e90fd247700de65450aacd4a47b2948dc59fc1 -18879,0xb914b1bec4f236cf3c8960641dedc6f8bfd790c8 -18720,0xba4d778fdc9493ff1333c990141953d242e7d045 -18788,0xbb3e8eac35e649ed1071a9ec42223d474e67b19a -18933,0xbcb18e71fb285a44ade176e760719d4206656c71 -18807,0xbce213d2a902a3b3b9d194a57f899a7d292d2431 -19088,0xbcee4d54b2650871ace08a9fe5ce3a53497d12b6 -18875,0xbd5c2675acb26c74c984310ba5dda1e483eef5da -18930,0xbdcb83be97c4bd9384645aca9f2787b95ea89229 -18846,0xbdecdf6d6b089e1970d2206dfe6c413724b7638f -18817,0xbe7a6a51a75173c4fefa12936dccf0d488c4c5c4 -18753,0xbfa31380ed380ceb325153ea08f296a45a489108 -18961,0xc5d8ed0acc60b08fb8735a6b7f74bff51e926237 -19083,0xc5e29f8f8320be16c4b340f47af482f3ced49a02 -18873,0xc627a3ac0480a27eacbf9207a08c1e0fe42e984a -18762,0xc73146ee99b8fa166befc80bfd57a214353685d4 -19039,0xc7a7bd0f4e42c5c91427e998994d466d71a69cff -18723,0xc7f1a22c30ae981e6a74a0267ce6cbbf27d8ecd5 -18884,0xc80dc0022d375b35fcb3a2838ebaaaf87477d2b4 -18951,0xc8c944bcab920216966224ff35807e0689255b83 -18954,0xc9bc3955e08faf988a68989a3c8be051db35cd41 -18981,0xca4e85b8a59d37aea655b12fd442ddf905078561 -19038,0xca5b08450308f8bdaa24ca27410a16b974b89a52 -18994,0xcaf368613feb4c0627d1d63075c260029bf9d006 -18703,0xcce7819d65f348c64b7beb205ba367b3fe33763b -18803,0xcd25a28257e420bd8496fc18391200bee84a653e -19040,0xce93fc719d8921b8be655ccb620ea34c9b11f140 -18731,0xcfdff4e171133d55de2e45c66a0e144a135d93f2 -18691,0xd02421294de2314f03441ee3c27d2c034c9a0cf5 -18844,0xd03e0bdad93487b6b0829652c7ea0bfe188b303c -18919,0xd050b02cfd85770486fb3ee4b6efa433312fb058 -18921,0xd175732fdb7d16a12b3cf0d23ecb5fed4fa1cec3 -19065,0xd1dbdad1e827d6c8129eeff912ab5afef47980b5 -18887,0xd22952d5800dbbf77682ac6c04fd7c52979b1235 -18772,0xd2caaad2a055be091f514d240799ca155da75a24 -18942,0xd2e4e2f80d98631d4d80a4807bfbeaeaf26c8e0f -18871,0xd41b83ffcae9d146842684bc267ac42417ca710a -18911,0xd4d17d30e4895fbce768fe91c50642e22e9bbaa6 -18729,0xd5b7b90a6aaa150dcc27dda33319c9a0e7af01a8 -19085,0xd5e05d27d49aa60a0589fb52fea42d33939f4c64 -18896,0xd93144b625a2c2d735fb5c2ee1b455d58c609d6a -19096,0xda496d2752e9e08d219110647bb16a231d0f1599 -18767,0xdb8edfbbd086e2f41b12cca220e4c272b3bd39d0 -19013,0xdd0d125475453767e65f1a4dd30b62699fdcc9f5 -18872,0xdd827aebf9d972c09f6594d82d50676b7cafda7e -18972,0xdda9224f7ee7857b33bb0f29ac440819ab4f7327 -18963,0xde3081b143272f935f592a41e4d01478a520d4a7 -18779,0xde48b1b5853cc63b1d05e507414d3e02831722f8 -19084,0xde4a82fcebd265516e56c72ba8e550c27b7104c3 -18913,0xde7445e706a95ecde5e9aac607c0e6fd8c85d786 -18793,0xdec42fcc5a17f86c866e160e26e31b35471372cd -18688,0xe01a1e95282195e59d54f3f40c4f0bddf7a91c69 -18851,0xe0392ea1babcf15d95819a5bfc68ac191df39c5b -19056,0xe131c519916517169fe45feffa37d843aa40ea3d -18906,0xe1852dffc5b6b6dcebe2b2f6a8812b910b050675 -18713,0xe22c22e5ae3132b6c30fc051dc2bd7b3e0de102f -18956,0xe35f47aa14c583887ff8bf51620dca63c4a6eaec -18900,0xe3da8c71ff45e3140a2eec17bceb4a12fa401426 -18953,0xe4f148185d27ac3623b1888352c721fd1afef88e -18980,0xe4fa087aa24e5c557cb774632bb95cf72f992ed7 -19025,0xe5493f9152afe3acb1a76f88a61698e6ae5bd967 -19067,0xe84208d0037248a2537e7d7d22b575e0fa951dea -18673,0xe8be7cedf930305cb056981c2547322f82804f4f -18796,0xe97831964bf41c564edf6629f818ed36c85fd520 -18828,0xe98b60a899fcc58b18939727f57fa952d6bb4ede -18909,0xeaa6111c04adb88debbc5396479b8c8689002b9c -18849,0xeacb3f1a4f3137807f4e901167a999ef6f8312fb -18821,0xeaf788ad8abd9c98ba05f6802a62b8dbc673d76b -18914,0xece7297e44e1018b1af22f187ea3b38ac81dce09 -18739,0xed2784eae4bde283d5908d4383dc059b8b9e462e -19077,0xefe68c0aa1adb045ca40c2845d35b572232b33c9 -18901,0xf0c74d42d2c9b7ebfe58918ef426aca0d7359e18 -18973,0xf0d0f56f01a3634f8e27aa0db7fe8be668926dcf -18874,0xf10bb5296aa5bda5c190ce22f8acb4d4adaae1cd -18996,0xf12c7a02251a68b83518699e6ed4f404c0f2a127 -18806,0xf24ecf73fd8e7fc9d8f94cd9df4f03107704d309 -18728,0xf3075f4519cd0b8f97858d1edaa7b3f5f2337a1b -18730,0xf31932b99f5e21a55adbd23f90f09ea3e53a514b -19062,0xf3840a31bd84efbeac057d80eb1fa03dcda0374b -19042,0xf384fbad5cca4c158c2f5a99a9230350eb0028be -19005,0xf456f7dcf9abf1a88b4ef819a4aa879321a24ded -18761,0xf492cf8c61103752a31f182eba372241e0de6a1e -18742,0xf5a0442d4753ca1ea36427ec071aa5e786da5916 -19091,0xf5ae18e524b1500e7efc56fc9a9d8c49ccddd208 -18734,0xf67cfa9a192edbba8d3d7e2bdbc7a104e76997ba -18912,0xf762ea3789d4d2b5eeee218a22d5db028f366700 -18755,0xf8bf4c15054a3b9f90afae156f00090fe7010be0 -18737,0xf98a377c561fb8a76a098d71561d8d3549d29cb7 -18937,0xfa73cc9a6851678d57a833135fc39c94b4e0cf1d -19059,0xfa8c3eeae5e1296e0e559177d26d1fe9122ca058 -18857,0xfbdcdefd5cd7992ac6fee832227b3cefde46ed95 -18966,0xfc1d029a07409fd25077da9f926b85ae88af5393 -19089,0xfc45d094f62d440bb9d6b1ba0d7d7b00e7f55f51 -18778,0xfcdf7c184726578a8c50c424f8ad84f6cd90875b -18978,0xfed052cd2b398c135669d70bc73c64ba122a9c8b -19034,0xff50ea0b460ceb9453ccd672bf46fcf8aba344b2 -18970,0xff92bcb13495db02a300a95db8ef8bff36835eb6 -18943,0xffef365c3e005fa5c80b7fc1b40f2b3756b53319 -20012,0x1f32b1c2345538c0c6f582fcb022739c4a194ebb -20013,0x23b96add54c479c6784dd504670b5376b808f4c7 -20011,0x8e01013243a96601a86eb3153f0d9fa4fbfb6957 -20010,0x92834c37df982a13bb0f8c3f6608e26f0546538e -20178,0x007676fab2c97d2f3e7d1273753a1c47fe9723ee -20209,0x119f85ecfcfbc1d7033d266192626202df7dbdf2 -20181,0x13c214b430fe304c4c6437f3564a690cd4e4f23b -20196,0x1555ef41d21b50793f3c3d234ac54223f5b4ad39 -20201,0x199b779a00a5d97e1b625cc8d7d40cbd0a7c2bd9 -20212,0x1eeb6477ec18902dfab10dcb95e744bff674b2c1 -20173,0x2059f5d44827141482bf11c10c35a9e745e2dacc -20206,0x252ea7e68a27390ce0d53851192839a39ab8b38c -20204,0x28207a64bb9de68eb4cba3334f3888bcceeb59ff -20184,0x29b159ae784accfa7fb9c7ba1de272bad75f5674 -20174,0x29e8dd7383acc9ac316dfc9055c177fe748a0be5 -20193,0x2c0128153c95128de27af4db4ae60676e971ede9 -20188,0x2f23eac2714d323bc76d2f014232fcd6f54c0338 -20215,0x39126a983ed0b4a849a5f20bd47ff9582ced9475 -20189,0x3a05928f309b097dfad20545c80f33a6bc183e96 -20172,0x3bc414fa971189783acee4dee281067c322e3412 -20208,0x3c7e63ba04ff4d5f0673bc93bbd9e73e9dd37ed2 -20190,0x3e1acfc916492027e351c94ecf00fa62f722e4f9 -20207,0x4870cfb6c7ef36156e4f18a6a4bf8dab0ce6de8d -20187,0x4936c2adac6206984fc42f06d1899b408472e34c -20203,0x546ba811099883bef35fa360e7ded8af439831f3 -20210,0x5c1e8d96a1b2cb9b4bb5a57262dc175a4b10c9a5 -20200,0x5c39a4a368ab3c3239d20eb4219e0361bd2ad092 -20175,0x5d5457b543416c5cb770d3c4352d6a561b969977 -20170,0x682e44d0e5fad951c1c40c93cc61f481244ae2fb -20167,0x6b283cbcd24fdf67e1c4e23d28815c2607eefe29 -20169,0x754e6134872d7a501ffeba6c186e187dbfdf6f4a -20182,0x7d39583e262cbe75a1d698a6d79cd5a2958cb61d -20195,0x823c55654d6e860f40070ee5625ff8b091df4269 -20192,0x83f5fbfd1a0ed0e25c64a22c5a1d22eb4da30ead -20205,0x840e9b5dd7f7de125ea7da4a138f3a1746d7855c -20213,0x85e439c5a4c7c9a70a5a49a74a6b6f689622b2b4 -20180,0x89c4e9a23db43641e1b3c5e0691b100e64b50e32 -20176,0x8a641696caf0f59bb7a53cf8d2dc943ed95229a6 -20191,0x8c7c645d4e6810dece35b55e59b020af4fc90dff -20186,0x90356c24c1f95cf29543d45122f2554b6a74f201 -20168,0x96f2539d3684dbde8b3242a51a73b66360a5b541 -20211,0x9c54e82ef8752b8f573390a6c36893e7eaca234c -20183,0x9e945df4625c0dea3c41160c433691124a030a18 -20197,0xa7c657a94eb9571f4e94f49943af1130e6d7337c -20199,0xb14b8ff25f6b6991720ae3b0ff8e75d8ea7475fd -20202,0xbcfd8233ae314113d353c3ca8858f71658bdbc55 -20185,0xc39d50f443525551d8adef2f8b14d673c569e6e7 -20179,0xc8811262a356aa682a9d3139bb8572b7c5f7f887 -20194,0xd0bfc23ab4a58726ae2df3ac5cb51af2b562fd3c -20171,0xd1a988b024c55d7baabb07fd531d63a4e19e3b4c -20177,0xdd4d71d3563c24e38525661896e1d01fd8c2c9a5 -20198,0xe161c6c9f2fc74ac97300e6f00648284d83cbd19 -20214,0xfe1eb36d31ead771fd5e051ee8cc424db6416567 -20407,0x00e7306e591c04e72867644df141e250acaf175b -20404,0x03bd2e719b6ec02927eeffdfbffe90c2cb274ba2 -20413,0x0a9f824c05a74f577a536a8a0c673183a872dff4 -20408,0x154c7b3eee18abcfaede98fae00e58e7737d96de -20371,0x15feea944a7f4ee4835c59abc488c1935f2301b4 -20381,0x16cc4ef7c128d7fea96cf46ffd9dd20f76170347 -20386,0x187d4dca18652677428d6a9b1978945a0b978631 -20383,0x1eb362b4af5eb4477d6dd5966a6953d9cd57832e -20401,0x2297aebd383787a160dd0d9f71508148769342e3 -20399,0x23ec43e2b8f9ae21d895eea5a1a9c444fe301044 -20390,0x2c57904d328deda322a2d60c46f864793e87ec29 -20359,0x2d61dcdd36f10b22176e0433b86f74567d529aaa -20411,0x2ef002aa0ab6761b6aea8d639dcdaa20d79b768c -20414,0x2f4c6eea955e95e6d65e08620d980c0e0e92211f -20409,0x31dc7bd74c0172b83b2a142b2476b8c694d9787c -20392,0x3773e1e9deb273fcdf9f80bc88bb387b1e6ce34d -20361,0x38de71124f7a447a01d67945a51edce9ff491251 -20418,0x3a01cfb9831e7c9a30917bd78c2a5c0243ab4b4f -20376,0x3b01dee1733697b954a7648f7b0bf91acff1eab9 -20410,0x3b4df7233d2d9ae4062b996a9694d0dfc556181e -20355,0x3c2269811836af69497e5f486a85d7316753cf62 -20391,0x462f7ec57c6492b983a8c8322b4369a7f149b859 -20373,0x4740469750ce90fdb73f5fd0a062fff0b1e4be5d -20384,0x48c28a1f73d4ef73e62ee24c1af6644cc0d9ad17 -20393,0x4d73adb72bc3dd368966edd0f0b2148401a178e2 -20367,0x4d91d0bd09d634a7f5a99b5eeef3af6241e9fe07 -20398,0x4e341b9cf90514a5b7dfec2c9a1f20aa4514c260 -20356,0x4ee2f9b7cf3a68966c370f3eb2c16613d3235245 -20347,0x50500ccd11e29f14008c7778a6ced655727a21c3 -20369,0x5b23e2bae5c5f00e804ea2c4c9abe601604378fa -20382,0x5b905fe05f81f3a8ad8b28c6e17779cfabf76068 -20374,0x5c824c516ba5fa8db75738ef5bdac4efdca691f1 -20360,0x66a71dcef29a0ffbdbe3c6a460a3b5bc225cd675 -20372,0x6c73c7a416d96d9c6fa57671aa1ed7ed0fdf5127 -20420,0x6d65a44bd6cfe1a8b2e816c918dd83a6b04c8dee -20394,0x75dc8e5f50c8221a82ca6af64af811caa983b65f -20370,0x763bfce1ed335885d0eec1f182fe6e6b85babc92 -20421,0x8042425b53dc6a475cb4e62778a5e17f55f2188d -20416,0x81855cd82374c3700ee579ad1ff0760ae039a19b -20395,0x81e792e5a9003cc1c8bf5569a00f34b65d75b017 -20417,0x86355f02119bdbc28ed6a4d5e0ca327ca7730fff -20400,0x86bb63148d17d445ed5398ef26aa05bf76dd5b59 -20387,0x967baf657ec4d4b1cb00b06f7cc6e8ba604e3ac8 -20354,0x9740ff91f1985d8d2b71494ae1a2f723bb3ed9e4 -20362,0x980205d352f198748b626f6f7c38a8a5663ec981 -20397,0xa0cc33dd6f4819d473226257792afe230ec3c67f -20388,0xa6bf2be6c60175601bf88217c75dd4b14abb5fbb -20364,0xae78956593dec9add13b4b892625a62a415dff4f -20365,0xb328c2c62e83d3a179646b5c7284a99182735241 -20380,0xb360a579dc6f77d6a3e8710a9d983811129c428d -20366,0xb3cb073ade360e9faaaac8f8de935858da3ef62c -20357,0xb6319cc6c8c27a8f5daf0dd3df91ea35c4720dd7 -20375,0xb98d764d25d53f803f05d451225612e4a9a3b712 -20358,0xc1b15d3b262beec0e3565c11c9e0f6134bdacb36 -20402,0xc1b85974f7c2f0ccb01d763f4a34bfb41a33d612 -20378,0xcbd35a9b849342ad34a71e072d9947d4afb4e164 -20396,0xd52c8cbef4ec47d88c2004f90dbc1da1a6a61378 -20385,0xd7f9e780d1e6254afe4d57d9de8449756b6122d7 -20412,0xdd69db25f6d620a7bad3023c5d32761d353d3de9 -20403,0xdf50cd348b018d955bd08d7a3fe6121be14beaa9 -20377,0xe04f26f3f47b6054aeb15796ac4af3c61f08e832 -20406,0xe894620b411e4bad99f59cddf1d42414136c6815 -20368,0xedf930cd8095548f97b21ec4e2de5455a7382f04 -20405,0xef32f931ac53808e695b7ef3d1b6c5016024a68f -20419,0xf51caabe877c28655ce881f3b08482ab82255e49 -20379,0xf665989b02709006448dd7ebd20b6ed25f0828c5 -20415,0xfc358f532d474b3f47c98f2af5654a813db1a1cc -20363,0xfe7c30860d01e28371d40434806f4a8fcdd3a098 -20389,0xfff96b173d824a96e99bc66ea31446b60f672325 -20642,0x00555513acf282b42882420e5e5ba87b44d8fa6e -20648,0x049beffdb026d6d1d64cfc8fe29bd12142967b09 -20637,0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f -20618,0x10f6fb1a8b495a972bdce5bf86f2969f64359b00 -20632,0x165c68077ac06c83800d19200e6e2b08d02de75d -20638,0x180555d4d45e67520adc7c0c51b512c7a50877f2 -20607,0x1d5702c6d7eb30e42a8c94b8db7ea2e8444a37fd -20644,0x2abe8750e4a65584d7452316356128c936273e0d -20628,0x2b1c7b41f6a8f2b2bc45c3233a5d5fb3cd6dc9a8 -20635,0x2b938f00a8a32a1295983711f007ffae33d035cf -20629,0x35be3f4fd8239a35a7f120756d4d69e5c5e10870 -20621,0x41684b361557e9282e0373ca51260d9331e518c9 -20651,0x4d47fd5a29904dae0ef51b1c450c9750f15d7856 -20619,0x54d6709db7cda47e5461a7fbd2126f081c4b539e -20630,0x5649b4dd00780e99bab7abb4a3d581ea1aeb23d0 -20652,0x58f1d0f9bff9d695010c92fb93d100cef5113f3e -20631,0x59a16ece7143459801c3b3f24dc8a0cdfb956571 -20608,0x6131b5fae19ea4f9d964eac0408e4408b66337b5 -20623,0x617dee16b86534a5d792a4d7a62fb491b544111e -20645,0x7d5ba536ab244aaa1ea42ab88428847f25e3e676 -20626,0x8abd8c92f1901cf204590c16b5ef690a35b3741e -20655,0x8fd8cb948965d9305999d767a02bf79833eadbb3 -20653,0x98565fcad2080c5c19c3136fa367ce371cd40bd6 -20643,0xa9249f4d7e84b206d010bc90211a11fda57785b4 -20636,0xb85ebe2e4ea27526f817ff33fb55fb240057c03f -20627,0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83 -20609,0xcaa00aaf6fbc769d627d825b4faedc3aad880597 -20610,0xcde3eb49d53d932c605efbc1bc425c6affa5eac2 -20611,0xd1b47490209ccb7a806e8a45d9479490c040abf4 -20620,0xd7ab1699280980e756c446769b4c0f04550f88c9 -20649,0xd8ac7f696ae99cd7b689726cf03c5711dd8dcdb4 -20640,0xd9bfe9979e9ca4b2fe84ba5d4cf963bbcb376974 -20625,0xda474537ce9b687b78b236452a05631f09b6eb6a -20641,0xdb2802994104fad228b49a3c7534187b9794b4ce -20622,0xdc4382353a007fcefadf0609920c256173f7d210 -20654,0xe222fbe074a436145b255442d919e4e3a6c6a480 -20624,0xe3ac3fd66eb31caf4ee0831b262d837c479ffce5 -20634,0xe3c78d1d79cf1b326f376eb54f208cd2711685a2 -20650,0xf9c2b5746c946ef883ab2660bbbb1f10a5bdeab4 -20633,0xfab94e9ff03568718ebc91583612d7e4c9346a61 -20694,0x00df5b1995b4e90f0fe16cbb730d34b175001b06 -20701,0x0775e75dffac48ac3b5421b0687ea5825cb0999c -20707,0x09a6935a1b03cc05910d8096cfdcd86f50fe358e -20697,0x0b8245b6c201d4978abb26714d4d0fca5327c036 -20735,0x0c595a273b28390afd403831c1767037ccdd50ce -20711,0x0ca2ca9fff09f575a178f4bb3c0e7744a1069982 -20733,0x1066a8eb3d90af0ad3f89839b974658577e75be2 -20731,0x11193470df30b37af9fc5ec696c240d878bdfb42 -20753,0x12ead486199115d1470d43db5d16e606ab3c1043 -20704,0x1964cf9d0c5c268dcf5a5d37f13600483237f6f5 -20748,0x1df21abffb3261d0fdbf5b2f56f23da17c3c7167 -20696,0x2403c08680bf0cf48063d2bc65695be6ee448751 -20716,0x2787cc20e5ecb4bf1bfb79eae284201027683179 -20738,0x3bc8d34314e77c2e36948fd8f4b353f1badc3f6c -20724,0x3e8b82326ff5f2f10da8cea117bd44343ccb9c26 -20718,0x49b7e13922c46f403b18cfdcdc729dd5de614ea0 -20749,0x5680722ec2240619c09e80a651a9735156010c0e -20706,0x59a719c66631c343ab0f58471cc582bdad26dab3 -20736,0x5aa7d1f42d2bdd7ca7f7fe3a724327573658f722 -20709,0x6077987e8e06c062094c33177eb12c4a65f90b65 -20703,0x640cf0c438d5c7c39479935856329f6bc5d2d52a -20727,0x6789d8a7a7871923fc6430432a602879ecb6520a -20708,0x678d8f4ba8dfe6bad51796351824dcceceaeff2b -20725,0x6895c1c37e466dcd5c9c17b3c30dbe0b261e612a -20715,0x6ba8eb350c72a665b841da90f03401539e7d765f -20739,0x6d6273f52b0c8eab388141393c1e8cfdb3311de6 -20726,0x6e56a5d49f775ba08041e28030bc7826b13489e0 -20729,0x6fd879830d9b1ee5d4f9ef12f8d5dee916bebd0b -20745,0x71f8e9fd4b8dd9138e8fc5fb59269b144d457204 -20702,0x7e2a9aecdb007e060ba8b98f7ed5706c9c02b4f2 -20719,0x8132ee584bcd6f8eb1bea141db7a7ac1e72917b9 -20751,0x83e13069aa457778ca349e0128927b417a2c2b3f -20732,0x84a92eac38614118d74b2977dee1689b2ee2b569 -20752,0x865da103d126b3be3599d84cab57109a861f5631 -20737,0x867f370d78efd1072907ec96aad977e3c825cf0b -20747,0x878b75007c76312a263a3dbe8b07a60aa8ebff54 -20720,0x87ebf432c7075183e5d3684c73249128d5747cd9 -20722,0x8954c7b1417e3de398c7f33520ebae142929ba2a -20734,0x920cf626a271321c151d027030d5d08af699456b -20728,0xabd4171d1be4ec3970992bc0e697342ea0bd913d -20723,0xaff0ab3913203c7bc165799f80199598a167db83 -20750,0xb753d2ee5dca1ff39a83ca3ec500656c31be940b -20712,0xb8c8a5431d3bd730841bc928a70115660257bf1f -20721,0xbc23461052632d98a13952aacf2835534f6d7792 -20746,0xbc98cf958ea2625384a05f473f1a65e5e5ad21ef -20700,0xc6a9c939eeb37a49161655f6cc153465de62181a -20730,0xc7088ac8f287539567e458c7d08c2a1470fd25b7 -20717,0xcf462b1a631d5adc069eef84936593e278cb8077 -20695,0xd02813baf080d06fc6f706cf93f5daa96d6edb17 -20705,0xd301bcc5d3229a0a6400f1030c382a5d503931e1 -20699,0xd515f58685ebe8b9da3b029bcf2f2f4d31bedac5 -20742,0xd5fe5beaa04270b32f81bf161768c44df9880d11 -20744,0xd6ab436f8e2188b27e60248e3178f34a40b4a63a -20713,0xdadf709d49180f6650b7eab62d81301b3e6f22d5 -20714,0xdb1bdfbb9704d66b339dd1c45dee742b3bfeabde -20743,0xe5bb889b1f0b6b4b7384bd19cbb37adbdda941a6 -20710,0xf486a72e8c8143acd9f65a104a16990fdb38be14 -20698,0xfcb3f8fce594bd30b2cddc9efddf7c6cf1da8d56 -20793,0x10bff12281138846b6c23e6ef7db26367bb5b99c -20778,0x20b5683170412daba16e64315a62d766f8d6acc6 -20785,0x37bb03a0c5464da8886d40f733e1d383d75ceccf -20773,0x3aa2a4f9bc75e34662f7f2877a13a4dda88e00e9 -20784,0x3b83c0bdb49802fac45ebb28a1b0fa797b16328c -20780,0x5c7e78f7fa130b284b1c208c5114553ec276a8fb -20777,0x6e2efa455298116ede2e47447e609b133262cedc -20791,0x7314af7d05e054e96c44d7923e68d66475ffaab8 -20779,0x813bb65cffb67f8328f1447b196db33bbc6482a8 -20792,0x87ab1c824dff612686b45b43504bbea63b7b4d2e -20790,0x90ae03f8a0f9942ba02ab6c45cf7e0b905a2dad4 -20776,0x9bd46c3a496ab1b18bd34eee4be5619048aa0f79 -20788,0xa16192ff74ae344e2918d230b0af736f0d4491fb -20782,0xb01614bd478e8776e1c8528d0469c453b171c719 -20789,0xb0301bae14f8bd66b81a776af6d001414a1c3fb3 -20783,0xba291167a78612bac28f016853fd75c0aa31a4b3 -20786,0xd0b67611ef0acae64769d761559de790672a1ebb -20774,0xd1781f9ee563782c7454b9da699914d1ede3921e -20775,0xd25489b568a6c67052a3f119f3700897e2937fc8 -20787,0xe3618d839a1030964e3ff0ea6a641141332bef6e -20781,0xe7c74c7cc30fc5eea17d4c590c11904c5c4661e6 -21002,0x03bc653285f8527e1c877b18df285e66898864b3 -21010,0x2a49451637f0c8dd063a76fb868d600b2c144361 -21018,0x2d0e006bb94409a4edfc3348a6703801e770bdb1 -21007,0x35274e47010354173686958162369c61fb677e69 -21019,0x38aed1b3d54843538b2489fca7bdc5fd838d879a -21017,0x586cce2d7ce78e9c9fd5c062ec6ee59880eac78f -21014,0x811f78b7d6bcf1c0e94493c2ec727b50ee32b974 -21011,0x818dcb8b72f83f3824df9e7d83b38e5f7a96515e -21009,0x89b3a08d6f73bb44b1ff182bd7bf0590dbfdb21a -21008,0x930a54d8af945f6d1bed5aaf63b63fab50a8197f -21003,0x94b58178d4f4fd2ca7c00dc226d091a0f9afbca7 -21022,0x98fab5d1366de24f152ea683a1f23338351c47ea -21020,0xa3061f06274dbe75006b47f84f6ac24213a69df6 -21013,0xa513a13db767d4609ece1c705dbeefb0f5822224 -21015,0xa6cdc0e62f1e001efcd033515e7cd78a30353b55 -21004,0xac84c7795157b9410d184d50d1a672607de2894a -21005,0xaeed612285f9e519f76f1d5cf1dfd84d3a398ba7 -21016,0xaf9132b3afa36b36f6ddfff63fd40b9df43c0928 -21025,0xb56ddb0edfe1265bd8d7f25e45b759ab82bb06da -21023,0xcbfcbcb93c133478da38a77e7a2fc01479ac79a2 -21024,0xd43509074b41824bc51c50b71d7694ab8d2568ab -21021,0xd8d32702c398528904e1367999f1563eb4df731a -21012,0xf82feba4fd33f88cd5f2455b5e4d0de2611647ee -21142,0x00a35fd824c717879bf370e70ac6868b95870dfb -21132,0x033b849d5db77f98840d422a119887e120493f94 -21112,0x049e04bee77cffb055f733a138a2f204d3750283 -21135,0x04f0fd3cd03b17a3e5921c0170ca6dd3952841ca -21130,0x06cd0fa9e4fc0cc3476e79bd94743c7a29a2ab4e -21114,0x0b186669138879f50ead3ebf703f00f7f1a45b58 -21141,0x17533a1bde957979e3977ebbfbc31e6deeb25c7d -21111,0x17c6768f438f1c67a70889dcffe49c665ccfe769 -21138,0x19fe4a2ada33bd690c66a99df3de6c0899b6bd8f -21147,0x1d073cf59ae0c169cbc58b6fdd518822ae89173a -21146,0x20a7f63125d24a7b7aaa1f214df452d8a1aca786 -21149,0x23be15c4972279fe56dac4460be0b60cc8301bf7 -21110,0x2424c30e589caea191c06f41d1f5b90348dbed7d -21152,0x2ac63723a576f89b628d514ff671300801dc1702 -21108,0x3596b7e6b0c31d02b7cdd687d303a9c4dfc00b8c -21107,0x4645e0952678e9566fb529d9313f5730e4e1c412 -21150,0x4eda3e4f9e42a7dd5c853e8b857271fae88e7209 -21154,0x5402508a800db6b72792b80623193e38839a9e24 -21155,0x57baf809703769be301da8ff15f3713b941fffa9 -21129,0x5848ca62295c20049c3d6a19c6eccb2ac2779d6a -21125,0x61e38fa2a349b5d4ead78458afbcc1e4adeefab5 -21123,0x6e6eb84716c032f2fe9ef9bf1965cb0e76989d22 -21158,0x6fb612d5b817183845c6a5bb2d2afb6e7cba4ee9 -21121,0x707648dfbf9df6b0898f78edf191b85e327e0e05 -21137,0x874c01c2d1767efa01fa54b2ac16be96fad5a742 -21148,0x8bd68d02251b8466e802436e96734082c664eeef -21153,0x8c5d04d22ca563aa9fb783c263ae9bea20d3a332 -21126,0x8cfbe29030260b8931ae499edf5ce3f3cb02226f -21134,0x8f3ab2be32da1f5b1db934946fdc5f5415140f8b -21143,0x8f3ae32ddd5e20c814d12acdc15eaf2e225b77d9 -21116,0x970d6b8c1479ec2bfe5a82dc69cafe4003099bc0 -21118,0x9c8264cb356377bb1d263f6e0ee03e977b3cadab -21120,0xb795d08676e70f09e3217eb25d742d4ee6e987e0 -21124,0xc0afabc1cfb1a07df27176a388f3b78aaef07297 -21157,0xc0e1a46cfea33e598cf0102ef4e06421ed16fbbe -21109,0xc640a79205706062bf0277a466821a6130a8f78e -21122,0xc91ee78162424c5d1279dae6f04babea4794b34f -21117,0xcac4e73f528fcf354645122d62fd2a70c23b6b90 -21127,0xcbc71843e4dec985469191dd8bea2c04eb83b300 -21113,0xcdb9b4db65c913ab000b40204248c8a53185d14d -21156,0xd0b628cb062bcb34331482391c2110cd7a731e5a -21140,0xd4f65611e6709b995930f3de8095d6514111cd52 -21151,0xd5734c42e2e593933231be61bac2b94acdc44dc4 -21131,0xe0b57feed45e7d908f2d0dacd26f113cf26715bf -21119,0xe724ffa5d30782499086682c8362cb3673bf69ae -21139,0xe867bee9e05866e2d7a01d376dc6ea07706cb703 -21144,0xedaaba6db46bb1a7967e7490a024f07abe0e8941 -21133,0xeeb3981d5587c185bd809d74b3ddb6c3b0363099 -21145,0xf0c48ab947d650b5807322f3de61e14fc81771a2 -21128,0xf7564e21a2439cca399779580db97e85a766d2a8 -21115,0xf917832969f60c4019fee163d7fd7688eb677ee5 -21136,0xf9d60249c9590ef60b6a7d3f95fd0f98cb512588 -21227,0xc2fbb8cbfd3bd833be6830d4fd3c9034393e08f8 -21506,0x11104f7c9e50dc07c62904f3d281fc16b123feb8 -21513,0x181c4bb6413534b09b7da80a098d2dceb2b55fe8 -21540,0x1f5a482cb7de1ec22d3a7b5a17afb51b09a768c9 -21544,0x1fa0fd3654a7b6fa917f4eb4a5c624467a2d16b3 -21521,0x227babe533fa9a1085f5261210e0b7137e44437b -21515,0x2338457f771cf7ca7889aad848eeba18807cb206 -21507,0x24fb3dfba019e73ed45c884a31b5a60608968529 -21514,0x27fc4059860f3d9758dcc9a871838f06333fc6ed -21512,0x2db08783f13c4225a1963b2437f0d459a5bcb4d8 -21511,0x369ffdc7132ff7044acf876825baa4c7a0b1b28e -21523,0x384eab89048520e35309e48ea933d845c495a613 -21534,0x399ba3957d0e5f6e62836506e760787fddfb01c3 -21531,0x39eb30bb50b7929169e821271e9af594753b4692 -21518,0x426872fa15cf74c8d3e64a19dd161bd871ea9c80 -21529,0x45b265c7919d7fd8a0d673d7acaa8f5a7abb430d -21516,0x4776ca31368f11dbaabd25a7de012f244a6c1409 -21541,0x48a7b0667bdf8c03d32c7d497b4b6c3c42cd375a -21509,0x48fa7528bfd6164ddf09df0ed22451cf59c84130 -21542,0x536c9ade11d74c8f7271d26cf6cf95a9cb5bcc63 -21510,0x54fe0d5da2c787a93f2dcb4d25e202c4e44e4458 -21501,0x5a6752f16b81a8dfed84c785b49693573e5baf1a -21539,0x5a83002e6d8df75c79ade9c209f21c31b0ab14b2 -21519,0x696607447225f6690883e718fd0db0abaf36b6e2 -21537,0x7c53378987f6e82050b1244b4d836f785147544b -21522,0x86d50c37b7c5f92ac9ddcc034280907ebae1e86d -21502,0x889be273be5f75a177f9a1d00d84d607d75fb4e1 -21543,0x8b73aea22a6a1cd51c0b950a07e5c793872532f3 -21527,0x8c8ae22fea16c43743c846902ec7e34204894189 -21503,0x9816d7c448f79cdd4af18c4ae1726a14299e8c75 -21536,0x99bd1f28a5a7fecbe39a53463a916794be798fc3 -21504,0xa4a4343e8b7b2de019c0c101b4c70158454e8ade -21524,0xb549c8cc8011ca0d023a73dad54d725125b25f31 -21505,0xb765006321c6be998f0ef62802d2548e76870d3b -21533,0xb9318f3ecea2fc328b9a5165bf19204930236baf -21535,0xbdcf0bb40eb8642f907133bdb5fcc681d81f0651 -21517,0xc3a17dc6b70cd58f8ae49fb969cca5a57cf84a73 -21526,0xcb88cf29121e5380c818a7dd4e8c21d964369df3 -21508,0xd8284305b520ff5486ab718dbdfe46f18454aede -21520,0xddb3bcfe0304c970e263bf1366db8ed4de0e357a -21538,0xe1442ba08e330967dab4fd4fc173835e9730bff6 -21530,0xe565e118e75304dd3cf83dff409c90034b7ea18a -21532,0xf352dc165783538a26e38a536e76dcef227d90f2 -21525,0xf5e0e2827f60580304522e2c38177dfec7a428a4 -21528,0xfd76d7ecbf91b2bf7f225af29c1cb7f213fa71b6 -21567,0x0074976043140a371aead31189c2be459950c816 -21568,0x190da1b9fa124bd872e9166ba3c7dd656a11e8f8 -21571,0x4763f9720603342aad40056db65883bd55355945 -21564,0x4810fa5942a322c7bd30519ed3ebe732066c3db3 -21572,0x71bb67179dc6378cca27f5e4140af4e0a70af6f2 -21570,0x734afd33dfb5100ee91efe690526dffdedbe0cf4 -21569,0xca1fee73b00c221966e5f25226402146bdffe259 -21563,0xd0df5a352d74a746754c592a6277c9060a7c9c87 -21573,0xdb8502a16efc7074ed1dd1e087d6616851c3374e -21566,0xdb9b9797319e9458d4d3b6eaa86c4f15eddb989c -21565,0xf29571145b421f660775fa3deb16d9ff6085d0e6 -21659,0x01fef4d2b513c9f69e34b2f93ef707fa9ff60109 -21668,0x03f821d24dfd8ecbeb4bf31b9374b8087e9a406c -21633,0x09cadcbe6fe623f01b7bc37635b97abf385d46dd -21679,0x0a0a82d2f86b9e46ae60e22fce4e8b916f858ddc -21671,0x0bc299d77e7b2a4ba0b29b3463f0d80e4e5d0231 -21701,0x127d8cd0e2b2e0366d522dea53a787bfe9002c14 -21669,0x12d3e615c755b6a8b15330263b2137eed97f80d9 -21653,0x17ad93bfd041fc9784c4fd203330bc4b0320c272 -21684,0x17f8ab5dcfd4d635efdacba484c4db215ea82806 -21647,0x1bd311bd5ad4dcfe18bcb55d42f0a3c714e2bfde -21690,0x2249afe319baa0137cb434116ac4d4fd5c3eb719 -21655,0x22ed23cc6eff065afdb7d5ff0cbf6886fd19aee1 -21664,0x28846f4051eb05594b3ff9de76b7b5bf00431155 -21680,0x2a84370fe9ddfed08b076273cd32cd39a49c6b01 -21666,0x2a89237b746154fcda7ac90976da95b44680a554 -21654,0x313fe505ad3ead0d35dd5d6687fb9c6b2469db3d -21632,0x3254ce8f5b1c82431b8f21df01918342215825c2 -21678,0x3344ae78b00d0d92454b0efae5285703a34ca321 -21638,0x33d8f735dd64cec51d212616bca5ad9b7769cd34 -21675,0x34c1de737e201f9581f37564b492b492288eb7af -21645,0x3827426f69ecf2bab6f4227dd2c3ec3a2bb6fec7 -21700,0x39d3d5e7c11d61e072511485878dd84711c19d4a -21677,0x3bca6531edb2b41319fc56ba3c142367f83b65ee -21662,0x3cc08cd599fcff2e7f6ccc0ebd5aa57bddf11257 -21686,0x44ac5ed26d8b7bbcf3bcb9ac486191cbb294b140 -21661,0x4f99f135776bd4ae336f515caf01f2449eb3099b -21660,0x5461da36c57a2056d04cee085e9fa73d04c141f8 -21650,0x5c31e2d1c611d062befdf3883ade05a32c52b9cb -21670,0x667782d4be0e47522a68cacbe0cae8131da3a53e -21699,0x670b8378d53aed04cdeba632c2276f4414d693b2 -21657,0x6793c4390dd2f95a74e20329e93957243ae70232 -21658,0x686497f9e8bf13f6e89382c5249c87a6df49976d -21682,0x6c7256cf7c003dd85683339f75dde9971f98f2fd -21692,0x6d18725e0812b6fee55ff4215b154b11289d4b77 -21656,0x6e925abef722c186c1a93d9bd6c492430a4f7cc9 -21673,0x7141e629316162e12c31dad29706e5e7cd84b721 -21689,0x7552c5c0735a7f3d24819a951d15f7cfa1cda039 -21691,0x7b00d124fc98107ff97fac5d37493e1784bb5c8e -21697,0x7cdbd859f2eda545289378112fd991571d6eb73e -21639,0x810d6b2425dc5523525d1f45cc548ae9a085f5ea -21636,0x839c2d3ade63df5b0b8f3e57d5e145057ab41556 -21643,0x84e6b05a089d5677a702cf61dc14335b4be5b282 -21672,0x878c410028e3830f1fe03c428ff95012111ae1f1 -21637,0x89566cd6302ec9730970bb4e9374d9329b756e2c -21652,0x9341dd1191837c3ab30447ada907f7625a61cf14 -21687,0x97ff8dda82a635e10911abd7dc7589814d94f0e2 -21676,0x9926955e0dd681dc303370c52f4ad0a4dd061687 -21702,0xa4361ff34949bef0605a10ad7b50fe1e22c92e3b -21683,0xa4bf319968986d2352fa1c550d781bbfcce3fcab -21667,0xa5044f8ffa8fbddd0781cede502f1c493bb6978a -21644,0xa720c0b9a659817ecc0807b74d8e7cf597268740 -21685,0xa7c805e4ad4e7b51d2a1eb442b2014a9b63d3703 -21688,0xa9b99766e6c676cf1975c0d3166f96c0848ff5ad -21641,0xae37a6b396b7a56cfbb9ad56d7207d949dda6544 -21631,0xae7cd06693191c1f5f1477b9e9a567732d715f21 -21696,0xb168b14bd0ee6199247880f645764260488ce2be -21674,0xba7d437711d575c24eb7d04cf5cdb9b3908a5181 -21649,0xbb590b0fc4080e434adac39a51dc9ee3335dbf09 -21646,0xbb9ffad3a7866c3a966a82edd2bde0d8d931e1ed -21663,0xcf8a882763c92857feebd54620e2c09d8e12e60b -21698,0xd0889b53e270cf0b5d057976f04098ada184483c -21642,0xd226c68579d299a2e6d93a3ddaa5cf613c27912a -21648,0xd393e6cf57afbc950475caa187d6583ec8279554 -21640,0xd875a38dd91a9cc1129b93920d88e68b77f5b9e6 -21694,0xdaa3f68f0033d8ad252e0a53b402943221705714 -21681,0xdff1091149be811adb2e5fe0fb48dba8dce5a755 -21693,0xe11d191af6c834ce5b11a0ea52cbc16d0fd52f1e -21634,0xe3d068954336fef7a6a21056431df2e1044f1a5c -21651,0xe6aa2d277aafbb9e19354f6f893737c3608ff995 -21665,0xeab396d90b53b733349ee7a0c1afd62f23d889a8 -21695,0xee7afecd937a7a133e421719d790563f871410e7 -21635,0xf352901811ddd6ce803a626d6a8e4a221880f6be -21832,0x54b743d6055e3bbbf13eb2c748a3783516156e5b -21997,0x396d5f1ef3aa92ddad4dead04388374a03bc5577 -21999,0x822f17a9a5eecfd66dbaff7946a8071c265d1d07 -21998,0xc6fbcfe16d5ebfed21ace224c49c94de49046a04 -22080,0x0145be461a112c60c12c34d5bc538d10670e99ab -22093,0x0728e3d57115604ce604e35c2fa14215aeca881c -22025,0x090a00a2de0ea83def700b5e216f87a5d4f394fe -22024,0x0f390559f258eb8591c8e31cf0905e97cf36ace2 -22020,0x10010069de6bd5408a6ded075cf6ae2498073c73 -22031,0x10010078a54396f62c96df8532dc2b4847d47ed3 -22049,0x103f2ca2148b863942397dbc50a425cc4f4e9a27 -22079,0x10e08556d6fdd62a9ce5b3a5b07b0d8b0d093164 -22074,0x14cb5e017a3f10b9f6254ff24b87e2297dc8b8b3 -22055,0x161ef5a92a3ec3e7c475f32dd7672474e63a709d -22035,0x1747d329cb37e0a0f387f24065addbc60eab69dd -22068,0x198618d2aa6cbc89ea24550fe896d4afa28cd635 -22077,0x1a61a72f5cf5e857f15ee502210b81f8b3a66263 -22059,0x1cf3993eba538e5f085333c86356622161dd8c0b -22045,0x1db11cf7c332e797ac912e11b8762e0a4b24a836 -22034,0x1f8e8472e124f58b7f0d2598eae3f4f482780b09 -22069,0x21c8b25ac1adf0b3163c868fb94e1f44586bc4b5 -22030,0x243e33aa7f6787154a8e59d3c27a66db3f8818ee -22090,0x274e94f03ac51779d14bd45af77c0e0e9d97cef9 -22060,0x27a1b793b5b51a8862f66b0a1181ef42b2a8d9c2 -22095,0x2808a9f0a3af7fcf5dc2ef389f28043560ca07fa -22019,0x28707252fdea41b72cf321d153a6c01fa9f6fb79 -22098,0x330c43d22bd65a60ffea93a197f04e87faa27b70 -22078,0x35594e4992dfefcb0c20ec487d7af22a30bdec60 -22081,0x371cb7683ba0639a21f31e0b20f705e45bc18896 -22043,0x3a4148dddd121fbced8717cb7b82370be27f76bf -22058,0x3a7f310ee75b8ce3e46410ac438419842b541d10 -22039,0x3ffd03ef31f6d5a6c517cefa9cdf43efebee8399 -22062,0x42b458056f887fd665ed6f160a59afe932e1f559 -22100,0x4a2694fdf12ee1bf3d5c646ba66ea55ed8727850 -22041,0x4adf575dbe0e6f1c5909ae9c7119927b4faabbbd -22089,0x55bba7755b77420d3d3c966440164f15a74f8696 -22052,0x5734bb74cfac69f1c34ba66ea6608ccdee6b81f2 -22087,0x5a5755e1916f547d04ef43176d4cbe0de4503d5d -22048,0x607312a5c671d0c511998171e634de32156e69d0 -22088,0x6333000289bc27a090b3d51e734c2f13e3edc4fd -22053,0x65116121a48bec43f855e278dc3e2157f1132ed8 -22046,0x6bfd171ddef7ef775e6c1d6078c10198229dd242 -22065,0x6c63287cc629417e96b77dd7184748bb6536a4e2 -22076,0x7100cbca885905f922a19006cf7fd5d0e1bbb26c -22063,0x73280c390da5c6fe05ad2d1e6837e8e8c05e4b32 -22082,0x74b8932801bfbf63b44b001d77e62c808b1e2d12 -22084,0x76e47710aee13581ba5b19323325ca31c48d4cc3 -22061,0x772918d032cfd4ff09ea7af623e56e2d8d96bb65 -22070,0x816e21c33fa5f8440ebcdf6e01d39314541bea72 -22086,0x8279b109d3a8a61fcdb2532f08e14e763a064be1 -22066,0x89db3b59381bc06fe9bf74532afd777e5f78ef02 -22018,0x8a0d639f272f4b966b2dea42d4b743dce7e82c28 -22022,0x8c6139ff1e9d7c1e32bdafd79948d0895ba0a831 -22027,0x8e15a22853a0a60a0fbb0d875055a8e66cff0235 -22072,0x8f8bbdbae2814aa1c41a4000e213eb89c7ddefb5 -22073,0x929c697c557d2ed4c8c446e60614cfcce0b1d680 -22101,0x958ed8d4be3b6eda845c7e40c1147591a753ef64 -22056,0x96a0eea3a9cff74764b73a891c3b36a4f6b81181 -22096,0x988174f4ab5ad41e1313f1b07877dfe4a78ce5f2 -22067,0x9a9c7c065efcd4a8ffbf3d97882bbcaed4eb2910 -22023,0x9d56800b8ae23b79fe9d4822aa3245fa527caf3f -22050,0xa8236eafbaf1c3d39396de566ceea6f320e3db00 -22033,0xa8cd5d59827514bcf343ec19f531ce1788ea48f8 -22092,0xa9f89108bb45c2805eea20021fe2205ce662fd02 -22038,0xac8204a9d79ca87d192ea98a9381600642a66a5f -22054,0xb11c769e66f1ecea06b5c30154b880200bf57c25 -22064,0xb426c1b7fabea9ea6a273e8427040568a8c7df13 -22042,0xb4bafc3d60662de362c0cb0f5e2de76603ea77d7 -22071,0xb608c7aba0296124bbe9e4ace6c7339d618625f2 -22083,0xb994b84bd13f7c8dd3af5bee9dfac68436dcf5bd -22040,0xba57440fa35fdb671e58f6f56c1a4447ab1f6c2b -22028,0xbb93c7f378b9b531216f9ad7b5748be189a55807 -22091,0xbe7ca18470b4ab61741bc2dcad50b1d4052b6b04 -22044,0xc3cc9369fcb8491dad4fa64ce1fbd3dd2d70034f -22057,0xc457d2dd3209b7186934426acd8391d504dc3978 -22094,0xc9f08308fe6724bd7f0e87e2661de2fdfcc9e8a8 -22075,0xcabc8fc306fcaa4c05b58522b13756ae12edd902 -22097,0xd46db083de31c64af3f680f139a31ff37bac004f -22051,0xd7f3bf2085ad32ff95e1bcc408d37f10f6949270 -22085,0xd97a2591930e2da927b1903baa6763618bd7425b -22026,0xe4e43864ea18d5e5211352a4b810383460ab7fcc -22021,0xe8f12f5492ec28609d2932519456b7436d6c93bd -22037,0xec378cdd60e890332f7a8cc251315327a4f244b6 -22032,0xedba32185baf7fef9a26ca567bc4a6cbe426e499 -22099,0xf14f3bd905049773f4cfabc1cd233810ae0a7edb -22047,0xf191d17dee9943f06bb784c0492805280aee0bf9 -22029,0xfcd8570ad81e6c77b8d252bebeba62ed980bd64d -22036,0xff39252ce6a8fc5657235eefeb45702b86c42e8f -22290,0x03b53753ccaa50c9d95bb88aeae1efe67942feeb -22187,0x03d7f750777ec48d39d080b020d83eb2cb4e3547 -22280,0x09992dd7b32f7b35d347de9bdaf1919a57d38e82 -22151,0x0e0e3d2c5c292161999474247956ef542cabf8dd -22203,0x13b7f51bd865410c3acc4d56083c5b56ab38d203 -22243,0x16284c7323c35f4960540583998c98b1cfc581a7 -22188,0x19b2162ca4c2c6f08c6942bfb846ce5c396acb75 -22168,0x1f4075ebb27b8fcbb5bff2bfa5434db29bada300 -22245,0x25a5a48c35e75bd2eff53d94f0bb60d5a00e36ea -22248,0x25fb92e505f752f730cad0bd4fa17ece4a384266 -22226,0x266e2dc3c4c59e42aa07afee5b09e964cffe6778 -22247,0x28529fec439cff6d7d1d5917e956dee62cd3be5c -22213,0x2935008ee9943f859c4fbb863c5402ffc06f462e -22178,0x29fba7d2a6c95db162ee09c6250e912d6893dca6 -22256,0x2b566c1de5d63662ee405adc868979bb4af0b9c0 -22241,0x2e9bb44c9cb774939462ef874fc854d8df524f13 -22161,0x33fe5bb8da466da55a8a32d6ade2bb104e2c5201 -22267,0x392b9780cfd362bd6951edfa9ebc31e68748b190 -22183,0x3c0d0c77c75ce5dc3f6cb5ece08b88e5575a670d -22152,0x3c0ffaca566fccfd9cc95139fef6cba143795963 -22224,0x4014dc015641c08788f15bd6eb20da4c47d936d8 -22227,0x40580cf5afd37a8eab5bd23c2fa3c2392fc4689d -22281,0x4285caab762f2e405cd256069d0d47d13bf05b00 -22279,0x45269f59aa76bb491d0fc4c26f468d8e1ee26b73 -22271,0x452bb48167460b0c2abbdd793db60c50a6cae246 -22205,0x4f836ab2393efed4f88bc914389b2c1b215981aa -22258,0x537db35e019454e0f1f645a933d3c0e7ee68322d -22235,0x6b8b75c66d808ab236d8a7996636521a12e374d8 -22165,0x6f03052743cd99ce1b29265e377e320cd24eb632 -22262,0x755569159598f3702bdd7dff6233a317c156d3dd -22181,0x76ab3512f3bfa0f5a9e10e584b44ca2da9fe248d -22160,0x77b99c3c99fc6947e2086dbb95af93602237db51 -22284,0x7ccdbf143668a2ec26bafb01cbeb6b9ef3b0839b -22294,0x8d2481802a94dd106efa2f6d813490da8e6eea46 -22240,0x95d6a95becfd98a7032ed0c7d950ff6e0fa8d697 -22236,0x99f68f7819e0ee731feb1486c7652e3740ae63dc -22162,0x9e037b11d139289e30a1e432c1b41c6686c03839 -22170,0xa0075e8ce43dcb9970cb7709b9526c1232cc39c2 -22199,0xa0b798bcaf87e033e2e6b6c1fd073203f314475a -22177,0xa1434baa460be13f75eeccc6a7314cbd2ef00bd3 -22209,0xa9a4eda1b277333f29370ba22afc0baeb0adce67 -22285,0xa9aa42831775d11e5c98d8de5daae9b620bd44e9 -22272,0xaeb1b49921e0d2d96fcdbe0d486190b2907b3e0b -22206,0xb0cabfe930642ad3e7decdc741884d8c3f7ebc70 -22276,0xb2f7d27b21a69a033f85c42d5eb079043baadc81 -22196,0xb33b93712a1cdf5379a13bd00799540452007c84 -22292,0xb88cb36743db01b46f1a9c31831b164654e08344 -22211,0xb8901acb165ed027e32754e0ffe830802919727f -22230,0xc5102fe9359fd9a28f877a67e36b0f050d81a3cc -22229,0xd281c4e19dc1f646bae1f3105aa43395a98550b3 -22174,0xd4d28588ac1d9ef272aa29d4424e3e2a03789d1e -22273,0xd7d52278b91a37a68bc1e2c9e4c71984464efadb -22293,0xd83d1dfb88bf293eb110bea05bd5c81ed21b48b7 -22274,0xd8926c12c0b2e5cd40cfda49ecaff40252af491b -22264,0xde8b1669158dd3a3cef6ffbf6c285f15ba120a6d -22176,0xe13a2d5eb0e3e34022556ddf44a1ff1a5a930ba3 -22295,0xe35046e568fc86ad5dd4f7510fa797efb287a406 -22249,0xea4f19ef42be85a381eb9a37a51e094aaee761c1 -22153,0xec4b41af04cf917b54aeb6df58c0f8d78895b5ef -22250,0xecf268be00308980b5b3fcd0975d47c4c8e1382a -22244,0xf11ebb94ec986ea891aec29cff151345c83b33ec -22222,0xf2a1c5571808efbf7b174bcfeace9f2cbcc51d7e -22166,0xf587b9309c603feedf0445af4d3b21300989e93a -22228,0xf7a67cf5f140dca1a68931745d5a239dc8b97f24 -22210,0xf8ef312f977fd610369a6d538bf3c2817bd3193c -22466,0x260c1a016c5708d96096ad2f653cce4b40092f28 -22476,0x37ab5e12d9bef0daa4399eae9cac62c6ba9e904b -22475,0x4e0a63c5146c90bf9eb172255d4d4125ced8e4e3 -22474,0x4e816ef463abd19779e1e31c574fa4d304c0c197 -22477,0x54a06197130e02aa0244c4a413f70c52348c3610 -22469,0x7e277614644409a69ea70df5cfcb114e4a51676d -22482,0xb3999f658c0391d94a37f7ff328f3fec942bcadc -22467,0xbcf096cd593f656fee04f54df955ebcf6e21d6d9 -22478,0xc3ae72e62359bdc8cf063c26b75966924e89a27b -22473,0xc7e3f7bb22a4407ed9641b0c89ab06fabb5790a9 -22468,0xd33d83aac02456839400140d3e4c9a80347ac2f8 -22479,0xdc1df355584bdaada533d1108ea75d307db946ee -22480,0xed2f78f4ceb26c2e40056fba5468d8aa0e105438 -22472,0xf52637995c01ac88e7e7f55a8c8cc0a167120385 -22471,0xfa27029d4c5943f73312d7fd97deecd7537d7339 -22470,0xfb1b9a97f1836173390d8bdeaf9004727311a8e1 -22481,0xfccd2eade7c1e090f48e219331fa86385abd3c55 -22716,0x0608c83c77ef641d78045ce87e2e13450d866133 -22721,0x0f27d74e66b923126c4ca12bd0eb814afe743c47 -22738,0x17f511ec08dd6f5e36acbc73b64306eaec55393c -22723,0x2098aea80611b51ca26586e122b3c629a2f263e9 -22730,0x27403fb4be8f1e4d869cd1a888a20933a869bbdb -22719,0x2865f0539a6b12d1ae099f5689d38a1734a5e533 -22726,0x2e86dff2c140f7169a93018f1366df0d1b60fc45 -22734,0x3b902a8a1e04f1e6957f589804ed2d8a9f9489c1 -22737,0x3caa041d1a0a78d141703c0e95408c1801ed74dd -22729,0x4939530837c7e4e883735212622091e049bf639d -22747,0x540b73cfc4bcd1c1a9eec86dcba9e256b150925a -22731,0x5ef1b1bf27363e3728eb3edef4e0b8a97c01d845 -22741,0x6bdf77554c02e27dbe47ac3a0f5a27f28fb8ffc3 -22739,0x707c4622aaecbff23ed56133b8e19ef940008998 -22746,0x73a42213d73ec0aa285047e272f26d5e96137cbd -22718,0x73f28ae13d96103f7e276b963c4ea40ffe0bbc0d -22743,0x7aecd735b862d8fd158e0c8e1f6dd2c268512780 -22742,0x7c85bc57310815e60800d91d00732ff36f1872bc -22744,0x8906151f76948cebc1cdcb556c38cc7d3bbbe61d -22732,0x8e0b8d765159ceca4cdf5f22d0d6478c79ab788c -22722,0x90e37c98874fbd1e6ac35461cf0abae457bb599d -22720,0x91b4334fbe88a0fe87595bfb36e085679e5a1e8c -22740,0x9613d0389dbe4f4f3e52b6c38e3980bcf498a782 -22717,0x983d4db882bdfb1b1624ed09670535907a27e112 -22727,0x98d7edaceb21e9d0d48c3593e5fc466661ea0bc7 -22733,0xac3e6058c55ca81c229f25162226f0a0e6581917 -22728,0xaceb52aabddc78cceaaa034e9b48d62f29f41221 -22745,0xc5d9303a7b3dda47839ab80916419b45afe22d6d -22736,0xcfce50f38d4d727b24a3a3abdf790049371e6328 -22735,0xd16046b1f09a6b26b4a6a9ea022e8ec51807177a -22725,0xeb23e59cd33b9711d6da0a51a783c8429d2ce10b -23157,0x025d9d36c616946530ff8ea32d912abf73170947 -23126,0x067aac7397a5370a93d4c85fe3e5a02cacbe9915 -23145,0x10615d451a5b91c92ce8538703e7aaba5d5ccc4d -23114,0x158b0b1414f153e58f8acac50e777feec234dd9d -23134,0x174876ce93449636cd9f69f283e97b2277639445 -23165,0x1cea29e753add8b00d368e67e8a009fa6a4ad9f1 -23142,0x21b2cfb4976e66b70040efd9d48d37dd857e327a -23133,0x2544f398f8500a44f901fd2b9a0ffee9ee938fa3 -23159,0x27d72395a9b6170b846dc9283d5b041a4413426a -23148,0x29563f73de731ae555093deb795ba4d1e584e42e -23155,0x29a7060787fb6842751138f6d4c661a18a84541b -23112,0x2b3bb2efd6ac1b952aefb316bbba088bc0c1010d -23160,0x2e5b8913971990ef2938290aa0abe780199b0fad -23164,0x2f30225c0b75f4dcefd0425b9bed920adfbae2f2 -23135,0x37133a8dca96400c249102e59b11e25b0f663ee0 -23153,0x3884fcd0db58c0fe9f6da852fa43db898524be95 -23118,0x389ee3f9a95f5cb73b50d138679b3f21883dac37 -23156,0x3cc0a623f1affab5d5514a453965ce8c80b45549 -23110,0x415bab1a6fbde318bb063c92b2d74dcd3ae0f0dc -23143,0x44998eee6fe3bfead1e6a69cd15208e2ca4a5280 -23161,0x4ce5065045f0bc85858b3e81ae86faaaea22bb55 -23131,0x4d8d90faf90405b9743ce600e98a2aa8cdf579a0 -23109,0x57448a16b19d6e45fd097f1021ef13d30bd648ef -23123,0x5a249c0941fbc181d8b2dadb70b7f83e82896ad9 -23127,0x5a89ec095e6466379dcad8b60059a95977e14fee -23163,0x6387c7193b5563dd17d659b9398acd7b03ff0080 -23115,0x6a0406b8103ec68ee9a713a073c7bd587c5e04ad -23162,0x6c8668f60cd03442adb361a41f264152ae17e475 -23158,0x6e20e155819f0ee08d1291b0b9889b0e011b8224 -23125,0x7024aa60286264ab0d20be9920de68a9a53f8f84 -23139,0x72fad09e3da8cec0e975bf253c1e5eafdb927fec -23154,0x7366c31311ce84e0009334d2731e262f17f8b217 -23147,0x7ad7c36c6e0dee5b8df3d4cd0aa57d03ba6df96a -23151,0x7b48eb52c1314b4ee374315b343caae38ff00e90 -23120,0x7c8e7536c5044e1b3693eb564c6de3a3ce58bbda -23119,0x7f8face1fba2901cb46f8a87a9e1717ecb215210 -23116,0x815d8963f3d54e025be83b5757123a76238a4857 -23140,0x872b9e8aea5d65fbf29b8b05bfa4aa3fe94cc11f -23121,0x88a7da508a7dd4571e86c0aab4747943302914c5 -23146,0x88c5f55255788708dc3db8078770edaeb334ce64 -23113,0x8fa4a73fae398f41812dbb30dd595abfd4bee585 -23108,0x9546f673ef71ff666ae66d01fd6e7c6dae5a9995 -23111,0x99b0c08fafca02c51fd08291376892dd3b8f2cac -23136,0x9aeefef549323511e027d70562f0c7edcdeb294c -23137,0xa52e2bf962b714071064de9b5eea792ad39f4250 -23152,0xb3ac95152e9ab64f8f2bbae4f0024a6c01cddda5 -23122,0xb80cf55df5406300347928e319c7838b18be74f2 -23144,0xb839c82630a36b5f7a5320cd0814886bf4900f1e -23130,0xc01a7ad7fb8a085a3cc16be8eaa10302c78a1783 -23129,0xc63c9511be941e6c15e6c43e519933041ed1ce56 -23150,0xce1cb5377884ee4bc2d30cf1588de9e352ac7a7d -23117,0xd539294830eaf5c22467ce6e085ae4e02861845a -23124,0xd7a4faf578783691d3b28ada46ad43fe5b8ae578 -23132,0xe1752ae13c5fa44bef8c7352de499d95a1d56cd8 -23167,0xe2dfcb86c99599dafc12028f67b8456f99c9de4c -23166,0xe51a7937a669cb4f9291a03fc44e407302f4ce16 -23169,0xed984a0e9c12ee27602314191fc4487a702bb83f -23149,0xedfcd7f3373a788db9f26f022afa2a9265eacd8e -23138,0xf5604a93caddb18a4ba8ee8f3f20324ca286dcae -23141,0xf6d1cabe63237970b0e5fec45afc5b2c1312d8e0 -23168,0xfd389dc9533717239856190f42475d3f263a270d -23128,0xff50bcda308254549be68e5f3e79cd6304d95478 -23590,0x00cd233ae7f31dc3664401cb040f24f6bf615668 -23565,0x0c1b55f485df69cc10527995c48883438ad3fc4a -23567,0x0f98547e09d41e3c82086fc5eb0e42ab786aa763 -23564,0x10825896637298473877b6610bb380be51d20d04 -23566,0x16794382eac0ad54e6d6204e65d51bb9b1d5f4e7 -23578,0x1a7c6f95b7d4a3ebea6ff4ac98d8befa525c5b91 -23598,0x21a1f8279780d6d6ef09aad531525b3b25917386 -23575,0x268ef1e2c19c4d10cdb24a1c8d95b7fca1badd01 -23583,0x27c43ee6b8def6ad804ca9106736a43ff9dfb6f1 -23574,0x4a850f463d1c4842937c5bc9540dbc803d744c9f -23576,0x4bbe502461d67d2cecb3834ff2440510f33be024 -23584,0x4be4b959ee75226c517e1abe5d9fead275583b2a -23588,0x56296242ca408ba36393f3981879ff9692f193cc -23573,0x5987a30f7cb138c231de96fe1522fe4f1e83940d -23591,0x5a780d9477389ef8197c302d59bb5a433b39b56a -23581,0x606e4a7ffd2cf4db089bdf530c17618ea29b4f1c -23592,0x794c30729afd129716ebb3c9dda9b8b8b95d7f7a -23579,0x809e751e5c5bb1446e9ab2ac37c687a35de53bc6 -23563,0x835a581472ce6a1f1108d9484567a2162c9959c8 -23597,0x9bb7ee67b688e4a5e9d24cf9604996c8dfa1c9ab -23572,0x9cb0679806225080bfc3a9a72b09a71b95756a84 -23582,0x9fcc854b145bd3640a01c49aa2cfa725ed0b4210 -23569,0xaac049be4ccae52d981585371829b5aec4a13f53 -23593,0xb70acf9654fe304cfe24ee2fa9302a987d22c31e -23596,0xba4999bd9cea79a76442f1e1fb0e5e448867e3be -23571,0xcd3618509983fe4990d7770cf6f02c7145dc365f -23562,0xcd5abd09ee34ba604795f7f69413caf20ee0ab60 -23587,0xd092e383478bc565655331f0b88f758eefa2eeb7 -23589,0xd8471d139e1dbcec97ed14bd69bdf8001d28f6fa -23570,0xdf25423c9ec15347197aa5d3a41c2ebe27587d59 -23577,0xe773f10de367e803940693cd3aa3c563a354de26 -23594,0xea190adba4b43f35786a7fc2d5c5f75232271e2b -23568,0xec499a9082142ea0155dd6bb59215384c31c69c8 -23595,0xed42e0f4391fa24e579b16191f6eb41f934c3b1c -23580,0xf347ce7a0678afe4e7498172e5aac76c5aedb7de -23586,0xf7b93519a3a1790f97f7b14e6f118a139187843e -23585,0xf7de229d52cda2ca42bf325f467870acd92fdd2b -23647,0x0e387d23cbca12954971c44fb22c071de382fba6 -23649,0x0fc6c37755603dfdeaa63a227ca78850f31d8478 -23639,0x41d77f5a2173ec878f33d88dc5d369f2f29d4954 -23642,0x455391cb23189f1bfaae1bf2de62718baf33d409 -23646,0x53e71045cb4611374e3b28c1a996d25a4397fe45 -23637,0x5ad276439e3772fdb6696b6cb61401902d4e8b72 -23641,0x5b832c4b91ede6b13a3301cd0f43a5cf30549643 -23640,0x845b42deab9f007c5a7429606cd01596ead9f77b -23645,0x88f2f48a949b21bddb00fe735ebba79b42f8e261 -23648,0xaf34b014fb7efcc89094c3ef25be7c3c29c1b2f3 -23650,0xca968c8a5e10d037af18ead8eae5c6967a1ad0a3 -23652,0xcd3cac9dd1ce5f2e6cbff6de7a5f4ccb6f8207dd -23643,0xe037e66cdec65c62a4141aa495e80b9ad1f2ad4e -23651,0xe08d2e25e156c602ba78fc66ecc8291a6c157b0e -23644,0xe5769b506e624044ac2d472e76bedba53dc2bbed -23638,0xe8f5c41fc53ea331a68e45cdb0ee2f8849edcaa0 -23789,0x01051113d81d7d6da508462f2ad6d7fd96cf42ef -23810,0x0322a0441c5663280d0443b34bc4b4c477d6d29f -23781,0x04462c8ad55a3d970fd9b4944a2f4c7c15700883 -23826,0x1064d40bad88574d1bd6c844d1e6bd4c84506172 -23780,0x1d1cb3baff5455defb49146f6b29e12d1ad4ee91 -23802,0x2625afbb3e63f885bc4de0f289571d81cf228b0c -23796,0x27ee02084023fd171e9798d33c8bba4255b24775 -23779,0x33bf81bdd8185fd1798c493f6937e15d88f89fb0 -23795,0x33faf125fc531a99cd764129bd2769a7e4c4daa9 -23785,0x340759c8346a1e6ed92035fb8b6ec57ce1d82c2c -23788,0x4055cb250ec8d539c5222eaa71fa7e30fe94f8e9 -23799,0x4f43aad55a21bb8661147208d61fb96ce0467f66 -23809,0x52677d053fe817c40556ed1f4178aac360619115 -23794,0x527a819db1eb0e34426297b03bae11f2f8b3a19e -23824,0x5a70e998be2bb781af0e33cab598ec2c0b2bbbb5 -23787,0x63c51b1d80b209cf336bec5a3e17d3523b088cdb -23798,0x71c2b9c9708c19a816e8e652f7c4cfe3f3a3d6a5 -23783,0x74c1f8ce8f2c6d6c8946d8eec15374f9a9c57f3a -23801,0x7abbf5a42b425abf775d26bef7c3c0878a2937df -23791,0x7c5c4af1618220c090a6863175de47afb20fa9df -23818,0x8341f96bd961146023e38ebffe3f2905b1bd9fb6 -23814,0x86198dac220d960d1ffe72acab541798bf7df2af -23823,0x88682f3d90ae60f65763f4f3a46044f620f948ab -23807,0x8ab6adbc1fec4f18617c9b889f5ce7f28401b8db -23806,0x8e0d2df14178b4f1b2907e566127006c80738810 -23813,0x9193477b8085e1cdceb50792fcf303e3b41d7ac5 -23817,0x926a40b485b8bebf8f739fca5c45f7cc683890a2 -23793,0x95f4538c3950ce0ef5821f2049ae2ac5ccade68d -23790,0x96d05a93c83340a45492eb81fad540951ed62eed -23820,0xa2cb32faa843bdeebc89c26868ee45085c854fa1 -23811,0xa5f9b728eceb9a1f6fcc89dcc2efd810ba4dec41 -23804,0xa89ec25e75096379fe3ef4f0c4b7023a30a2f41b -23800,0xaad3a6178d741dea76f57901feedac0f7bb280e5 -23782,0xb3f5503f93d5ef84b06993a1975b9d21b962892f -23825,0xc28aba27c97a7fb345da3b1012cd526383ef3b0b -23784,0xc8ae2a55bc4bf3280a38c76077a43d4d4a086272 -23819,0xde13771af71efb856bc070319e3c14e827b56584 -23805,0xe196330228cde3bb6b29413f32a95c45accf4952 -23821,0xe8e0873f12d7e04630989401bc7b54a67e403df9 -23803,0xec13d90a14b0310a679cbeab8d5804f5840009cc -23812,0xec5dd455bfa2436491b0f580bb017d7ef79a845f -23792,0xeced284da71818dd95779260cd55833a6f44cd93 -23822,0xf76f764bf04a64c70ea5cbd01b366af1c26a0f83 -23808,0xf8c08e9626293bec226cfec1b4684fa9598f17d0 -23786,0xf97e8f7bc4f69666b517b415c16a7a1c0de5d057 -23797,0xfcbe47b22457363988c9f5c06ac71571874f8194 -23935,0x03e074bb834f7c4940dfde8b29e63584b3de3a87 -23958,0x04357b844a321b5d5a2a370ad0cbef43dac776ee -23969,0x079be6d395f6ff0090f5bdc20b4b618590a81b5d -23954,0x07d1c8e181a1b8694d43577656fddf53df9afc20 -23968,0x0965d634a98bd702a9b7e40dfed7eabbeea41b0b -23967,0x1085baa80b2bfb2f0e55822ce2f547d95aeae7a4 -23937,0x17744b586a6e47a45fa98d080141139f87314e82 -23928,0x190b9e10c3a02896386be180767cf6e89df5e798 -23927,0x1cf24e4ec41da581bee223e1affebb62a5a95484 -23946,0x229195fe6cdb876ed7d55f939b5695e27b3cb425 -23926,0x26d44a1f39a0161617db5fc8a54f542b5224a33a -23934,0x31652d308ce8fb667a87646830d3ea359883856a -23945,0x3e57e261f1420f11688783534dd4a462a6b63bbc -23959,0x411c166474432c59aea0af9e1369f0dc08c1c5a5 -23925,0x4158bb5fdb8151c754a4d25ed93b61a80c855621 -23962,0x43b007888191ad30aa4548599a7bf5fa8cbd90f9 -23931,0x48cdd2531b35cfa35061125ce977d796571e4627 -23944,0x4a8b5932bcc2075af7a80169ee143721387c1e17 -23957,0x4b62c0369a13b68f75bbbda9fd91ba8270ca4862 -23930,0x4cb46032e2790d8ca10be6d0001e8c6362a76ada -23922,0x4dcc76fffd9b8345b8daa15414fbd787a3b226db -23921,0x60a048475ee20d8fce3ef18d4134d4be7771c204 -23951,0x645c72e90ef4d56c6a37802024467170a618f3e9 -23952,0x66211ab72fb0a06e9e6ed8b21aa3c1a01f171521 -23942,0x6e16394cbf840fc599fa3d9e5d1e90949c32a4f5 -23966,0x6ed2428624da78cfe2daec70be171d1752cdeff8 -23964,0x72bb87f030ab1d668e3c58a206a7140706e1b474 -23932,0x749a2a8850c1861afa1003d876d90c9b730ca58e -23950,0x76e77921341e4414e60bde4a3f56822c70e3adf6 -23933,0x77f535f7b2785fe0fd310dd97d30c5a4e020019a -23941,0x82a2646400032eaa9c32022f7b1d4335e1789f85 -23961,0x88ed3b8d03e5a70bf23286872b24cffd76e91922 -23939,0x897167c23139f4588001ad808263aa999628387e -23923,0x9de0ce8aaa2772f9db00d223ce9ca17fc430943b -23936,0x9e19fbd8c75184eefe096932fbb6cb338d3be7cd -23943,0xa9c29ea1a067740be6db1f98fcba0043c475041a -23949,0xaab2aafbff7419ff85181d3a846ba9045803dd67 -23938,0xac9448604f09b9e16c5e99d298e54c055bbbe983 -23948,0xb41fd44a65bb5e974726cfe061b6d20f224b2671 -23929,0xb5beccf2734c97221379a6c08b718d82023b1498 -23947,0xb71073364b78debe996d041bb340ff4f03ff23d9 -23955,0xc2756b0d2c65fbd1b695772f86758bfc8a5f3dad -23953,0xd3346737e370119961c444c0c1b10d7c3fcdf36c -23956,0xdc0f52145292e8191befcb427ae5c6d1ad47dbc6 -23924,0xddadb5dca827f8d59000d610e456630695928660 -23965,0xdf2aaed088917b611805af0831da56aac3b10ce1 -23960,0xe07d8594ca4fa3f34c66ff49be1b1352b2f8b64d -23940,0xfef427628770136f1b9b3252b05f6b06a7754f3b -24121,0x0ae84c1a6e142ed90f8a35a7e7b216cb25469e37 -24119,0x341836e0c5741527c5a1898307e690ab84e53d2f -24120,0x94cff60496c71a0302ababa0da1a1f21626f9613 -24123,0xc2544a32872a91f4a553b404c6950e89de901fdb -24122,0xffe66a866b249f5d7c97b4a4c84742a393bc9354 -24294,0xd870a73a32d0b8c34ccf1e6098e9a26977cb605b -24308,0x15c32f81df9b00b97b68148b6bdeb72d57f24845 -24303,0x28b413f9f09fba829a4c0b43783541373ee3f6c3 -24309,0x3b090839c26fe3b2bdfa2f4cd7f3ab001ccdf73f -24311,0x541a7b5a8a6bb28ec44fab9d41cc2b4aaf4f9a33 -24307,0x720face4d24018cbe1d68b1df64a730890598f47 -24304,0x78b2d65dd1d3d9fb2972d7ef467261ca101ec2b9 -24310,0x7efcd4c1ff21ffcba1b6a245a9e385b4ff45d6dd -24312,0x8ef21e9bfe8f40eddd9bf7828707da47d5536c4c -24305,0xcb1205ac28693beda01e0b66e9b4d06231609bfd -24306,0xfd49a007d9fadf8904849edbef1b3344e42142ff -24528,0x052a8d32901739646bae1850da37105af47f8804 -24568,0x0c9d44f5a573f6cfc9e8264a5ca72a1184616ef4 -24520,0x0e4e714fedbd81d0266ec486cf7c165129849109 -24549,0x0fc448f9c53653006e6f7f3d39d7786ac97b959b -24551,0x1a97e89572fc303e4bfd4fa49d02cd94161744dd -24564,0x1e42739587bc929a823b1da099c6742b5c0f1163 -24538,0x292a1f318b1ab0c7701f30f42603adc7f95f5e66 -24521,0x2c58ece45ac754b80fd39f5e7e67a4b0e91a1525 -24523,0x2dad3a13ef0c6366220f989157009e501e7938f8 -24526,0x2f879c47f4a89a89d69c21fa61d6705e26178511 -24522,0x360b864a59240eebda0f1a9b2534ea96ed591b53 -24539,0x37b25ef89a21049b66edc50a59678ef784544cab -24541,0x3afcb1ab1a78ecdd075647566258561acbec01d5 -24543,0x40059b04f08900d51868da27e30f497453e0462d -24534,0x47efc0fe6a096c98d7e022daed2309aa9286dfef -24562,0x4b677e52be607c8f7b81e8dec0d9711a5c7010c6 -24569,0x4db48b2e9cb0f203eb0a8579b274f5ac3d53360b -24566,0x4f4940a87d63a290d43b6550911896ff7aee354c -24565,0x5a583ccb69402d60bc360d691eedb8efda6fb7e5 -24550,0x5b414093362a7c768273d5f0405744b8c09556a8 -24527,0x73c3c06a2e1e5eb3d24f2ae021a1af8e0dd2b5bd -24553,0x797de59d04d43261a402cf3b85ed76c4af40f5a3 -24556,0x7fd13c20eeba7a3776ebc422cef7c8752d40766f -24557,0x85603119c938750dfb5904f8a501b64f3f3a01d2 -24563,0x8756d971a8fcede86763a67980fa161ff54fdb9c -24545,0x8774da8cc2d4af76e0abb2a46909d614a0a35545 -24540,0x901850951df792103e1aef6b52a68453941c0ac8 -24552,0x94114a6494f6b8b87170fcdfc41159ab8e7d4ec4 -24567,0x9511d7b9ba1eaa5ba58c716977b0fa32e2aebecd -24530,0x9ace8a056b0d87ed099876cefca7e234ad9099cc -24558,0x9f4c4fadb66b19b687952fe20f7659ce23ff99ca -24529,0xa4b43b3d33f4e583194d05485c5691c0b205a75b -24524,0xa4dd7df0a13743508217941d2b155b5b32df7045 -24537,0xa69b526924f9403ff410591cbb8281146a5c0d3f -24546,0xac9e42209038bca0b2b026906de97a032400f2b4 -24535,0xaea8384088eb1a98e555d8b5c8aaa953087716f0 -24536,0xb7d8613728efcfbb18bcd63deec06f64441d322a -24525,0xbced1ee549dd723bebf8e6f784150858e56e3ee0 -24547,0xbedfda828475005dc8e4a09aaa34bd0b8ca807e4 -24570,0xc719a01e39553e3d06d77ecab604c3ec3ca0b932 -24542,0xcefa665165281b7a9807bdcd4d7ac54a54ca1e13 -24561,0xdc15865785a57c13a4753c8318caf4d775ffa417 -24533,0xdcf0f4a0fc44f33de35d8c80ad5d248dbf838943 -24559,0xe0bec4f45aef64cec9dcb9010d4beffb13e91466 -24548,0xeb94331e71f7e0431de9e61c87ee91ffd5493d90 -24532,0xec120bc6e12cea127553b1f53445947b1f73a132 -24544,0xecce0f5e758d01c142ebd5ed2565038e4c0edd80 -24531,0xf082e2d5e1adae33135e9948bf6ca37a74ed2433 -24554,0xf1f46ddb3e828f22032197f8e92af9db76b3c3fe -24560,0xf9cfb8a62f50e10adde5aa888b44cf01c5957055 -24555,0xf9d40a60fd6bdc866daf0918264a30acce1dc9cc -24616,0x004d1bf176c59890e11e487d1270d809df188c07 -24609,0x03859fa7549449a7dde7663e5c06a38c5b0d4489 -24630,0x0b9d4ab484f95090074008ae59db1fab6606bc7a -24659,0x0dbd485f80136bdeb4b1d69d294d6793b6cf83b2 -24631,0x0e7eb3feeae852126dec868505961a0a43823b6b -24667,0x136d84968d65ffdfef32a4fe07660adbf60cc9da -24593,0x1586c0aea22b2a7a943d7af5a097bdab3f809409 -24627,0x16748cb753a68329ca2117a7647aa590317ebf41 -24650,0x16d76f0bf4b39dd2de7bcb112ffbc0c8e78e4560 -24613,0x19d00f1cd36770c283b0ed62794f02a78db928f1 -24653,0x1a68b97d70bc7470fe9a981f2960f2583fd37f3d -24671,0x1dcf89dfa88363ef33d49dd591b1ee5e84dd0f75 -24692,0x1e5efdf50e47176e062770d5c0eab5a2f196c012 -24647,0x1e925de1c68ef83bd98ee3e130ef14a50309c01b -24675,0x1ec842425a674d1a6af6bb3ef80aeb7ed02930da -24682,0x1ee401f9ae4e834ab4a8d02499f23a40ae7a4dd2 -24611,0x22ab31cd55130435b5efbf9224b6a9d5ec36533f -24607,0x28034c536d6b0d8d23034d89c3417fb9563339af -24604,0x28f3d029da71d9d53ffff921d5ab6e41c3ffddb3 -24642,0x29babff3eba7b517a75109ea8fd6d1eab4a10258 -24654,0x2c04733cdf38c1883e9cb7142632adcd5c5b79da -24640,0x2dda32b8e9ee8ef2b4e64825373d007cf222747d -24601,0x2ec9c8ab2ed93189d5998f53929a13450fc00971 -24669,0x310a2694521f75c7b2b64b5937c16ce65c3efe01 -24592,0x3179265d20d13ce507157b8087de48759eb21006 -24603,0x323ef62d6ddbd2a971d347299bb68e212af702f0 -24612,0x338c7b1c8f0d78d872502186253430258b007083 -24638,0x3744877f596db480c52aa89d8c8b1c67678feb93 -24684,0x37ac9c4a26db589ee35215c7053009ee645585bb -24656,0x389a7161b5ff4d57e23bd90186bb8204f7f12d4a -24608,0x3a31a7e94b30bd92151b4711522f118902977c3c -24699,0x3b44f6da5958ccb16b30f3dc433920f151a51c50 -24606,0x3c6bd2ffb9cb007e469cdd7b08d79102b5ae2b54 -24663,0x3cecea7ef91b6f6d3760f6b5845c3332dc00a420 -24689,0x3f0f47c274a8264695eb047c5e73bef531409e5a -24600,0x3f55a319d2fd003f87a96c1c3484121936243c46 -24672,0x3f6cd7982b83e10eb1a41c4c8c1ae27d0cfbe07a -24652,0x3fd0a9afae344000c53e5862b350a17775b39afc -24670,0x48304b3ab7f906ede1e9008c9b41a9528c26859f -24602,0x49f137f60b5a7fbad577d16f33cfe6be743c8c06 -24615,0x52274fbf893d5e05372cb05a8a8c3835f1eacf96 -24651,0x52ee5238e5676598551c8d2bbccb62c72fc3a0c4 -24657,0x54c5b9ae9c06faf8c55962a7212e1e69799764f9 -24635,0x5e454beff7378781376dcf5cb733fb4259e1c7e9 -24665,0x5fe09baaa75fd107a8df8565813f66b3603a13d3 -24637,0x60d92e570d096f8e5c99a600bd130d71295aaf38 -24626,0x60e543f802d6417894bf653b64e2a08f01a5c146 -24595,0x675d410dcf6f343219aae8d1dde0bfab46f52106 -24641,0x6b111f8602c51690215a4b7964d91d354ad45519 -24605,0x6c51c1718dc482b8001fbec5649174c9fb7d41bb -24596,0x70b475ceae83e0572a35f65cf45694de47617928 -24695,0x7f8421fe6c09b02eaf6905a27863be66268c6601 -24694,0x80ee33816077a79e6b369a8055829eee815e1b0a -24594,0x810221fc9ec7d0ee775983ade221d302e4ad9392 -24658,0x81c9a7b55a4df39a9b7b5f781ec0e53539694873 -24591,0x86f6d8b717d49527db3126f31bdd3b0047694bf9 -24629,0x884988e0bfb0d6a18f664329acd0402b2fb6056c -24680,0x8a9bbec5d90e2f7e25afe4620be66771bdbd2a6c -24614,0x8c2f35c8076bcb5d4b696bae11aca0ac0dd873e4 -24619,0x8cfc1c9279bad831261aa6bab111fed097c7805d -24702,0x910e91d24a948c3e36b71b505fb45fe80e95adb3 -24634,0x92024c4bda9da602b711b9abb610d072018eb58b -24678,0x93d386f247ba146201d4c72da1521f0b03ed40fa -24664,0x950bf56757264941d7bc7b9110a1c965ae1b498d -24666,0x97881f5f59ef58f6735de243ef44c084c91dfd50 -24632,0x98f4df6736aa6c5d26f0f9678102376ac06b55d4 -24693,0x9a9baa66bd7239e14f900aed6124446626accfc5 -24645,0x9c36ff0412ab4c03606eb1fefb8cac177b1577c6 -24679,0xa00e0bac10e4019765235ae0a4d882849e8367da -24685,0xa0d96f92abfb65330aa21550cd334455b5236f16 -24676,0xa15566e9cf856f3780706715a1b1efb2918304af -24621,0xa430a427bd00210506589906a71b54d6c256cedb -24610,0xa4cc12ba6848d3f2ee35db293891d95d9fbcba66 -24622,0xa6b60fb117809b05263c126691c707fb19713825 -24691,0xa6fca9d9f2c2daa0c16f09ec33ad0b064a6160ae -24697,0xa76fd3efea52575fe84294a92518f92810d18ca5 -24599,0xa7bffcf681be7ce32e6e6c17c240ed68ee6e16e2 -24673,0xadefc7f878e533b479e36af349bd3d8cbfa08311 -24677,0xaeb62e6f27bc103702e7bc879ae98bcea56f027e -24597,0xaec84eac74981ab22905919cb282b78c7ca782df -24674,0xb045acf3e2c3de6aeb4428fd6625e4f53c7ad2cf -24617,0xb1609a89ad8e99370348b152b331be880e2e2fa6 -24661,0xb27113b72135942065e0fa09984fe2bf008d5f3c -24687,0xb3b542ab7ad269bdcf722cfbd2f02051a17d9bf1 -24696,0xb5396df393b5c7b3d7b0c3c7288414e877366d3c -24681,0xb6083c415d22a6e9fafc9ba5ce491ebc46070b28 -24683,0xb8b1f590272b541b263a49b28bf52f8774b0e6c9 -24633,0xbd1ba78a3976cab420a9203e6ef14d18c2b2e031 -24655,0xbd60dea86ebc79aa294ad856ac104b34771a09b2 -24688,0xbd9c70db872fdd9029ee5fa2a0ea30eabf7a1583 -24662,0xc3cfa122e6067c520e2477e82ab97ba64495d120 -24623,0xc4d4500326981eacd020e20a81b1c479c161c7ef -24639,0xc91dc7a797cd5fbcf6f334c792a2b24eff55292c -24690,0xce454b42f142b779b9c9453955d8ea660c230d52 -24624,0xd46a3d7cb5d004dbde04015b11204745c78acf47 -24628,0xd6aa0254c488662427d508e8c2e43ca80a436201 -24701,0xe09b4f61bcf2ed5e8315693e205fa079b9935fb3 -24644,0xe296e3ae5e1db00f1b9710e6ad885215873cee83 -24598,0xe4f8ec6d400d99b83133985601afb97b8e554d42 -24636,0xe94e0068ea2b8ed9490d29bb534d1ba996412567 -24700,0xeb054e3390fdbaa7a510f601be8f149095b91411 -24620,0xedc5689308b3a5704803165d509df9d6d3dd4f66 -24698,0xf05385bc0b38e941f65d3031e1695bfbf8d60c90 -24625,0xf6da0e129fdc6e8fda49d8b2b33a6d4ba43c677b -24646,0xf880bb912b652a594abac1a9d79c968754d09ac0 -24668,0xf972f71332af1b7967ad21921b8ef4de84c94e72 -24643,0xf9b612ffe3fab24e74026d2b5d13cbcead6380f2 -24649,0xfac96a321f79642015d32bbcfda4b9d1867d49f4 -24660,0xfba759bcd1a99a7724c5068feddb4f5b844b941a -24686,0xfe0c5abf52e3e3076f3cb7f6323c7c1c91f54b74 -24648,0xfeb73a16d452549c7849cf04f3f8e7f09ac59ff0 -24618,0xfff4fe4af99dd3a1c9ac637c71ae2685a5358818 -24847,0x4200000000000000000000000000000000000020 -24848,0x4200000000000000000000000000000000000021 -25044,0x09a21dd9311ae7ec338bba59ee677bbbf8b185ce -25043,0x35b1d4fa1b3e1baf03e8b47d76102b647917706c -25048,0x57a1ce7686f3b2ab61f5191c76361f985b57e0fa -25041,0x992da75e8d8b2f7a13b1df173d755ac7540c87f8 -25042,0xb1f120578a7589fd9336315c4df7d5a5d90173a8 -25040,0xeade97afc8f79a8e5ba85d57c4a4e629b1160c6a -25047,0xeeeeeb57642040be42185f49c52f7e9b38f8eeee -25392,0x0fdf64b6746ba759d120e973c85349a8b9cde8d4 -25395,0x1d3e01bd14214fe3cd10904690dfa908c23b37a1 -25385,0x3b2378a29f9cbdb1f20a72611c0f000af05630cb -25399,0x4a6c794192831fb9f4782e61bec05d6c5cc9f3ea -25401,0x4aff45ce2e48c159147fa7dd5b436cafd9688eba -25390,0x50f9f913590e1dc0efc822e0552ebcba5882e5dc -25391,0x5f030dc08eea2abfcc497f3c5351746dac971ad2 -25393,0x610460dab540dae1eb84c22ea97866bfc0482c3f -25386,0x668fb4dd0d2c46057db97b1b1e38e6b99a472b5b -25384,0x784e00cd961a994eff675e889af6ca564689bf39 -25387,0x8fc296f44017b50ccaafb2e002d590c2e0987363 -25404,0x972b5aa054f8d21f3f2a9e928e26b3dacf99b44f -25402,0xa479b3af0476a9e605133975c1f0c414d0654d12 -25396,0xb60ce5bf2a231eda70825f9cdcd0795102218ab0 -25389,0xbf042126c0c0d2c658915c2d21922d3440092f48 -25400,0xc5b9041f9748a9a4437ba90f9806ce8c3f9085fc -25403,0xc80f61d1bdabd8f5285117e1558fddf8c64870fe -25388,0xcc8ecd1c017b68ae336bbc2883d54400d22e6deb -25397,0xcd887f78c77b36b0b541e77afd6f91c0253182a2 -25394,0xf2acd5f10e16bcd2b436701647513bb6c20cf62f -25398,0xf558c5bb5c77f658453dd4baf74b69f2add3d934 -25383,0xfcfa6855b3e79f1c3ae4314cc0e85f37dfa14b3f -25471,0x05473db56ad153170f61461c58d568791f6f3ff3 -25493,0x0be4cd601fa9b21dd1cada6a3893fd998bf5970d -25509,0x0e55e1913c50e015e0f60386ff56a4bfb00d7110 -25527,0x11ea8ae84bb067859e56d08bb551b3d70e5b569e -25511,0x124760902088ddbfeb8f27210d3b0c645a5c0a8b -25501,0x146b5c8dc3d077c928bfc7594103c8c1da688fe3 -25521,0x178de365229ddc479793a43ff2ebf2127f8c73cb -25491,0x1f58541635b9da02cbe073451046ecac29b1f582 -25476,0x219f75d739a48701462f2795f6a69770710b0c9d -25484,0x24c4d2705f6f0d6b6ce37b066a3d74c53b649c20 -25496,0x275c0c779ccd4d5d3a9d507d8529c943c4d59bcf -25497,0x2a8cb13758ac33ac24d8e6901e3143ce6c20d67b -25499,0x32705a4f4f2b5f072c30d541fd24a0bad39117db -25533,0x33477767e15885a9fee815b398d756c52196af34 -25526,0x3566f242bee341ed7d7947865a1349d65a995227 -25480,0x35c234ee2e15062324f1232ef9b4c41ea5e85b5c -25503,0x370675baccb2506410dfe7c94a60f178134d937d -25472,0x3810960a570f1c0b49f630970fee9acb64ef8bf1 -25495,0x3a2077b3de49d58c7eb1c93f8c37a0a524330c4b -25534,0x431287c37f31d86f86a99741200bddf1a9652edd -25515,0x498d99dffd9be5b55490335f6f9df8801fdc094e -25481,0x57f32f9c0e2eb094759ad76a4101714bae97c379 -25523,0x59761a4daf81b00ff06fa1bab3c01eb294aa55bc -25490,0x622f0715dcbf3eaa2f56c25c0603e66f082a3e74 -25502,0x6942b1b6965ef771743af4ea2a3244a4310bec8f -25488,0x6a99c2696e13b2a2a7f37d2f76f7c024efc414d2 -25537,0x6dbd6a32627b7165ed97f54d80016dcdd76a67ab -25492,0x6e49d90806af9b541dd517b2acbcb362b25f2fec -25528,0x717468c53103cf7d15a79b4455b8a1263e1ae9fe -25516,0x7902598f78fc912a6f2880ab692e6cc28805c39c -25487,0x7aa8e897d712cfb9c7cb6b37634a1c4d21181c8b -25517,0x7cdece74c6b8413072cf65a21ee57beef3063e4c -25507,0x7f4f2650b9659cb8fca62597fd87be7950942fe9 -25531,0x8179bc0fc9876a8514f317fcbe0a1994a2c31fef -25478,0x836eebba45dcabcfe5108b006a0cec72a14db61c -25519,0x83c3926e4288ba4f5139a75f9eb1bbae036e61d2 -25535,0x83efe5c5de92b02155de8ea6d0474a99ea0be96a -25489,0x8685bde610cf4136b7e40a7da16950c3046e2f32 -25539,0x8a466b97c6ac3379ccc3b2776d310fd4769f550e -25518,0x8cdc5fcc9162f05551c728283630ac27d0b5482f -25485,0x8f29231cc91bea2bfafd3c6b3cdbae2f54cc58f5 -25510,0x8fc0044befc882dcc99377f2d41ccd6be1628e2e -25477,0x96d8ed546e5374ed69a03d17d72a799a7a9b29e4 -25538,0xa2402daa9df9cbc0ffa729608e9a4357cd499432 -25494,0xa92c2ae3e1caa57b254f5675e77dc38f4e336e60 -25483,0xaa37b6ea5ba775f2736c3726b56d98ff2f6445e6 -25473,0xb3ccb05742bdbd51daa24f036fc5489f5ca0f38c -25520,0xb67d6f71f0d148c2d21740df961a5c004e36bbc8 -25514,0xb949a2648cf9209aaa1eea5dbc65b7aaaedc78dc -25529,0xbba7fbd2cdd4f145f7cd23547f1fb419bb246f18 -25532,0xbef1426930852516c61a48f276fc42ee30ba5ae4 -25536,0xc15617ca2bd59bf949ad3e7122dc7461d86549bd -25530,0xc338e9135e5ace8dcc70e2b9d320a03a101f96ae -25479,0xc8011c0158bea25e1c40ff84189e494717ef8558 -25486,0xd2eab485e9f0199ba2119aba4db2937d1459574f -25498,0xd30a54d45964abb9a48b652cd0c97ec18288b01d -25475,0xd48055cbd433d93f1aa000dfcd6ec36f39c0fdb6 -25512,0xdbfeaae58b6da8901a8a40ba0712beb2ee18368e -25504,0xdc789533da2a51791c06a2a5cbebb9f6bf04c1f6 -25482,0xdddf32ec4f8e1acc3dcd3fbce7869776ba32067e -25508,0xe03c4eb2a0a797766a5db708172e04f6a970dc7f -25525,0xe079521e2fdd7f7225c228dbfef8cc0430897149 -25500,0xe2a32a42f3bb1f5151dfa4ac30687ef22498604f -25505,0xe45e1e58237c9aa2d5ac63a26712cdaa7e5575f7 -25506,0xe8802303f6db1a658fa5dda5e518d852bd022986 -25474,0xe9dacd8118917e3a0522f45c191c6abe88d271b0 -25522,0xeabe087e69045992212972ff3c9e14499152d983 -25524,0xfbc6afb26ee9cec6337fdb595d76812b74986cb7 -25731,0x03d489d99998fc45d4a14ae260f537a2c435c0eb -25739,0x054a30b4fe877b0b92c3f1f4952a26ccdde19b4c -25701,0x07ab01da74874eeecc2255c5816a96a6e90eab20 -25654,0x0c0adb03423c8232a0b7785433919eaf62a59173 -25652,0x0c92617df0753af1cab2d9cc6a56173970d81740 -25663,0x0d535ca4c27f0c25a20e2d474ee3e99c1316bafe -25679,0x0dc9764beb9db8ceaae7e49a614ce270a22821bb -25659,0x0f18940db877d3fd173af087349ee87b853aa029 -25713,0x10ca65ba81f4f4d64fff0bae77ff0bc45e6ba0f7 -25662,0x15962427a9795005c640a6bf7f99c2ba1531ad6d -25678,0x16888e7935a1a08195e5a50477df5250c7f0e279 -25630,0x1c4d5ecfbf2af57251f20a524d0f0c1b4f6ed1c9 -25719,0x1cc2a5a1f7c4ea9c29be62a58a5591e82f96a02a -25643,0x1ee116b869ecc7cd13c629a8a2ae39fa361265cf -25680,0x1f144cd63d7007945292ebcde14a6df8628e2ed7 -25737,0x213f77d269d69823165d3f18ff81336329f959eb -25736,0x24ad4323a2e8f224f6c7bd5d063c3f65951643dc -25645,0x24d30216c07df791750081c8d77c83cc8b06eb27 -25696,0x26493cebc23f6369969862c0beaea25644e44fab -25666,0x271479036bb31de5bd4a3544ed5ba2b8ef4eebd3 -25727,0x2b575cfe387667b0a0b59ca5dd877a387d8cbd2b -25725,0x2ce498b79c499c6bb64934042eba487bd31f75ea -25631,0x369da886fc07b6d5ee5f1bb471d4f8e7833526f9 -25667,0x3ea2c9daa2ab26dbc0852ea653f99110c335f10a -25686,0x40a33fb67b8dafe88a5b1930be03c82157f47c65 -25638,0x40be37096ce3b8a2e9ec002468ab91071501c499 -25672,0x40f8417d3c98847eb553785b607edd64f90a213a -25692,0x4257de950f73107dd1979a49c32b014d3c7b203f -25633,0x428e1914404dbff52bd8c7baed9719cc5ed181be -25704,0x4333227e5a2ed1c439b1fc5ca0c025af6cb873b5 -25703,0x4653fc730e3c8b7eeb2faec65458c6f927ae6c3c -25685,0x46e803f7b22031717e5450a2a690d9aa42233cbd -25665,0x4779f4b09c74b9ed31abe60e1cfc3b1b4832f128 -25708,0x480798e9496a99e6a48948ab478807079289c4c1 -25712,0x4870fc0ffc415d6dae75d7023b9cb0f26cedb892 -25720,0x4b3488123649e8a671097071a02da8537fe09a16 -25717,0x4b72e30c9bd7e42186d28455b8493cc9b28026ed -25656,0x4f9312a21f8853384e0f6141f3f9fb855d860161 -25745,0x507d6c308d84f26c0c2a6df77d0090b8a68ccf4b -25676,0x515e0be731f23b4e4195d78b4fedccee186b4515 -25642,0x52eacd19e38d501d006d2023c813d7e37f025f37 -25674,0x5385bcc7420012babd14675a466e24fbe2f9b1d6 -25715,0x58c9e3b0ec4c37732ff269b605cda0ee3d256b0e -25647,0x5bede655e2386abc49e2cc8303da6036bf78564c -25649,0x5d05c14d71909f4fe03e13d486cca2011148fc44 -25637,0x5f7ca155cd53f552e60f8d1b088d6e4ca5885c35 -25695,0x61cfb14dd3b69d2bfff25aa16f1d34a1957bc7cb -25688,0x663f82d8191b04e826209793d592049f98172f24 -25683,0x6832364e9538db15655fa84a497f2927f74a6ce6 -25746,0x6a26318cbfec1e3e15cf5a64b0a326bae6adbbf7 -25673,0x70a35414fad53752c9352401be211779ec413bd4 -25684,0x7702dc73e8f8d9ae95cf50933adbee68e9f1d725 -25714,0x783f80dcd3af3c751104b165ea699c281ffc3f47 -25722,0x7c64caaf1e233d45a943ce903aebf7a6f1761bcf -25635,0x7d25d250fbd63b0dac4a38c661075930c9a87aea -25629,0x7e2dc2b896b7aac98d6ee8e954d3f5bdcc90076b -25651,0x7e7e1d8757b241aa6791c089314604027544ce43 -25690,0x7ec1f05fadffd3e8c0dab508e7f3266aa2e815dd -25729,0x8386145dd223d7f23a14490079d40627b252eea4 -25698,0x8417148b47ccb1e7c7249780e18cf5967b342390 -25700,0x86d9b423172b336bb7a4ba84354a9644edbc4c4e -25641,0x870ac6a76a30742800609f205c741e86db9b71a2 -25711,0x8d4eedf50d4e5cb672b13e16dcc8bb9e4c3a44af -25706,0x8e1d305ab9abdcb40f757b9e28c7782af2392982 -25664,0x8ec747dca348e35b5a3b94a950783e0e84707969 -25644,0x92273acbd2f898d05904aa2fb2ead119f6733b33 -25744,0x922d21f09b3e86395172864fbed543f2c8d518f6 -25655,0x94a14ba6e59f4be36a77041ef5590fe24445876a -25709,0x995b838c2daa933402cd9fbf7e6202ec8acdd714 -25724,0x9a0b57024ff206a658e46ffe9f60c7c14cf30b80 -25693,0x9bdeade7f62793b98834bc00bc18539a0342abc6 -25634,0x9e8b68e17441413b26c2f18e741eaba69894767c -25735,0x9ecd3c2b3a85a88deb7023bc03baf3bb66780bb5 -25632,0xa300a84d8970718dac32f54f61bd568142d8bcf4 -25738,0xa3ffff751db57b30aabb847d8f3d70c5567ffe11 -25723,0xa45d7f2e3152bc5e207a13ebbf6cd92208bf4d19 -25669,0xa6a9ea5421ed356ec62fa4767a3745c5419aebec -25646,0xa7a084538de04d808f20c785762934dd5da7b3b4 -25740,0xa84cabb3034282cb8b3bebd45132733a82e49e5d -25657,0xa89ebe8d7471d7d36acbfe4b0d086834390399b7 -25734,0xabf7fc467846923a9e3c573725daa1e6fb213f07 -25743,0xaf52ac01fa3b4d89d93056da818315317cecd3f2 -25697,0xaf7cbb43773cbf3fe1e035a72f4e67f62c702478 -25705,0xb173cf446a3aa3420c780d4a034d0e06b1774c08 -25733,0xb249124bab33f78dd975e4e725e6a20868d978ef -25650,0xb344795f0e7cf65a55cb0dde1e866d46041a2cc2 -25710,0xb50f07f637c6b782ff94c792051edaea4907fa84 -25742,0xb6ef2c765e994d435a2f486808de87e1e3eaa4c0 -25670,0xb7e0b3e00fb13eccf70907bc5b626f4f88f1fd85 -25707,0xb92e0763e3844f3f3d65c74f2ad691aff5f97f70 -25732,0xb9802440b4501111769cfb508f947f492467e58f -25636,0xbfd291da8a403daaf7e5e9dc1ec0aceacd4848b9 -25721,0xc40c933210213ea2cb1f0b5773b88643e1342ac8 -25726,0xc462ff1063172bac6f6823a17ed181a0586f0fc8 -25640,0xc5b1ec605738ef73a4efc562274c1c0b6609cf59 -25658,0xc76cbfbafd41761279e3edb23fd831ccb74d5d67 -25671,0xc9d1cbc45dd3e86e98067b7eb279c13f7b77c627 -25661,0xcf427e1ac52a2d976b02b83f72baeb905a92e488 -25639,0xcf66eb3d546f0415b368d98a95eaf56ded7aa752 -25687,0xd2de888007e7f417cec5c9119c48e18250cbe1df -25681,0xd65a18dae68c846297f3038c93deea0b181288d5 -25728,0xd7a23be4c0ded90f99e1974ffe7e1113246fca38 -25699,0xd8150d333aa9decbfd4b5cb535867ed9538abf26 -25718,0xdb196e256a60297feeda4c56172b868ed5d2b570 -25716,0xdd3b71c55869cf6f3a06926cd973127f9c3adfcf -25648,0xdd40bba0fad6810a7a09e8ccca9bce1e48b28ece -25691,0xdde8c60899e68e191498d798de653592c429ba7d -25730,0xdf0e115aa822443df9200cc5d0260fa8e1af06f5 -25741,0xdfec2ea848cf8fda096503f8d9f37afac6e0ecf2 -25694,0xe30657828bb95366f3e057be4ef29982eb844041 -25675,0xe8db80556ea859b15e5075992b4f0070d88b3465 -25653,0xec85f77104ffa35a5411750d70edff8f1496d95b -25682,0xed3c20d047d2c57c3c6dd862c9fdd1b353aff36f -25668,0xef535decdca4b72608ff82a692864e1a4ccd50e5 -25689,0xf56f6349b1b57e96c65a79fc782a046f44b8f0de -25677,0xf5923de1e8a7b884945d390166fd2f68c211cdce -25747,0xf6c307f3e9f2335ae2a0d299dc4be0bf390f1157 -25702,0xfcdb1a1afaab60230bbc55d8b3de27f47fb7053f -26026,0x055405f46344447efd7f771c747a5d09a81413fd -26035,0x1be6a71b0fec6b1071714c2390e306655e91512b -26017,0x28977a96894af29f582c6e1281a5ec74ecdc911d -26040,0x3171ba3acfa9a4858549ca508a66d63e3f820edb -26039,0x3c8d158e8a44b52418515dc37be75af286bfccdd -26032,0x42c6d736647b21ee2aa63a765542843a5cf173f9 -26023,0x5bb95d1b824bddd1e7fbf6e011ed3bc21dcbcf11 -26050,0x6069dcc91376b1628d244c4799db092af205fa38 -26024,0x61622417f3c3a7f6b29784ec1a5297bae6eeafc4 -26018,0x65028b18ee8216100e1edbb66ef7dd77192f1575 -26033,0x7deb95eb81b25e50400271b478b5766bf74579c2 -26020,0x7e31d7964984eff45642e3351b5775a2eae266aa -26034,0x82a93384a7ed690cd964da0ed9fb90e19dab14ca -26016,0x866331abe0546eb2ceda4cb5d528de718b2b7a6d -26038,0x92b3294584a374e076955ce05070a573b98b1130 -26031,0x98b9b1903f14c59b9f86041512ea9284b63205f8 -26047,0xa07646a5aeff5f11b011e22e7423c568f8eca8f4 -26045,0xa2eb3223c24b1246fd38e9b20b8ae564fd5c9700 -26046,0xa3a2904d0726a1a039568365006dc830234e7105 -26029,0xa845cadfea9c5e00c04e5631debe8819cbfceeb1 -26027,0xb17ee17c0b21a11ceea4378eaa235df86d82ba18 -26043,0xbae41647fe77244a2cfcffdbad3e0052ab7b5e5b -26022,0xbd76c5fed25dfaf228bfda6619e7220c3cf348b3 -26030,0xc0d6a410ed141c1369f64adb061851f28c112ad2 -26021,0xc26b9eab49e8ae942233770a43d1f15e8d67bcb0 -26042,0xda1cee888a5c917e4ce671b41fcd9e75bba07922 -26037,0xdcf476df1165d85477be841f4907569cf1413745 -26044,0xe28232728e3eb11b4512b3901c61b6a7ea35fc1b -26028,0xe3ec812a2ae6c10a59fc8186e880542d4cf0fcd1 -26015,0xe43b4cde662165ba1f646022e7d41aade8993b11 -26025,0xe54bfe6018cbaeaf844b0083147af68d48367b63 -26048,0xe689443ce80ffcc8999ef801216559e787ceb8fe -26049,0xe8e151a8138a914862c6e34b797e809f8eb8491f -26019,0xedc5ebe9fe347497703ae595baec028a34e315a8 -26041,0xf807787a4a03ef293aba59f153032206ad4873b1 -26036,0xfe2ffe021031756d9a556abbd9fc545d6fc21b74 -26109,0x08b8abce1ac67b0be4d5dab70dee950e99938e9f -26113,0x1214687e3aa359eff691d719971d81c9d8f0eb32 -26112,0x6f9d14cf4a06dd9c70766bd161cf8d4387683e1b -26111,0x70b015a16fd374c82697d8c1eb035f698567d496 -26110,0x719d33bc63a3474afd814973f79355724b385e30 -26107,0xd510dbac17f4afead860f4cfb172fbf7a020ce4f -26114,0xdc18115b29118c1a8890cf6fc4af3eb2ed32f91b -26108,0xf09ea2e82ebae3c06ecb579473748d4f27371e72 -26115,0xf0fb7602df6672e6da832a8ea3bcad1ee3dd6e52 -26210,0x01e90044e2089a2d5d8e84eed1139c2742bdf43e -26247,0x02389ff7db92d9b1063b64ce1fc0bdba84185472 -26224,0x030605530848177c08201494469abe89df197ed6 -26193,0x0673a749cc577cc348492f0de26facc0280c4a69 -26238,0x0c2698d597752e400ecc7f270be0a6fdc2bbe1a1 -26199,0x0d335a3f2284c974a70afdcab1db4d00b6c82079 -26212,0x1efd877eb785685323f4cb3c11754aeb79580739 -26244,0x291681a7cb4c489cea79c42de99986b984d52d91 -26197,0x2983589c067b36078ab65a603d9ce4bfba5e115c -26232,0x2d84fb9fa136828f30af793fe902138b586db9e9 -26245,0x36c3a2b8550558fe7eb86541dafed469cacd2ff9 -26250,0x3a50cf8122b89dc8785770cf8a8c07d5c0627c72 -26223,0x3b1d860ac03aa4faac49e1aed2ca36ceb3da506c -26204,0x4056334cdca09a54ad0e99c195a8de321406c242 -26235,0x49799190ad4ef8299e0d078eef07bdb4309f7186 -26241,0x4d637eccb6e21b18fffdbc8cf1327fa6de395c85 -26195,0x4e1cc2fed80fa764e05eaedb807a9c2fc85e1fd9 -26217,0x537af717cd649ae3307fcfd9c078e817df8b646c -26248,0x5392e06ac979e370fd45d25d0b5424cd8ca56529 -26225,0x591a22b85b8b7faabac9ced219ef19688d3e4888 -26229,0x5b2af1730efa60f72251d41ccaac41de69c2ee05 -26202,0x65d2630b722297a2b020e69e96d5c5a201dc6543 -26236,0x6660de08e651e63f09079b80bf4010ecb7d64243 -26211,0x66db420b6e9bc8e63578d18fee7270fa8cb47e2b -26209,0x69a8e7bf1850b1cf7dcdacb743b836fe38625217 -26243,0x6bce8135f21bebf9aa24b00fde283c63bbab2db4 -26206,0x79cc3c93e7bec01e03ee3249e1a661dd09a1cbcd -26221,0x7f6c3d5c5524b6b48607ef617f3c311ffd6d0588 -26218,0x839e8a3827b567391d54e2828b6a75b27a053fdf -26215,0x86a6556899a2c9cc1bb5353367fcf11d0a243c95 -26216,0x86ca89c04e6e09ef837efcaaf74805686fe3b349 -26230,0x8c68d952cbf05759ed858d9a9e454cc29932ca78 -26205,0x90f4d8deda37641d038079b99178a0a0d38367c6 -26208,0x9dca84cd4a631b6ed38105149c11d0688bfc5670 -26240,0xa35e98971608803c4f4bcd876451bff3f822cfe5 -26231,0xa7f09a12e056ff12d2528ded8b4658649ffd509b -26228,0xb63f1af6639bb53d21f31b621bf1088e8226565b -26219,0xba061f9828b389268c8bc79fd42f98bbaa062096 -26200,0xba489b949294b3be8f0413ff2a734a823845a2dc -26249,0xbdb11a55ad1e6082f18ac0faadd971bb89a8640e -26203,0xbed40b46f64d3d0bf74607b49744ce0182be6cf0 -26242,0xbf17ba651510a696338044355eeef1a3ad5dfdf2 -26239,0xcba1b717158264954a3f838b1f8b8d07e1dc1920 -26196,0xccff064c15e36be681ee2807fce8fe38176e8baa -26234,0xd0fb925f5ff15fcad74e7f0fa9242fd2ec8d190a -26192,0xd15141b6694bb5768ab80a0099b19ef229e838ba -26226,0xd2d92f35f52ec65d39c136ebd357004472bdc758 -26251,0xd46b102dd4699123bd4e0ba406357f5cbfe59afb -26214,0xd57e308e07c6dc80ce6784a0b3207501ab0dbdba -26207,0xdaee416231eb0cd1627bee8ae0b67af5796294c1 -26246,0xe3fbaaa15b2f35a58a9e95604a073a92d72a20f5 -26233,0xe513e1fa2116c9a79c97c151d0100b20da28a962 -26237,0xe7ae230f1b330866aae93960305fca251eb5f0fb -26220,0xedb701ccff9846634f4d73ed21b71caade0ea88e -26213,0xedc368077c56d0fdcd9860d5c937f871c4c01219 -26201,0xf342c94d81c6064941af6e511ea6b4b4ef39f1c5 -26222,0xf5fa95c58e9fbce863c37014d5909095615a203a -26198,0xf8adcf659201ad12b71ec049b20cf85d5e8cdbeb -26227,0xfe50be66e5a6643574a13c26b7039674b5fdaa34 -26319,0x33b72f60f2ceb7bdb64873ac10015a35bed81717 -26313,0x413dddce3d0ead2489648e482d192a7758c2b1b4 -26316,0x5706875cef89ab69e818d4921a47d0d2663d2728 -26312,0x58404964365264665f4f37217848b10873d50336 -26307,0x60e50145db18e09ff2bb277e88d3c264ff57b91f -26317,0x61ef2e01e603aeb5cd96f9ec9ae76cc6a68f6cf9 -26311,0x663dc15d3c1ac63ff12e45ab68fea3f0a883c251 -26320,0x7ec2e51a9c4f088354ad8ad8703c12d81bf21677 -26304,0x95c99ea6d7e72e99f18049019a427a4a17c53a30 -26314,0x979791c607a388702690599120c46332f61f592c -26305,0xa7b88a746fa457578d5abd6234471f07d895f46b -26310,0xc86ab72dc6da7ef91a96650f3bc23125cd997130 -26308,0xe7351fd770a37282b91d153ee690b63579d6dd7f -26318,0xeae9352c28a89b69ab30865156db18fd7fe7e000 -26306,0xef4fb24ad0916217251f553c0596f8edc630eb66 -26309,0xf7399c83b12edd1a21aab250d30ccb4474902688 -26315,0xfc2ca4022d26ad4dcb3866ae30669669f6a28f19 -26374,0x1429200a82a756aaba5d78e1cd4e88cee3f90a75 -26368,0x2cc0dc59063ec416fb788edf80d02ebee33d6309 -26372,0x32ab748043e4e562f38cab5ce1ac3efc27dad2ed -26369,0x9e9d792ded4335a0e0002d0b1292a2e638a16772 -26371,0xb1fcaf6d6b49ccedb84f0e689ca9656f78ed04e9 -26370,0xd1a57cb694cf4941360a937d5f5633b363204e18 -26373,0xe8d622345aebb76e583323cccfe3b124dd0714c2 -26883,0x01442d44bd06fe5e769bc371250f8d3d5270e682 -26962,0x01d0f84123299286bda08f716796b5279e4ff67f -27004,0x0315564a432c7955ad97e11a177fb8b29531fd5d -26766,0x03228ddd04f0ff07cb68f53d164b53dc2e4362ea -26729,0x03860eb57734beea99a79902b30f7d5d47c7d84b -26969,0x0472156ad30bb7f7138b63723c636615127816d6 -26922,0x04e10d18d2cd57267583de48f462b9aaa69aa277 -26770,0x051947f71f4b7fc8adde3a50a2f7eed8c6d20e0c -26726,0x054c194bb59787c64f09925381cb58807895d553 -27091,0x054ecadd4d26ec309e1a3b432ce310faab0b31df -26981,0x06c83f2ff77d4d5bee3923764fe1496085d51824 -26776,0x07e144e336fcc3fe928a8a2a5714da64354c1aab -26864,0x0838960ac4fcd6c9701350ae4283eb51863aae30 -27033,0x086d4bb2ab3a2b0dde8e8b5bae35939158ee79c6 -27042,0x0896a33e351fb11f405d3e9c03f4d4fc667894a8 -27054,0x09d5304b60a3ef8ef4f046d49875b0c31c902841 -26958,0x0a167dc9daf2c7c2ec8e3a73c5ff6985414b96f0 -26944,0x0ac209aaa2b1d078fda388629555886328334702 -26822,0x0aea690b8c68131264a727b0010ef3eee498c516 -26796,0x0af6f52e8e95c185fa27a2a98178fbf905ac8041 -26986,0x0b753bf5b4583a984d1049a19d9d0f3e6029e45e -26801,0x0ba200ecb463cd0d7a806efbf5748c19c53acac3 -27067,0x0c48510396da6d8661c3e18fd73fb94b46a2de08 -26733,0x0cb5491007d50c1f6c510dce8ebd36bf6f063550 -26909,0x0e43b2b9ea77fc80223e5b55535dfa7787a1217d -26931,0x0e45e996bc0152f8e46cd1bd082a4b902dd7fc09 -26735,0x11f8536f3a3c7bb16c3ac08fdebf3a819abe3105 -26873,0x136ac542035c2e5ae5e17f2684ef0eb5caabe183 -26955,0x142e2feac30d7fc3b61f9ee85fccad8e560154cc -26881,0x14f7d0ee5978a42029576cee7018879b56928b23 -26904,0x15b7199aa9b9cae9e611d858ab458aea8d36555b -27086,0x17c39280f075b17ccad807b67b910c13697c9050 -26772,0x180d3920a665b6768e48f305ae61e824a48aa56f -26783,0x18921f28c08dbf4f09a18191904fe4689a337463 -26799,0x18b4f4212e7f074acc894938f9804441b7d4c745 -26954,0x19ee7efe992b27b28c39afe0a84df8115db78011 -26779,0x1a00d8c8e98b43c926b1907d0ac6d230bf631ca3 -26711,0x1b597bfbaa7cb065b1a361114fc095e15ddc5247 -26723,0x1dab8a114abf1d9f3460ad1fd7dce33691e047e5 -27066,0x1e3fab21fb741455463d846d85d0dc9b6de39e14 -26882,0x1fd29b4c3bc99b262f06003e4a89db42b35908f1 -26730,0x20b97f5dc4641259101485a4d77af4dc9230f3ca -26740,0x20b99a4d64bb54e8ba554bce9a433fe272c18163 -26915,0x218bbeb243498607d362570fe89fe20aad67a845 -26950,0x2202b20ef7769a9babb446be91f9cb38d0b13a12 -27075,0x2387f224a48add4b7017febc12c493328b0f8fd7 -26813,0x23a58349510c3e1ad1d9cdd195c76e27d435cbdd -26897,0x23bf9e6a2ef2952e6a2944af26007c0b5ec84c33 -26708,0x2474680a3475ede148b5270f7736cae6d63c06d5 -26815,0x25466f3b08b5d6ef067ef3f7cbcd79fb5f251536 -27013,0x25aee138e0c5cc1d928fce94d2f7d809c91a7eb9 -26997,0x267d170956179b242d472c3223a6c68d8ba33633 -26834,0x2921e0d8b339a71bfc10441665e297ee6f0c6b11 -26975,0x295922d988800dc6dd1de3c3083e7e6bda172ed7 -26996,0x298e266a08d0d742fcfe82cf8e82f2795f6769ea -26746,0x2a34d1fa6860e721e19b51aa87fe62119b66dc6c -26743,0x2a488eb37de95c7b42b44172f56e17453cd9c0c3 -26903,0x2b1be43b733d2c232276524830d4cccaac8ceab3 -27000,0x2b201d3d131a721631c04aaa2b046304971d236f -26856,0x2b3779ac26dddce403a71e818e4c7491a495b195 -27027,0x2bd6b94bb5ca5d63be943342560322efda4fe590 -26762,0x2c0eb424d046b42e83f87e48e7a38530aca2dae6 -26945,0x2d7012bf4e7f2bb8f3e7383735407760ae65eeaa -26854,0x2edfe5e371a8164dc1b45a53b6bbfcf5b2ac3c95 -26959,0x2fac63dbe4bd4f3d118909193ff2563da027eb88 -26872,0x30855d1129f398f71636d4b94450b71457057724 -26972,0x311f271da38116b034a3e7812867bf185c2dbdf5 -26907,0x31b1256e5a9778001306bc15babaa3fd3bd3a2c9 -26773,0x32f9b6969e91fcbf9ee7c077272cc4e0145ed439 -27001,0x336a6cf0d85932611ca5c0c7e4addc60989692cd -27077,0x3621f9aac18a42d2343270849611ea09aaea3e54 -27073,0x366138a319d57087d323ede5f3258bad16cc58cf -26805,0x3686e19c10f4e2fd979ec1d4a94de3a77cd60486 -26930,0x36a7c619398597de9958369ca08702269652dae7 -26838,0x37166a2a6967e2d430533401e7600685471201d8 -27056,0x3727181ed49576bb5e00cc04c788e98c563cc649 -26847,0x37776d08a5121a9cec7c7e4c433fe9344eb84c49 -26978,0x377bef5c8356aa71f416f5f1c631fb6600439fd1 -26756,0x381e4726e66339b8973329e760071c74c917df59 -26831,0x3824825faf0ac44a44de40fe95ef4cdc17d234d9 -26998,0x387cbe87edd46fc5042042ce5c9683bf36eda65e -26923,0x3988513793bce39f0167064a9f7fc3617faf35ab -27058,0x3995bb5924341462cbd88d641c11e214da0191d0 -26902,0x3aa752a86d430800b0cfd64e4a619cb70e150f58 -26905,0x3b29629a94a8606571e206bedcb98f924f69ba03 -26840,0x3bcdc30ec51d9c65a27e8daed8a23642528db096 -26843,0x3c6832d8a25a7958776fb9cdf4944fcb94f29d9e -26842,0x3ca225df55fa883d9c6ca678743a70f9d7492234 -27097,0x3ccd75acf038b6d0ffb097b516cc246bd8fb261e -26826,0x3ce8f07645ff6c2be03b393e11663170a1c76e4b -26871,0x3dd626be44c220cac45c677e9eb0d5d103308b49 -26790,0x3e42187e7007708878e221574144162534cf7885 -27015,0x3eb5e0179fb199bbed5d6c5695fc967893c6155f -27072,0x3f336358ddd7876dca6aff7f957c7e34f437cdd2 -26710,0x406dc0234f39ab81026709b64b771490356b1ffa -26782,0x40b3d5094db253601f98a8bbcd53600ab2c6f719 -26992,0x40e85ae7f475ee4ffeb16bfd21a61491283fca82 -26951,0x416e9390e6972c97ea273419331ecbaa4bead6ab -27026,0x438286d83b20de7aba859e68af55d5705fd9c5e4 -26828,0x448a61e4977780ac83da5f6a5e62e4cbcb59106f -26956,0x44f1dc2062b60bc23e87e30c914b9b24f8b54eee -26928,0x454a70b8d766ef1f8d6cf848aff6e4ea4d5d6425 -26758,0x462f144689140fd4ad95b704a731876a23fff54a -27085,0x46bb6dd6ff067cee6fca21ab607140b411e55e57 -26984,0x46cdf214f7bc1d6e464ada134d6f778acbfa61b9 -26844,0x47048b3f30d22ac3f08ba08a750d751d87c73e1a -26895,0x476ee8b22ab667720ae5df709f87130bba44a06c -27034,0x4ab5bca031ae92bf05decfffd9013426b495056a -27079,0x4b6946647ac257f44c6b8e44c7521bca167df9ed -26942,0x4bb4dfd14a98de99b1821013bb826b2c29a912e2 -26987,0x4ca53a6d1881c1ca65f30e03d2fdfb37f50d8487 -26715,0x4cfbacac15f15537daa314c18881f6ec8f0524d9 -27082,0x4dbb0bef7ed0f25effb28a342a7ae3cd73c414d5 -26878,0x4e6cb38ae03b567c12c09cfaf495e6fba77215b5 -26870,0x4eb362c3bc5ca28429ae7e2ea27248b7ebdb7c4f -26979,0x4eb600515b58b5dce1f0929a80c23b6bef875d4d -26771,0x4ef18713a279d18db7725291609d4ebe8825fd51 -27068,0x4f0e3e0d54935ccd983d940717def6a38ea689bf -27037,0x4f53fa21040fd7a1f4d62de93f1f1975b0435a24 -26985,0x50f51c33755dd75f20ee281cf0ebf19b8cc063c3 -26734,0x517ea1f91086f0186278e42839a3a1d6bcc7f8a5 -26709,0x51940187e3071513ec1334d4d04eac08a63a14bb -26789,0x5212797d402c11fff8f19c4bf7eb311a122521d9 -26728,0x525f9584cf000c7645896e5774bec11b4ad5877d -26731,0x526fc7a4810ed1deaf7c6f8fd0cbc40bdad4219c -26896,0x5298aaa21a50dbf21e3c82197857fbe84821ead3 -26890,0x529e961c85b066dd6c563ebb6e65d659c172441e -26839,0x531a6730045136a14ca481adf413af40f01eb5c8 -26999,0x53266f7747c6cb7272b0dd255712d54086821215 -26806,0x53318268a7b877e1ceb185d334d36ef88e3b3e63 -26717,0x5383082ae5b1afb5bcc98fff3036b9b863abaca8 -27047,0x5475f9c6156c7d231a23afc2b7ee97894ed6c228 -26926,0x54e90ec191569bc10111df85c3f8acc77c068890 -27098,0x555c9deebc1926902cc7eb4cb2c8f00d240fac03 -26868,0x556a9285271bf33eb0f9ef5d6d28d7cc498455cc -26991,0x559c412b89b162428c6edd7e12d50d3e8132221e -26967,0x55c0fa326145f21a8db86b1023dc6982b7d3863c -26724,0x55eb5800fa618f5b7b3977edc1fcf117eeb6a689 -26968,0x5686747cd9f8e01165a93c39941f00ba39fbc54e -26995,0x56dda05845f4f334e69d514e12b27b3ae24eab80 -26920,0x56e2cfedcbdc4e0341076fe32d5bf440032ff9a5 -27083,0x579948c194aca99ef7826ee16a0643d475ab3798 -27006,0x58eb4351f0ad34e7b3c2f1bf3a3ec1244032d7d3 -26989,0x597b8f6d2f9e0c35771795f0b1266cb110caffc9 -27024,0x5a2637803f25c3b6c10430f21819bd5f83d89b4e -26884,0x5b59a43b164089efd6c62cd1716d30ec25c55d1a -27064,0x5d96b75fdef89eb7d823276a43f353692630294e -26963,0x5e50c5b25beb060787915744f3d7720a967950fa -26899,0x5e569f06d2ae28d66291cf099ac554b1ddde9fa6 -27014,0x5f3f12b5c5b3a02bcf640c0fe28a87e31614ac2e -26800,0x5f5187478205770337fcbef314c201b2286e2b2d -26966,0x5f99826ca44a00e3a6a56ff058ba4a442c51a6af -27065,0x60158efa98fe2c305fe54837f4d01e88a5de113f -26767,0x60e4c65a394e9b525dd1e1dc94e9741749d6afd7 -26934,0x619c3902bd5285d961e37ff8cd3f00306ae40708 -26841,0x6212ffdde8f24e525264df10a7ffc6d017c8424a -26943,0x6243e9db93f405d81ba7b56f48a3c80bbb5a2c4b -26900,0x637cbf313513d8b63b2faa31c2254fceec662472 -26784,0x6398acdb72c41545a13a211e566c762f84ef530c -26982,0x63e95c6766e2b7859095a0552a63492d946b0063 -27002,0x643b31e7062e2b39a3235473b2a9e832f144465a -27057,0x645f8ec14d0940efd7272ba478c12968e8eb9937 -26925,0x650f42efa6e90162874f28eeb20ef5e4b9dd8c35 -26814,0x651362a2d3b5af4c98e58e0cfd4f78114f54ccb3 -26875,0x66b1880cc598ef3f93bb95e25580c2e761e1a5c5 -26817,0x66f50e06e1099aba193b8e0f33751dc338694573 -27039,0x68ac5ae47341f7ea3c05512b0df44f100bb96d28 -26829,0x6900ab5969e4e6c6be69af22182aa398c5e5292b -26848,0x690783db69fe92c8027cb010f02b124340a216a1 -26716,0x69217c630d4a88071eff2d67ddec5b2c7ba3fa2f -26850,0x6a136e9b41b4f0a80a083ad0953820af528c1fa6 -26803,0x6a1532428a7f434f202a1d6a7ee9aa290f5d4940 -26889,0x6bd05e330a3a2865f90c9863de392e31fd230bd3 -26809,0x6be04743a8894a9f858b0aa41229b0cdfbd64541 -26719,0x6c11da29d282520cc185280b7262bd3612ead548 -26877,0x6ca177b0ee3b1d352e6a9af62a403ffd7e2013d1 -26777,0x6cb521d672ecd842cbd8c2a27a8337e1cdad14b7 -26833,0x6d7544cd055e9c0e1dc3125526b3ed5129848d62 -26876,0x6da9139009c7a77b16a6acbe4ebab257b6798570 -26862,0x6ddfb6e2053e57c6f44b511e3f59f3f94936a9aa -26768,0x6de2a40897a0ae21aff9b1d0d7688ba7d56785ee -27040,0x6de4c78b94abab75532c8da42473823be924841b -27035,0x6e946e7b06195bfc027fbdfcee5ee66a80902d84 -26832,0x6eb6c78bfb44689673ee09a3fc6b82718660ab7f -27021,0x6f2eb15dc0dbf79c966ada52a4a737321efb5704 -27094,0x700e62f15b404287c93ff86cd7c9df283e123471 -27018,0x70547e8185628e0fbf74f78be65e725b8c914490 -26852,0x70eb5780642b6aaed36f40f0ac9390e8c3873c1d -26836,0x739285224e455e6568fd36f9147e163abb0e1f53 -26906,0x7552dbd8c810375bd9d4590c5bf48770ac160840 -26914,0x75783716eab35f41b53112c77576bf4fc638a837 -26867,0x75a199882fcf49b3727d787ed5d4a166d08f92ec -26765,0x75fd3271fd57fab61f72c872f79c94ecbacbc610 -26774,0x764032d76da5c0308eaaca587b97ad044925e6fc -26859,0x76e3f0b91d1c4e119480bc6b5df4dd91fcc815fd -26791,0x770351f796a448f88ff94638a462ecd1599b3636 -26893,0x7746333466c0d6c9099ff28590a9deb3d52bf0d0 -26818,0x7779605c020db38eeea13167a77c1d2b97eaac06 -26857,0x77b07f67d8d7a8a3d334a8c3fdd79781da592a96 -26819,0x794bc23e04aa3d3d7403e60e177a76dc5198e344 -26748,0x79e535ea97134b81620543e2878d5e783f4e9df4 -26894,0x7a8e980f4fc0a2e9410a2a2bfb81a6199400b6e1 -26939,0x7b4b7f88823a75ec7ac9248c810af77d558a4bf5 -26718,0x7c86a5d26184fb06c7a266f4a5e9269b868947e8 -26874,0x7cae64704a69c4f0ce7d6e8604cfe3cf113f6152 -26754,0x7cec5593370f8af7805a6f54d4ffc1dc8acacd43 -27012,0x7d64401f338a00f59a7eef892e71f5fb0ab519c1 -27051,0x7da191fc84e4d0bdba96365226bf22fdc22a1741 -26749,0x7e406b4762901035e241fff6caaa73e721d46539 -27003,0x7f1a04397236dae726fe63f322e7f3c08ab53cf8 -26938,0x809c11464878db56dfc4ea4dc8de5bd1e1f8c1cb -27074,0x81d0faa774b3e70e7910cd4ced7383bb4ed85215 -26960,0x81f3cc4390d67a89615b35d74d5fc6a9b1297536 -26792,0x836be52391bf5ae7d0208b5c7c0f8b52d1b02c1f -27062,0x83eeb07021f7c1942a7bb9000374b4602c780c43 -27061,0x8613f1db0869b7fceb43d6c6954183cada9b335c -26798,0x8624aeecde9d2f3a6f5d16097f705b2d81b052f2 -26858,0x86c5007eea22bf48be5ba52bfb1ab97bb4633ebb -27005,0x86f31e1bc6c68bae1df43e525600ca9f734e5dec -26993,0x8711fc4253005da8eef748419358ce28c8ed23cf -27020,0x8711fda4252113f0d883e878c6a9dbad76cf7d4c -26751,0x872a4d77a5ce7c73e6c9b144193f4a92859f2602 -26727,0x87c12595ebeea31acc59a549f5a98cb8c9c6d970 -26795,0x882acf2169786e7dfc2e5c341d23e1c5707fa851 -26846,0x8a4f3aa0a44902dee9dec305676ef4b02bbaf38c -26983,0x8bb6369a97c2af1bbdb0606aa741ed2759cf2490 -27049,0x8c35b8f6bd40ef9db06d460baaf255b92d9c7d7d -26853,0x8c75df7ced9ed00e171d532881e295460e16a911 -26941,0x8d44dad64269dbbeea31260cefe5f1c8333d7070 -26725,0x8d5473f91d04f55d0b288032d603163e092c6098 -27078,0x8d97d5a2e9c0b91282ab66fac84775f9f48c07e8 -27069,0x8e5af2cb2d256b818e4929778c3c60da829f495c -26949,0x8e878069b1290054ced228e73bcb0a0f2cfbadb4 -26759,0x8e9647ea12931c0ecab3c0cb044168e02a70b72f -26764,0x8efb229bef8e2bdeade7286a751481e3657de1d2 -27030,0x8f4491ed76be9680ec801cbc070fa12067b5071a -26769,0x8f77b1dbebd2f09abffd660532f853ab72f09890 -26755,0x90355484ad6c7994f28275675e386531519c5f4b -27076,0x91322822c067a2a85cff1f40fa85ea099a1e24bd -27081,0x920f37bb1964b72ea99b5036cbfbd734f7c094b1 -27022,0x931b666ee41e0e9cf9b9adc09924191beb9c125b -26824,0x932c54abf6d1e25d969182306991a63ca2642b97 -27036,0x93453f96d08219295ac480ac37149f17842c67c8 -26921,0x9717af87d119b34ebc0ae0b8aa57c08d4d575a33 -26737,0x9882b5f45a427810f0212db128a27058eba44381 -26732,0x98f3a72d42f194fbc2357056cb4b8c9fc50b0bf8 -26753,0x992ef3c97e9f79e3894392408b0bd4f91caa9612 -26947,0x99d5da6d6611f5211a9ee65abe5634ffd96ae9b3 -27088,0x9a6d482c81f2d7b520fa52aac3b7614af7000caf -26721,0x9c039178b92edfd86d42633b1e35cc2ced07acc8 -26917,0x9c2525c2ed4bad90da42a7c418d6f43cf24cab76 -27071,0x9caed0553eb7dbd1d33077f634f0773f6a3809df -26855,0x9cdf40b7bfff7e3db56b416285a337315f1e2301 -26761,0x9ce9bd76eb2b798d5cbc4c125ee6d79e22f2f6a2 -27041,0x9d124e66401880d7b18a3a4b66d4de9cb030215f -26879,0x9ed4e8c2b3256d1a896a6a6c771225b948191bed -26810,0x9ee66a6ae209630dd8d3640c3a3fb2ca54735285 -26946,0x9fee88a18479bf7f0d41da03819538aa7a617730 -26712,0xa03464668b01c27ee3015cd66795247e57c70b2d -26780,0xa04379eb9d3b9ad9a5d716f6f598008490a366b0 -27032,0xa1ab057c535c3a0fcb4d0959b9059196870185ec -26885,0xa1e6d475bd67da878fe6e4a68064171714f55856 -26973,0xa2822a8d9aca3d56a2ed2be70f0c11136875289c -26970,0xa3344e41a3f191ac5f125a55330f1b52b68e4b1b -26786,0xa382e11ad946ea5b0641c4a41bcc7d95a3f563cb -26936,0xa3a54ba1572bcc1468789c793be2d46f38cf11dc -26785,0xa3f0b83fad64dc8ac9809fe9f9d3239aeb235dd7 -27019,0xa4acf5140a3080be2f9dad3065545c792eaa38c3 -26738,0xa5a5f8f8ddf2e1ed0f75c470be6f3a8d48c6821f -26961,0xa66066ce365f0685166346645c33f81a04c54cd0 -26901,0xa8266193f0d9671e0a0fcb372f877520f77131dc -26977,0xa92c4b0d2314199e7e0e2e7ece7b8a6130b1cfb4 -26988,0xa992714614ce49fe445bf0e13761492bc5c8c92a -26886,0xa9b34960cf856d4dd98f9ec9becca67d019f1a21 -26940,0xa9f912c1db1b844fd96192ac3b496e9d8f445bc9 -26851,0xadbdec92b9daa3fdef58c4bc6ab887dba9ecb351 -26911,0xae2a899e922af3c46e7305791e20a69d6ad2eeaa -26837,0xae548a79e1f8f8f3218480240c9947b953ee913e -26811,0xae8de3937946b928267695692d2dd11292372f55 -26888,0xaec67c1edb1e0859a2b65ba92443546eb1c6397b -26964,0xb016e83d81e6073de6cbb06bcb2299e1ad071e11 -27092,0xb059efda8120221ee88a4d40128324f4b5c1dcdc -26912,0xb182f49a5543f885488f7bfb26b00b9f925cc967 -26965,0xb1d851934bac947e8ccb383b6246a4d424b8e5ca -26927,0xb20ed5aca7ecac6559894beb719668fdb5c7cae9 -26816,0xb26c905ebee589bcd3365ba928a9d29c872ae4a3 -26804,0xb27f3f87b962f07188612e17d2721df7f23d7e71 -26787,0xb48a842f447eedc353f9fa3b290c8351b9410305 -27031,0xb56c5752f83c7427e17128f39a157cb3d51d7156 -26793,0xb56e58e48317ee3e8af5cb9c26f80f494586e7f4 -26937,0xb5b5f2fec05c1e4639f0aeeb8d2b5d9b670cef25 -26980,0xb687162d66280074bf2784902b57f3f1212297e5 -27087,0xb78ce931ffdb497888d49a4b3aa3283e714c9e6c -27044,0xb87613327712baae4490876eff657f310252105f -26910,0xba7522351a4ab86209444761cd80ab74730888ec -27048,0xbb45a44e2db39a86723134cb3789a9ba2aa9e9ba -26976,0xbbe853fc53bd27063e4ca2de72cc644236ccb078 -26918,0xbc87becd9b2aed3e282d352b94b80045946cf4b9 -27084,0xbc95cfad5ed6152c98cc0b7d63b158234afa520f -27008,0xbd0663b0bbc79b8359be0817dd156d61a15a51f6 -27093,0xbdd216b4c1428a38a3e1c7087517d2a5c3bf5276 -27009,0xbea170c49036d7917cda7511b57cafd41efedc01 -26781,0xbefa33261ce1df58f47e274165cf85d504336421 -26808,0xc11f4c869a12214084f871abc3656c1b2537c98e -26797,0xc131b096c9cbc7390121043d0366bc9b7be1b418 -26736,0xc2736666de3154c7db1d630fdf30bff3d4ca4dc0 -26953,0xc295faa7b3cb6c70a9d52ec07ed7fe2b656d823e -27010,0xc44736e84da10cb3b1d93c2b102f690ac75c1413 -27052,0xc50ce0fedc0da16f4283fd3797ad28680e06cc4c -26957,0xc5e24f77f7da75ef67610ae624f9edc0cccc7816 -26913,0xc7b547cd7c6f911472c688a2b15fffb24b5fb00c -27060,0xc7c3050195395a3f0212a060b12a3bd2c38b97d0 -26820,0xcaf4eacb42ba47cda67abcf907d4faec60b01c51 -26823,0xcb30348311503d66c1f2c6d4dbb8ad6402f30cfa -26775,0xccf8730dcf43ef4284e029f3c0f0006e1b8eae87 -26929,0xcd9ee1034d105f5a013805529caf06d7117e92d4 -27043,0xce17bbeee29159f08a61a89259c2e9adeda3ec70 -27045,0xce8c0efb9c3ab3c5e12e57ecad88a4360cc21dce -26866,0xcec6e74e58a1a99d2a860d05f5ad02fe92ff9ae3 -26752,0xcf719eeae8b7b66d2eb12809323d0725b4e8dad8 -26994,0xd0d2eb0f55f626ac8ed144f9fe824e416f7e743d -27089,0xd1898a60cc897f9ec74e0d843bb082f979c02623 -27046,0xd19d36d3a235a33505b8fef6f03f829a661ffbbd -26948,0xd2dea6a9961dfa08d15a9865ed00b5a9749cf739 -26974,0xd328d6f4d734f4ab74dfe462f767d5db4ad5c111 -26863,0xd4ba913ee85b2bde3b3809c0935c6dd4e23016a6 -26807,0xd542b7765b8f1b991520ddf13cfc188472299228 -26742,0xd5dc951d64a0f81b31b851ac62944c6eac55e0ae -26745,0xd8bce52fab386a0462c7a7ad1b8201e4fb72f519 -26714,0xda9d1d035cadad6b439435a189e2b80573a521b0 -27017,0xdb1b1f1a261298003992add45ae77a13b05f0dae -26880,0xdceae50e65e8329e3ed271868f563934ca561cd6 -26916,0xdd0aded975b1a687b1900eed6071ec6e7eccbe28 -26952,0xdf1be9157bace4ce3037241472e1fa4160ef7afb -26802,0xdf22f3975ea6897716e4b5fa56e310ef0534e5f8 -27059,0xdf584c43d24034806eceb4de21c628347df3e837 -27029,0xe02cd1eeb72388634da7fde8143367b7fa544be7 -26788,0xe10cfffbe8d6ed4ee9a6165b1a9c11ca96e07b1b -26827,0xe21a0c6acbd576cec1af241b6ba04c73d4533580 -27095,0xe2f9b946c4dcc6ebd1e00a8791e1570e4e6d74d9 -26919,0xe47a5d442ef0309a0767e974b6f673d930fb8698 -26932,0xe5415e054fff990ae83affe3b6301db93864fadc -26812,0xe59c6bd081e370e776f7f4621ec57d8ad63131e5 -26924,0xe5cd435b19fb9b5cda5cceccbee62fe5b3a6b27f -26741,0xe6fb1ad7cde0688866f9ab1f8dd7ae38b57c1e54 -26869,0xe73bb5252f821bde9c23e37a5f0c9e96fa0ee510 -27096,0xe7608020bf2b2e58e512f0a261e768932f57755b -27070,0xe7f9987d3d6680e94febd0309623cf0734909c9d -27038,0xea404e28d0a1f966066a9e3585b9ed5f230bbdd9 -26720,0xeae744563eb715305231193730ec829cc7dd8a95 -27016,0xeb13d8f0ec600f4ae0f990464de3aa64d159ce9b -26747,0xebe7395d6d32fecc119249c0f9a453ec90d8163e -26887,0xec6933496c4a3c339fafa0bfc9cc6f2b46833f2e -26713,0xecaf977a599cd94c71e7292ba0c9cea9ea227d2a -27023,0xecb98467e199442629a4735189c3a5117e1b8f40 -26891,0xed19a25bfa14a41527ec692826d4124b480f498e -26892,0xee29bd08591f569d1c249653efdbeda2a21115bf -26898,0xef4b78348eab4dfd776e1bffe9c31cbd76ad24d2 -27025,0xef5e55637a40729f93f5eea595164ff41e63d84a -26821,0xefd9c20fc3022ba58a0bf21824403bb7d1ee98d8 -26865,0xefefca1f7c5ac491d6fe720e6d6725255290d621 -26722,0xf00babe708694ac1ade6e89f97b4449a4fb0afc8 -27063,0xf0da0ed7ddf6d0ed9f2982bd1f2aafcc66e0c4f7 -26760,0xf165ca3d75120d817b7428eef8c39ea5cb33b612 -27055,0xf1b3cd2fc6f487257df637b78ed7369122eb3cc2 -26830,0xf2d2a22591e7c8fd6a7ec29a1e557e10e3bb2b9c -27007,0xf333dcc032dd81dfc92495acb8100bcf8b54385a -26757,0xf37ebcf7281b5b1a262bd3f552641192e7bd50d9 -27050,0xf41194adc81e30cd57721b815713f2307565950f -26744,0xf42ac62b0b86c6f419695fb885a15cfc28d3cb92 -27080,0xf51ba8e32bbe73b753e740bedcfa771c408682cd -26908,0xf6a897bd9f7e9bbf938dc025cb18066662404d45 -26971,0xf781891ec2f2465e1e3d92328f3745559ecabfc7 -26935,0xf7a812331c331df5eb63fa175870c3f9027064f5 -26860,0xf7bd9ece384aa0aa0bd9808a218be720f1717839 -26778,0xf826cffc74b9b95245969ba3206f98e61e792e5f -26835,0xf83f1762a4a6c30234692d52c48cc5f9d827f0de -26739,0xf8c62bd5f2fef9e1a329c197f32e77ad6866b022 -26990,0xf8ed6aee2a036ae2b326e9fb09d9cff40745fe40 -27090,0xf95f44af18c3462884a798c779b390caedc91ebd -27028,0xf9b12e42964f3899ddd485c517dd90572b801163 -26861,0xfb4a499126b75568d260e11a2b66e06c5a91eded -26933,0xfbfc15807dd82f0bde3ede7993c6710cebf20087 -26825,0xfc1856bd1fa3762a5fd180e4d01b4549f116ec0d -27011,0xfd08e400397bec8f9f87db9af8712156dda4ee6c -26845,0xfdb949f5ff93d5f396cbf855f75e9f69b667458f -26849,0xfdfde8359e9176a1e62250801a83f9919cb79601 -26750,0xfe231f6b46d84f1dd046b4e4b5562a6433f3e914 -27053,0xfedaac9fbfbb352fc241855f1476be1a7d362217 -26794,0xff77c67a654d49ef81a4bd75af69c7cc19e4c3d9 -26763,0xff9a741e9481a9b542aa20d855126a8691bc006d -27280,0x001e3ba199b4ff4b5b6e97acd96dafc0e2e4156e -27300,0x004a476b5b76738e34c86c7144554b9d34402f13 -27283,0x0100fbf414071977b19fc38e6fc7c32fe444f5c9 -27249,0x05d4e2ed7216a204e5fb4e3f5187ecfaa5ef3ef7 -27299,0x070a5c8a99002f50c18b52b90e938bc477611b16 -27242,0x0a31527a8de2ee97bbd8cce14db8e8826a0b6c4f -27257,0x0cc51c9786f3777a6d50961cebb2bb6e69ec5e07 -27253,0x0f9cb53ebe405d49a0bbdbd291a65ff571bc83e1 -27281,0x114c4042b11a2b16f58fe1bfe847589a122f678a -27261,0x12f407340697ae0b177546e535b91a5be021fbf9 -27258,0x1337bedc9d22ecbe766df105c9623922a27963ec -27252,0x13fc868d6571b8a9371e70c4e404342deec0f2ac -27237,0x167e42a1c7ab4be03764a2222aac57f5f6754411 -27244,0x16a7da911a4dd1d83f3ff066fe28f3c792c50d90 -27248,0x1aef73d49dedc4b1778d0706583995958dc862e6 -27267,0x1e7b1bd0490dde12f6e3d09766beb05552afe27a -27240,0x22d710931f01c1681ca1570ff016ed42eb7b7c2a -27290,0x2fddedf2d842f23da2b81b9144e75cecb691bf19 -27289,0x35796dac54f144dfbad1441ec7c32313a7c29f39 -27297,0x3c0a405e914337139992625d5100ea141a9c4d11 -27268,0x4244eb811d6e0ef302326675207a95113db4e1f8 -27264,0x445fe580ef8d70ff569ab36e80c647af338db351 -27263,0x44c7a35ecb0cf37a3d6fb38b4085d9328658e01d -27287,0x4f3e8f405cf5afc05d68142f3783bdfe13811522 -27285,0x50e09ee7080b32aef3e92346891dd2dd389b5faf -27262,0x54e8a25d0ac0e4945b697c80b8372445fea17a62 -27278,0x62e0031b7f87da1fe3a54d13c51c2b535e5c921f -27273,0x64ed8cff5ad3daeb217abe03a00ff2d90b86456b -27291,0x65a0b01756e837e6670634816e4f5b3a3ff21107 -27275,0x6600e98b71dabfd4a8cac03b302b0189adb86afb -27255,0x66b5792ed50a2a7405ea75c4b6b1913ef4e46661 -27266,0x6d65b498cb23deaba52db31c93da9bffb340fb8f -27272,0x78cf256256c8089d68cde634cf7cdefb39286470 -27254,0x78d0fc2b9d5ae65512db242e424a9c683f18c243 -27270,0x7da64233fefb352f8f501b357c018158ed8aa455 -27243,0x7f90122bf0700f9e7e1f688fe926940e8839f353 -27246,0x8474ddbe98f5aa3179b3b3f5942d724afcdec9f6 -27250,0x8664742f18f94065ed02f39a56bc55750dcc9e56 -27271,0x877a7aaee9edc34911a2442fb814ece2679cf3fd -27241,0x883feb1b9862e0c8559d26f53bf19264c5e0839f -27276,0x89287c32c2cac1c76227f6d300b2dbbab6b75c08 -27265,0x98b4029cabef7fd525a36b0bf8555ec1d42ec0b6 -27286,0x9cf512116fb29ec1dd3798e6ea9a7cd9d18bbed1 -27293,0x9fae78c4bbb649deb7b2295ddb8a03ada7eb660f -27251,0xadf698e4d8df08b3e2c79682891636ef00f6e205 -27274,0xb7de33440b7171159a9718cbe748086cecdd9685 -27277,0xb90b9b1f91a01ea22a182cd84c1e22222e39b415 -27279,0xba28feb4b6a6b81e3f26f08b83a19e715c4294fd -27260,0xbd2d41dbbc5a13b1c883f6c62b14924aeaef4483 -27239,0xbf7e49483881c76487b0989cd7d9a8239b20ca41 -27288,0xc1c49622b63b961ce1d352ecb7d8261ab5556695 -27284,0xc2b1df84112619d190193e48148000e3990bf627 -27259,0xc5cfada84e902ad92dd40194f0883ad49639b023 -27269,0xd166eedf272b860e991d331b71041799379185d5 -27295,0xe35a879e5efb4f1bb7f70dcf3250f2e19f096bd8 -27296,0xe5de15a9c9bbedb4f5ec13b131e61245f2983a69 -27256,0xe8269b33e47761f552e1a3070119560d5fa8bbd6 -27247,0xeeb3ddbcc4174e0b3fd1c13ad462b95d11ef42c3 -27292,0xefde221f306152971d8e9f181bfe998447975810 -27238,0xf6bdc2619ffda72c537cd9605e0a274dc48cb1c9 -27236,0xfce359115dfe1533a2458650123f86c454bc0213 -27863,0x02230cd940f345e17de344dc1e2672b3a48f9fb9 -27633,0x02947592ccd44e529502a5ea33f4c44fa029b2f8 -27825,0x02de5ce7748429723535a5ca9bdaeaaf99c374da -27691,0x035e744c6ef9fe6b1d54a5f6f4ede13315e3f288 -27875,0x048edb9066be4b028d8286f5e58775b528cce395 -27892,0x063f783098afbe8e3f75e9f9cefe6f2f38f55774 -27773,0x0719091763669343981fc3e6dfd7058333701257 -27887,0x0749cab92e3007b8c27d2bd620b45e38640d4d9e -27810,0x07df53185b19b4db007177c66660e9a29b10b1bb -27630,0x0917faa8d5625deac55633b982f5227802d99051 -27734,0x09a3472dcd9a2ecfd823b1d99193d580419f08cb -27605,0x09c83264fce0be141632e4e45a43d0e1c6871226 -27797,0x09fbf8bc38d433f1ea3fc5b6d576b9c8860c3efc -27718,0x0a475f5923e2e9f108286aa68734c8df0870a5c0 -27818,0x0a9f0a078da0c485e7897e60dcb85143c6e575b5 -27760,0x0aae8c2afec95ace8fb49a49a2e3f319b83f7864 -27823,0x0aba4f13b4cdcd2374a465b86746bdc5162ce057 -27662,0x0bfdc9ca8e8b30aecafa7bdfa1429637170dea48 -27709,0x0dee8072d4b4f08d015019e18431137dd7006c15 -27866,0x0e4e173e807d7e990a97bed88da5491620e7fdc8 -27616,0x0f5e7ffd4bd5fe3da3547ae1d1b5aead4706d686 -27896,0x11439987f214f911465584299d6474fe81083519 -27722,0x12735d3579c13cfac90ba4dfb1ccca9a7a677bb4 -27714,0x13431d6b8e0a62e544c36119f981616b2511616f -27635,0x13b5bd7e85a3fbef405d6fad4fc00a892bf4b242 -27824,0x141fe5aebb958ff5eea3bad76e4e06835020c49e -27808,0x146118971b387ea4aaa197828a2d99cc34ea3f83 -27776,0x149e74f93ca0b03feb46fa5885f57e6e213e636e -27595,0x15043f8077e6cb4875ffac00804aa852669343c0 -27910,0x15237f1a154c409c43ba6726e08544feeb44effd -27680,0x15cd0762b917e36577243a815fbf7467709065b5 -27900,0x1603f08cac6519b17163b3f55de6a2c7eac0adb9 -27732,0x16e73dc1fc3e4b37948f29cf879fb0fc1fc45adb -27641,0x17aff89bf88b4eb56a1bcb256ff49fa1910e8410 -27748,0x18090ec0248b4a197032edf1ce462471511519ea -27841,0x1832e7aaae135940b508b6365d8a1b8ecac8850a -27678,0x193726b2733130ee63dffb02c5475785d527e543 -27685,0x199a5c14159a76f822a766565904e62b0a710d03 -27754,0x1a391cf29f25e1188687153565b9e056b69fecdc -27673,0x1ae5c83911fa698ce3ef709f6ee2728f0cc9f7ca -27626,0x1b9d2e49a106a88cc5385654b50f741d786da95a -27707,0x1bf00e36afe8f8459cf0d429c960c544cd34d180 -27795,0x1c056f5d833d1f804874d27ac9ac6129f71037d7 -27646,0x1d7fa19362838d0cb692cbd1c72c82df065e299c -27730,0x1e95a00de45089d1c262f43aa2ff8a0e3bc90f37 -27923,0x1eafa484b87ec5462b4a72394f6cfa08919b7506 -27739,0x1eb8cf50246d4043f583f0862548caf947fda262 -27821,0x1f513585d8bb1f994b37f2aaab3f8499e52ca534 -27698,0x204d31bc2efe956ebd3efc8208febb5eea6c8719 -27737,0x20f72224b5809308263306443b832915eccabd2f -27878,0x2117f0a4fae1d664b30a91b06e52b6ec20cf94c3 -27779,0x237d5a97d98469dbef7d755e2f9d3b0389402b05 -27671,0x245bc556ff2da5e14cad44a882812ad95d8c6640 -27877,0x278355f40f920adae0dfddfa1de9a958b1a003a7 -27586,0x29c7f5e1f4a0b4ed9312091cbd8f06a5f8851804 -27870,0x29eb75ec7c01ce0a1747c85c2386f4c7b2201b36 -27667,0x2b571e5f3d64b05ae91e6a419442cc4bd9eb248f -27819,0x2c3439a697ebfba4005e4c5ebde5602aedbfd3b4 -27600,0x2dbd45dcfd5bbd97cc251fec32eb5658b43c332e -27912,0x2e99fe9c128fb22557762080ef8c1cafbe4718be -27777,0x3027aa2eef60f4f1d023cdc35d0d79ec3b234ec1 -27865,0x314efb237ab48de2adc3aa9b130aad84d0781612 -27880,0x319b3142c44e9c89767bae8d3c85469f7d04c687 -27860,0x31c86a6ef5eb6cf5a05f31c377cd6125c52fb5ab -27631,0x323968bfb6f8f9dfe379b9eb326c7fc5ecf28b4c -27721,0x33970ddcef5530b46800878c08f96d3c10335d03 -27740,0x3424a0bb930035fb78638129d14e214ab2a20a92 -27729,0x342fbf2b2f16ea4aece968953c3aaafa3e36ad85 -27803,0x353a2cf0de5704574d2e832d94c0f365bdee69fc -27746,0x35b52d46186d01ce0cd55849b7d05347b0bfd5ea -27582,0x367aa4bf0e50bf6578e0c87018a0172e07e2dc57 -27784,0x37203bfdc6665cde2b9f2e69f536e84f10e45504 -27858,0x37fc500795e22fce2485718ca68da43cf5527a6f -27907,0x3827acec957ef31ff6eaf6fd6b9150941d6b686f -27765,0x38500dde2e52a17efad45bd62d1b6164ccffc3d0 -27606,0x38866682ea08615810daf46e2fc117b5ea48cb8b -27881,0x3b5efbbee3fd3c8dcf66be676ebbd38ab1821cd4 -27660,0x3c44542d48f27601c4ad24212c2af99724b578e8 -27752,0x3c524732d02a06bd4fff64e9707bbfab38bad4b3 -27856,0x3ca517f794712ed12357761c600b502c8a5f8685 -27692,0x3d6c05397910740232b4301b5080a13038c38c50 -27705,0x3ece7b8fcb2d95fea4a7b745bebcb8ac9d5ffe03 -27792,0x3f2f0398e1b8275a4714f636f299c7bec003c22b -27743,0x3fe029572b44f47c2e5380cf01f9c4aab640dc61 -27674,0x417c9a790c1eb8b33244ab34f37028d606022cc8 -27769,0x41b9dae7fec8fa6c38e19adf408dbe0531ec5e08 -27898,0x41e9b3a7e9bcda14723deedae4d74991b4786a3e -27690,0x44f4e86bd50da8de493eb6b6c7fbb2a702d3c959 -27666,0x45ca3fbd84fe3ebb02bf327f03cd0347092d793a -27901,0x47c0a65718f60d2f46a0513159cd0fdf031903bb -27790,0x483ddbb9113aee97d9dae19b048a3edc9fc8ac72 -27915,0x4856eaebb50686b1ee9f1e95b5de45e56f274272 -27785,0x48b9bf8bfe8c25188ca9582296e407d9b6588cf1 -27694,0x495fdb2dbd62c80aaaa7afb471a42738dd9f503f -27670,0x49d0716e7eddac4de1b7a1b4fbc86808e77d2510 -27675,0x4c5f7982cb18693a64c70eadbd382bed3ed50df7 -27639,0x4cd366f2f7f79f2d6610fe287222acb977e2e71f -27883,0x4ce4fc833e18789684413b5ac0af70c5c8cbfadc -27850,0x4d0eb155c1c41e0a54db80ef96ed29e5b1c87b71 -27702,0x4d2819a87d4f2380b7e19d1637aec6adbb736d64 -27895,0x4e22a77990841fc30e6cdd0ab7cb5599f6908bb4 -27849,0x4fd19e11ac49f8c407fba4a92912377c17213900 -27684,0x50372154dd13921632a9c1cf0790dfe479adc453 -27926,0x50e67ee6cd0523351fbd621ad9425499ee7c98cf -27608,0x51064d7282b569934a54f59fdbcce516984652ae -27611,0x513473d9cbe685ca004fcbebfec7cb39f28dd4b2 -27847,0x5180f4781705dd7947c4fbd86fd652c405a68015 -27928,0x527a8a352933be482537938f8f1fd48b4af3799d -27619,0x550cca4fb4000ad4a6eb5c557ed9f7e3fb7afc54 -27645,0x5717c62e61a8a905db905c0b8cf4b16af97e8e57 -27591,0x57d2559df06c5c75749ed3a8a174f0049eac7b6d -27745,0x581f6e1c92cef989407afbd654417923e22a0177 -27801,0x5874d229c78ee4efd2acfd6874bd8e030a455f1e -27840,0x5a29c0b39d0eacf8b1c05d0fe0fa6c4149d37eda -27874,0x5a419dc3ac9f3dd1034c87da7435804bba15af54 -27806,0x5dac1d060f5915acd61003f476a89c9bb26cd9d6 -27834,0x5e0d5e2371aa4fee5afd30b2a626fac6c23e6a3f -27876,0x5e8e77b29f01f03c7e06fb6c89f553b6f7110fa2 -27733,0x60370a641ebb433fa750c2245928baa1faaf6927 -27844,0x6482529b0c53e0c65d906c606e7f8a3c97530bbe -27661,0x64f4507ac35782121c17dc7ef26e66f491fcb400 -27924,0x65c279a23f3b9f81d89bd47690482b2130340434 -27885,0x65e4c903a96c9604697cac21226a470f96f21cb9 -27815,0x66b65176b5e0dd0866641a4b66d882d013aa48c7 -27768,0x671bcdd3e7d65592eadb46cfe1aaf75940cea8c2 -27693,0x68c9d3f0c0589b0e82d79609d1a9facbcc29f431 -27699,0x6918b26df7a4fa1b2004ddf6c0feb5319160215a -27612,0x69867d448a34bf0f28f70c687a8b88fa82781561 -27805,0x6a6f74d05916171f20df2d098436dfac50635770 -27712,0x6aa096c8e6417cc6cdfab892a4dc89e97a0f7185 -27867,0x6b098e6e16e2c7f32de11aa67ee053010ea1a65e -27747,0x6cc814efe405ce7885d02a8d87eeb0dab4e8a8b0 -27814,0x6cc98bb833f8b7eb7fb26c97cb9a212a5b966bfd -27843,0x6d20b1f08b0289d87b0d1309c28681b6b4aaf3b9 -27794,0x6d5266f18932e48611b0df3abe7ae7437ffdc7a7 -27770,0x6f49edf421b13971d580d34f8954fbcec97d1cbf -27914,0x6fe09aa3f7a708400704fc76645c814592d9b1c9 -27788,0x70d860b904b9b5e5df72f938dbe22015cf3ede2e -27688,0x710b8935f664b767eddca1b058840e776fb78b83 -27830,0x724ba9b5546dfc66af75048b86d09fea5d6b8aa7 -27927,0x73232f0eceea96bcdd3b1b7c1a369f8f4f397571 -27727,0x73e46cd691c227bc58e77c2e6892d2cb6ae4a3ab -27679,0x742f198cd57bd79826a94725dd49d89e0ba58edf -27807,0x752b491184ff31f38179895f80c3600bf0fca651 -27873,0x752ef3fedc289a11e56c32da1bc061c4e27ea589 -27583,0x76fa65ba058555fd8a924079f17f3d29b6490db6 -27724,0x77a015e5e475fe20445a285a399102037cfee444 -27711,0x787307c93838e6ef1b24d578376bca0f3b0bb4f5 -27780,0x78f118012b1709e97df6cbaafc4fcfa7933be468 -27908,0x7b85f771af89052c6c238c376e63a95287c54ea6 -27838,0x7ba593c5ed09bdf63a9e4c8536db179ec28b71d2 -27589,0x7bb7a0c2a55fb45b8a4159d8040884755cf3dba5 -27624,0x7c6ef4654580078bb47684d33f247032525c91e4 -27851,0x7cff257c7cc6d9d090e5b445c1747e353e73dda8 -27736,0x7d656def133b4643ca42e7044636b59ea323a6e2 -27796,0x7d78d168dde160db7f7d7c15407b62381362c009 -27835,0x7eacf8cfa75966ff1b32bb35e6a1aadc98d59f89 -27703,0x7edfad1b566657a236c8422ba6997536bc647a29 -27584,0x7f72b065fe176218d330d41052b38bcf3cd1e1be -27581,0x7fdafc1556ffff471884bf01253cac7e188fb35d -27657,0x7ffbf9ced2ec03400640fd1bd15b18a5df46b3f4 -27833,0x800fe0dd6d3397f6b3328f4a826b9e94a8bb616c -27846,0x80e036f4e4532a79f20d41ac9fc6b90cbd5f651d -27599,0x82f6491ef3bb1467c1cb283cdc7df18b2b9b968e -27596,0x83348f281d9024730215cae44e1516c21ae595c2 -27751,0x83a093eaf4be1796139ab261ece9a2f1d30baadd -27879,0x860fb65a774714a4481b1b537fe1f255d8e2c676 -27604,0x8620a9680c4917c9b4bdc13fc7768abd72dab2fd -27602,0x863de173ed8598e185d5c113e220a39f0f922f8c -27886,0x875106c596f897c1fb42169506f49df641d286b0 -27800,0x87e0092e3aae5350f9119ee36d0e2391019f311b -27848,0x882e87b617e5ee80cc3a59b08a19eda0bd200f8c -27580,0x890acda47659778b61119898d6ecec45877bcac6 -27762,0x89627112eacd324af941e203cac196abd4fc2ef5 -27696,0x8987f25b13b1041742c25832d9c3d41f7e81f732 -27697,0x8b371d22568665d7f0a42e7e3f416688539bd951 -27767,0x8bb52773cc0941257e48126793244ee24240ed4f -27783,0x8c934d0067a85731ab9b796e606cb5a896690714 -27598,0x8cc05f601f6763d75e262bea620051a3fa6ad845 -27872,0x8d28dc7059aea82482969ae8b7e501c63d6fa59d -27888,0x8da6e9e78efe68e83643c03fdaec1e63c26eaa8c -27677,0x8f8d9be2b064f0c1a8493bb05eb4e53fdaef03a2 -27642,0x91297d7e0ccd409333287b000c5a0d8bd18525e2 -27622,0x91d82e1172a70cf8cac704ae5e3dd4327fdadd58 -27668,0x9222c73166077ed1bdae0d2a9c9c6b1092544b93 -27590,0x93e73e1c3c0f79c9d2c56ca8c4d35883e2931300 -27925,0x94e01c38851eae0e01a1a7e39d1a562b0bda4fb1 -27636,0x953b210e4014fd9cbc6c947c179cca25476f9b6d -27853,0x977f1007881f42d7888123e367399dc74aca5cc9 -27665,0x981f5177d667be31c6798b654feae88918dbca19 -27836,0x983be3c67d536245c7bcb16aa7c1efe2aecae34d -27759,0x9960e41b3148b74a73244fa115de64e274fe3198 -27903,0x996e0a0a3801a3f642be2c5a4745bf9d526fa668 -27735,0x99e7336b8a6c1d3b3c721e08e959209656b3550a -27917,0x99e8b2549084dd260c0d2dba451050b6935afce3 -27771,0x9a167b9006227e3789d8d3f698dc81ec39fdd638 -27891,0x9b3a4ceef16dd2a58280777adb94870c64484650 -27921,0x9bd926a1fd122dbf95bf1a6cc316f0aeb025d8fe -27775,0x9c5a8b04f1a001bebd0c98cef4cf68ba1a546f0c -27607,0x9e308a7aaddbd35e1b77decfa1a3b54e8cd2c0ca -27683,0xa0df06b16b8316f209f579a933c4a05446472493 -27774,0xa1022f8e527a001936eed647ccd80f40eab2311a -27916,0xa34a07ff0096657fce46cca6086c61e068c8bbac -27585,0xa3b8050b30a3817c373283f869bb5edef16840fa -27625,0xa3e25bad9267e7a7cf44998104e1ce1e5195b978 -27634,0xa536b5a917746a29ab70fe7a6c2f57902bde1f5b -27700,0xa623e72431b02bb78b241db1a7ab28bcc61b78f4 -27804,0xa77e4748f1b525dc132a7a0feb90bdaae6779aed -27597,0xa81ebc382a5d591bc07ffeebdc404eca487c00e7 -27864,0xa8f055be0061850d64cc2690a098e7f5047a1f52 -27829,0xab634ce8ba30bf20825c2e4c19a4ba3983902a85 -27882,0xaccadeec27f3a1788a20dcaff4427e0a14487493 -27859,0xad2121f0da76e81b15793138f706a71c95753d3c -27869,0xad461801d75cc1c367f1cdf75ea2a1df20e3c28f -27868,0xadacc3557b7548b19639b3db123084244f839370 -27720,0xadf5154cac965075200826c024a2e503b201d60d -27663,0xaf523ca834681cb995908b88aa4e52b8fe2fb8ac -27763,0xb0c855bdd72e334fc5b8351185b32233dcded4fd -27811,0xb0ec4a6c4657a2ebababeac08000a36040e05a43 -27890,0xb17f6fa3fa64e845a853a351821cd56af7ac6c20 -27902,0xb232d84fe15be3bb63f95b94a6d23790efe15dfb -27789,0xb2fa45e65cd7842a5dd314b0300901f9fc0cf9ae -27613,0xb3c9635317125768738f7771af52e5feee0b6c96 -27802,0xb542c030fe1a9ebd1b335e5fd7669a7737ceabe7 -27594,0xb5f89b6c09fcbaed09b08705c21da5cddf3b9c1d -27897,0xb78c7ae9b6bfd9cdef490a1d9d61726125886d0e -27648,0xb9750e320cf62d7dab41a3c6c51b9cb72eb16597 -27614,0xb9865900d206a0588def053358224da240844450 -27717,0xba4a0cd838479e468c5f05655dbcfe210c157c5b -27884,0xba4cb5a755b05de761933f5642fabee25926a622 -27791,0xbc237d3c12946a40c8f7a0b85e63079e0d2ebc20 -27854,0xbc5dec02764f221d585109bdb3a327a5094d7f4c -27632,0xbd1aab6f3802ccdc8d34f92ac033c99349451c3f -27812,0xbeec19aff0bf081740fdc0f6b52a9f0e43be4190 -27744,0xbf5dfd2f1d7a358b76549bd0ffbb914beb55696e -27638,0xbfc907920a9d9e11a356727f43b6c56ae2774add -27845,0xbffb1e87436dbaf3fa90661ddd8520c53324f3bd -27716,0xc073f373f207a77759fb2184b1cfe1ddd4598d65 -27629,0xc0e08823e8831d33c777e111e753af59ce041417 -27637,0xc19c95cd60e800b33176301fa6942bf1692cf319 -27742,0xc1a078bfc6c94d3c50b88e0a2f4d6c6528eab768 -27710,0xc2021ca52a749cb70ad42dea2814d1b2147fb36d -27852,0xc27db0ebe0d2ecc80939dc09ec28a4b241ee4071 -27832,0xc2ca6ea9800bb69b2657a9519b211dc224074868 -27664,0xc342143f6de5a2bb3f40dbe79fb97ee998cc1ca7 -27592,0xc3a1b9248ba380317d4a6f13f124911685359632 -27672,0xc3bd25e769461ca6a2ce679f2123f5c98d9afde8 -27647,0xc47867b59b4d53e542ceb22a6b10277e490b6b92 -27615,0xc59d4fd09d196108c52f43b6e37d9ee6ca52fe70 -27719,0xc614787c3c90582a08acb36d1f84bb5c787006a4 -27681,0xc7b04cdf2a3f4bbd5bb4177924d1945fa1bad2d1 -27713,0xc7daaee20f7b7d87e7e5cfd2d4fde95431e72556 -27708,0xc7f8f23164efe501ad8ccfb38a381c9b7ff2b2e9 -27761,0xc8ee4b907405ef4f31889e286f0296f0702ccd09 -27822,0xc963d9e610f31b2fff727a33511ad4c343e9f513 -27862,0xc9d8edb1686a813001509b074ea10086ee21090f -27715,0xca6037e4236331dbe7e94ab38c02ba6ebe8412d9 -27799,0xca907dc4bc4246684ccd8349024e9cf758325ebe -27593,0xcb4c5190017cb106803d6d5225e21a06231b11ff -27899,0xcb50c7d694cef595d5252dc7f21c4b55150665ac -27809,0xcc3288f7e10d9e47cf2e9359c4f42d9c3c31496b -27587,0xcc5411ee2a3a684a4685516bf601471bf9af2bf3 -27621,0xccb9c7872d14ebf17cef54d6c90261507aedf94c -27756,0xcdd4cd3166c110b9cfcd5b1d9accd6fa7305836b -27837,0xcff5c0c154a159c75c1c8f1296a286f365a245f7 -27650,0xcff72f2a3238c12336e91add2f758c5d0953e9ad -27628,0xd088acd92ed641db127b9c4ee30885cfd8260b66 -27826,0xd1a416a36c97941db3bbf2044df5da6f7c7daea6 -27640,0xd1a614eea9d4df1565ddf6329234d0c74dc2c1fc -27620,0xd1c259ed311b2181931133bf5d30fd1ee607a2c8 -27601,0xd1ca15e3c238d2ccfbfbc44b9633df39a877bd87 -27617,0xd1d00646ebf5e50539dd7904fc15f205016a56f9 -27659,0xd29d0e378186e87a859d814a111f7c3a0d263352 -27603,0xd2e0054649fbff0fbe5710327cff5ac55cb30001 -27906,0xd320782823df889415f3d70f0c6d032b4de89e12 -27787,0xd400a85e1f1ae6eb81c9b1f1d6cf616717d823a8 -27755,0xd585d4fdcc49744f65fa38adb709573e25d53993 -27741,0xd5bd1254c941d289a1900520ecdfe51c5ec58e97 -27588,0xd5d9454e73354cc7d0cb9fd70ba386bffeab04f9 -27861,0xd662db91d1583c8f6d34e208f2787d6480c0e2d5 -27627,0xd6e80d79d911612cb10f0060c149408897a5e425 -27652,0xd747c9be4c379e9dcb655c3cb735581d78afb654 -27855,0xd79c54ccb663b9bd9867597e91e532c7cf68d9b9 -27782,0xd7be6af3909b9b9596a2696c9962607b868c1138 -27618,0xdb0368c21b0bfc75660a709b6d1c0cc38b0a9e70 -27798,0xdb04ff8078b38ed24e8ebf432cf1fed4fdc59e27 -27689,0xdc109b84aad9f3376e3728a1b45b6ee4b9f17847 -27706,0xdd30d0545c87e325f5a0c53ce58acb09eea055c5 -27913,0xdda7c623dc9798615c20908f978370f9675f037e -27857,0xde763706332c71724309fe5f2e619153b38859a0 -27766,0xde98a119ce2781e5c4715a22df1863e989962ac5 -27827,0xdf7d60a70b67b22609cfbd3dc28eb3a64aefe413 -27911,0xdfda14412b0092a1991781880f6b05719b54d0bf -27893,0xe004566454aad6f7b4a1667c6db1a9dd8328c0b5 -27728,0xe03429e17a54550d2109675211b1cf2883c6b8cf -27750,0xe03bf2968ef7ad03162a998805b90ea63ec71f7e -27909,0xe0b8dfddd9d49d817d7bcecfb931377a8fdb5669 -27757,0xe14cabb0cefab38bbc69be4f79c9e1b6627aaab4 -27781,0xe16d4a6a3426b1fea7715eade7948a45dd23e733 -27904,0xe216a0adaaf0ac18308ee78518b0f50f037c0b1b -27623,0xe23a31b281bc29c73506d7e8dee4b5f3eaddd770 -27758,0xe3c567efbcd6a00090f0c8f961ebce40d278ff20 -27922,0xe4643f0a9957f797d21d2bc0ff2e1821ca7abe95 -27653,0xe47b126a9284653a5ed95e7aa001d4642372752b -27905,0xe4bbfc318d7679b63da19f4b96bcad61fcad0ebf -27817,0xe4dfe4179a22f4c6b571c2a139ab2b1c0c1f67a8 -27753,0xe641565532ab76064118b307b0308592f3955b23 -27820,0xe719e4ea17f273119b4f86741912117f35262d6f -27704,0xe75b71689a2b954fb41e9a9ba0f250eee4e63081 -27654,0xe91d30223f2d211ab076513f3063bbe67131270f -27842,0xe9a0ed737a533678d774e560d73fa2a93271385a -27651,0xea538fe5504163247248b64d5f97a89154dabd57 -27839,0xeb2f112463c1bf3554c02a1756e3545bfeb81bf3 -27669,0xeb942c42d8f5d6b31bb3d75f644d1eb09b3ef721 -27658,0xed3c32b1ede4a3b20280e22a8de530380af114ba -27831,0xee0da4f7d7dfa7ef988b3005b818ff3ed1702e7a -27686,0xee973c551749e75857d60fcffa1eac4a7ad2758d -27889,0xeebb4a705b7635da036532d4fe5759cdba192c09 -27655,0xf094da31f021ad590c326ebf662fcef0b9de2883 -27676,0xf1351e660a99e7dff07b2a5227b9121f00a0708d -27894,0xf14c0c056df6c7a8984fe87321325c427e481407 -27643,0xf1d79758af064a7bde87545200a72928e27750f7 -27695,0xf201f234e5a150ec417d3a2eeb53434b7b747e14 -27813,0xf23be7b4404c47713c96504e8d1121e97c1a0e24 -27725,0xf36c8bd2830e9e38a5f6db39ea419260491524bd -27871,0xf3a273374da1037dc1c29a38ff7ae5a02eaeb6ce -27738,0xf3b2293c161264e6980853a00ae7bbe60b1315d8 -27682,0xf4c05fa5da1d497eb3f298c5af060aab23d134f3 -27772,0xf51e7f9b05c90aa9d0c6f3db83ece272f82c3c81 -27816,0xf56802ea984ac9b5af5222c57986644c510a1b01 -27828,0xf68220ac61ab681d73fe9b07f2ef43d44710e582 -27731,0xf864fbd5562b6bfcb8ef811b9424434b7466800a -27687,0xf8e4e3c2e94938a98189ed7cdc41cee331b550d1 -27778,0xf8f35185b0f863b4ae8a09d08f1fe94fc2d5316a -27609,0xfab6468cfd6829a95027c32c7e791ffdc355a27b -27701,0xfb18922ade6e8e98fc02f65635f765216911ba14 -27726,0xfc2c749fd64f8cd4c808b89fa61c7cbc17363acf -27786,0xfe55eee9e7106f6194d914755f9a70a88aa98d0d -27656,0xfea0d5a435bdc7affeeb2e6b4dfb0b8f75dbfc5b -27649,0xff01e7e289a7d5dffc31a7e00699619bc552b596 -27793,0xff7a5e79153682bcc77f68b947b18d93b986cf22 -27941,0x47c2a56176335fb2b1ded8e7b5acb136d307dc2d -27942,0x65898cb482a821005e30ab98c0d7dc1032a48805 -27940,0xc86fa04e2916f3558de23124fd90d1173666011d -27943,0xd0d0cf7be1273b9267e1a80c5de867748fb84b14 -28055,0x0296da2ce82eb3b98eb05925bc5777c7da0d0f09 -28048,0x035aba93c18d407dec788ca2fdc77b90feba836b -28063,0x0f219898699b3f8008d9f05fac10bd08d4d6c65d -28068,0x1135cc96a7e9d8f161be8b6bdb74f896a9658a08 -28037,0x11984dc4465481512eb5b777e44061c158cf2259 -28074,0x172f55db1ec560ea877fec0b4afe9a780e5fd52a -28073,0x200fdcf7ed3d856a70380d1d88778ebca9ec6959 -28038,0x262bcedd20b6223cbb807a0be3900ab87316c671 -28081,0x32155c9d39084f040ba17890fe8134dbe2a0453f -28051,0x35a6cdb2c9ad4a45112df4a04147eb07dfa01ab7 -28064,0x370b03e887d5eb033fb05cedb449199db2535269 -28054,0x3da402d5cd4bf0a7c6003c0b9c3c19fdf1e90fd0 -28046,0x3e0fdc863607d0a3d50ebc90a1385f8cae29b9b2 -28060,0x50ef4b4436faf884afb95e7d5560f4c6eaed2a1c -28092,0x51058d7bda2661e134cc40137fc04cf3a9087e53 -28044,0x59d908fbbbe670388eb1bd5caf30af47cecb25b8 -28059,0x5bb83e95f63217cda6ae3d181ba580ef377d2109 -28075,0x60dcbc3bb9eac7264a2d279f790969ccde61f4cf -28101,0x60e391dbb9b3a324d8fe1e7d8047a98d0619cd2b -28082,0x63377166e406ee6a992b8e20961f047bb12e4d81 -28080,0x67528e1e4394225c322919a87d9e8d6c688a783f -28089,0x681f0a7e7bd478690e08d5e43bc6d003e522777c -28071,0x683151d43758bb7a37e9d47386154d5ae9b8ac48 -28053,0x6acbf8d58f785541438e979305a59636e729df9c -28086,0x6b5aeb8d272039154d0df19cf60b5e5226ff9dbd -28050,0x729a17a6802a7348eebefbe54146eda98b121656 -28067,0x72c393d9ae5e352877b4adc585b1939b670213d8 -28096,0x72c51564ecfa2344cf168bfc4dfa7e64a560aef4 -28084,0x745837468a6a4f7ef5eb3fee18fc6e74376443c6 -28066,0x785a4c7ee79778e9670a6a02c60c1bd148bcd525 -28094,0x7dd2bcc0828e0a43c67c1baeb3cc075695ac8b9b -28049,0x7fe09d217d646a6213e51b237670bc326188cb93 -28088,0x88483b3e3b4dd7cedb8efcef81f6dc9adb6292d5 -28065,0x8888759373137b84e9f0ef13ecb13e321c251ee3 -28042,0x924e679c3c23017aef214c9ea1fbc22e97ff9e2e -28099,0x92cfe86d3e760302c4822f54d9d3d08fd71703c8 -28056,0x9bacb5dea769867d610df21f04eb612ae3b7242a -28090,0x9e40a8a535d4875848e1821e3c59ab5c5dd86135 -28058,0xa05ef29e9ac8c75c530c2795fa6a800e188de0a9 -28039,0xa28de94d2e6f84659c2c32df14334daa08dd6461 -28083,0xa3e91aefadecb8919180f581f5be897c763be593 -28045,0xa93c4a6e19bee415cab341d19046c799a80a1496 -28047,0xafb88881e53589f5e6eb1cc27e9207cc7f03023f -28072,0xafe0a0302134df664f0ee212609ca8fb89255be4 -28087,0xb0eef3e1de973d045c3858e072c540299585252d -28040,0xc162d91eba9cdf57f6c1d82b10deaee2bf233c50 -28079,0xce157927222cd68b9c7057e84344ce6f0cee89b2 -28076,0xceb49aea4faec437bf1b82ea4a76940df9323273 -28097,0xd47bd37deaa7d960659f5ddab98f4e4729165d3f -28093,0xdb2a821474735c80cc2f05889be709a37ea02e89 -28036,0xdfa5fafac70a610e4224b85b7fc4764a0b90d2ea -28085,0xe03fce428ce0436ac7626970d221d64415f1fa85 -28077,0xe54d6a0af692f949622dada01ed1d64a1acc9c76 -28100,0xe733ee1358a8cea317351a89f98a8a042297a878 -28052,0xe8c9fa5195c607dd3523862696c24c86770af64a -28057,0xe9ddd1963d302d1e13239a3c25254efd39c2da91 -28091,0xeb35e251b29166acd5652e00892aadeb2b45f5d3 -28041,0xec2140eb4a23e36ff676e18626a8652ea2be47fb -28078,0xf5be67922914c0b933d13802e834be9e532c09a5 -28043,0xf659a7b83fd9f69b52f4bcf9389991515acf4fd0 -28095,0xf7de5aceeee6091d1103209c337fa00d0b4b9092 -28062,0xface86b9f34230624cf48eb582aff4bba970edcd -28070,0xfd9b8b1b7e44b39dfa04d66159ac21b68141ed38 -28069,0xffa5e2df76b9cc453609de4c1da551926110a4af -28061,0xffe0821f1f088b16e6760edb5d537eb2551a3a0b -28518,0x00ee580c07eaa3948351e09e087e39ac897c61f9 -28384,0x01a1c4b99d38bdf052457697fb5736d793b5da3e -28569,0x01c951f7fe7f2cd56f2fe371a6decf16b9cfb63f -28364,0x01dfb2dae811cb8492dde3cfdac617c5f3f5cd4b -28544,0x02777053d6764996e594c3e88af1d58d5363a2e6 -28555,0x02e5f8a99c51c5d85256d05e63f27d7a27406fd4 -28488,0x02f5e9e9dcc66ba6392f6904d5fcf8625d9b19c9 -28581,0x032f957bfbb8c535a1b2048f8b4fa27e1f2018fd -28357,0x034f1d70092e81b7738459f02f409a5c5c4b8189 -28538,0x035e930e88cdf1849ca1e9abb3e72043efdcb7c5 -28517,0x0542bbbfbc26a86ea92d2b7f6da07daf0ca091ed -28550,0x08736985c5bdf0bb2c16037560822c760b232b0c -28327,0x0a2bccfeec28459e4bf1ff1cfb2deaa74126e63f -28426,0x0afe8b0d27f47077530c5197486e56e507ada28f -28467,0x0bd3a2e11f7de1e76e7581a07b8b9134c10eb2dd -28495,0x0c1272d2ac652d10d03bb4deb0d31f15ea3eab2b -28615,0x0cd83cc474e69e611d240f0d35d5794361f5e5c2 -28314,0x0d110cc7876d73c3c4190324bcf4c59416bbd259 -28485,0x0d276fc14719f9292d5c1ea2198673d1f4269246 -28322,0x0d5642c6329adb3246c13d78b429a9fb1965a0d8 -28519,0x0ded608afc23724f614b76955bbd9dfe7dddc828 -28458,0x0e09921cf7801a5ad47b892c8727593275625a9f -28471,0x0edd9ac354033ef766ecd45cb235d81139df3d90 -28435,0x0f86056d00bcf39baef81bbed1786e6f32c1a5fe -28382,0x11086da4bbd522f087755e6576f1358ad2ca80ee -28486,0x11429ee838cc01071402f21c219870cbac0a59a0 -28604,0x12151e9e21caa9b2ca132c641a71bf390ff0bc81 -28497,0x12922291d1fcd0d121b5c88f061047fe18732743 -28337,0x138b809b8472ff09cd3e075e6ecbb2e42d41d870 -28316,0x13e3ee699d1909e989722e753853ae30b17e08c5 -28505,0x13f11f2139c10a48ecd7a6a14d804f90b2cfc89a -28595,0x14d2d3a82aed4019fdddfe07e8bdc485fb0d2249 -28401,0x1520874fc216f5f07e03607303df2fda6c3fc203 -28473,0x15772f61e4cdc81c7c1c6c454724ce9c7065a6ff -28553,0x167f21c202787763f6165f9c8d3c11aafad2ecb7 -28498,0x16a9fa2fda030272ce99b29cf780dfa30361e0f3 -28464,0x16dc12e91fcfe149aeb3e1a795ac6e988de8c6b4 -28320,0x170a530bbb5925808f743aac7ba89d6f0e748552 -28358,0x17d582034c038baeb17a9e2a969d06f550d3586b -28336,0x19bca7c81f3ed561a49326b78468eac64d0e8f2a -28419,0x19dc743a5e9a73eefaba7047c7ceebc650f37336 -28451,0x1aafcf49e103a71b31506cb05fb072ed1b5b0414 -28490,0x1c1245eefb57d50f90efc4070b508f4f24c3ab7a -28428,0x1c1df24f0d06415fc3f58b1c1fdadd5fc85d2950 -28594,0x1c4f2b051fdbe353d17c58eca8ffb2fe15b5da71 -28309,0x1c9213dcc6762efdff500a2d3f8df4508b37636a -28306,0x1e2faed390be7945acbf22fc25173c4bdda3ea45 -28396,0x1f69648f1b985344cdeccd5d2a36255cd22aded7 -28600,0x1ff4e94c20fa84e83b6adb912bc36e6681f8fb33 -28526,0x206a01d5b59b7d7315b6bc7b5866f62a6fdff7ba -28431,0x221618871470f78d8a3391d35b77dfb3c0fbc383 -28330,0x22f04bc4162d63730dcde051fdfd97b4f55ff63b -28537,0x22f3727be377781d1579b7c9222382b21c9d1a8f -28530,0x25dd1949cdb81f5fc38841a8abf342c4ef48dbfd -28577,0x261c05167db67b2b619f9d312e0753f3721ad6e8 -28403,0x26fce884555fae5f0e4701cc976fe8d8bb111a38 -28483,0x28a6b219403c1dac04172cbb8cc1ab8bf5925830 -28507,0x28e67baeeb5de7a788f3dde6cf6ee491369bb3fa -28411,0x290dd71254874f0d4356443607cb8234958dee49 -28315,0x2a8d91686a048e98e6ccf1a89e82f40d14312672 -28622,0x2aeb623abf436e256d7975787becc95f4a4bf89f -28457,0x2b57177ccc39e6b72e8534bc4b8c3755c039c603 -28356,0x2dd486f1fa76fd1228a9c818c552c6a92f138453 -28499,0x2f50410612f5e69f52c21fe9811e23cdab358ee4 -28362,0x2fcf37343e916eaed1f1ddaaf84458a359b53877 -28460,0x2ff1eb7d0cec35959f0248e9354c3248c6683d9b -28603,0x2ff8822f371b283604369700d6f06da3fbb31064 -28560,0x3035ddcd17297bd0ad3282fd15fb58fedfa8e498 -28416,0x32c9596d07aa0d6b31b79c0c25e6a23e5731a6f7 -28373,0x338ed6787f463394d24813b297401b9f05a8c9d1 -28469,0x33e047119359161288bcb143e0c15467c7151d4c -28405,0x3442bb3ad11957449d9af5ace9d17709240dcce7 -28389,0x34e0e85ceec6be6146c4f0115769a29a9539222e -28397,0x3626369857a10ccc6cc3a6e4f5c2f5984a519f20 -28533,0x371ead81c9102c9bf4874a9075ffff170f2ee389 -28482,0x372cc5e685115a56f14fa7e4716f1294e04c278a -28503,0x37aafb2ee35f1250a001202c660b13c301d2130b -28559,0x37f459eb4b9ca9b42bfe113c5b533d87f47ad847 -28318,0x38481a16457817e9b35ced28d0218baa4572eabb -28536,0x397c2082da7a0962a4fbf14e62e72dbcefb7a7dc -28463,0x39be70e93d2d285c9e71be7f70fc5a45a7777b14 -28379,0x3b06b9b3ead7ec34ae67e2d7f73b128da09c583a -28408,0x3e313e778da811f79a66570b8703c66204112dfd -28619,0x40bc21f78e05550dd4d1815695ce2d0d97ace826 -28380,0x41878779a388585509657ce5fb95a80050502186 -28496,0x43dea17dee1ca50c6266acb59b32659e44d3ee5d -28424,0x455d966bbf8d1cd3a8900800febea979f36a9a91 -28415,0x45954efbd01f5a12428a09e4c38b8434c3dd4ac3 -28508,0x464a1515adc20de946f8d0deb99cead8ceae310d -28586,0x46d677b285728bb641fda3470873637372a587fd -28371,0x47f260694802fe5918d9ffa3544b6a54deb7157e -28456,0x48f2ecf0bd180239aef474a9da945f2e2d41daa3 -28412,0x4ba31115e3996804c860a2e7695de169b7b0aab2 -28525,0x4c4814aa04433e0fb31310379a4d6946d5e1d353 -28591,0x4e1c6b168dcfd7758bc2ab9d2865f1895813d236 -28304,0x4f6dfdfd4d68f68b2692e79f9e94796fc8015770 -28350,0x503465204d3e093146b1e8762e2b221240e0eda7 -28535,0x50424ed4002bdd315d02ae863ff9cc8f1f4f12e0 -28310,0x5087dc69fd3907a016bd42b38022f7f024140727 -28608,0x50ac053def94ec35d87ca8234bcbb9a84e85ff3c -28311,0x517c2557c29f7c53aa5f97a1dae465e0d5c174aa -28583,0x51e06250c8e46c8e5de41ac8b917a47d706128c2 -28475,0x524299ab0987a7c4b3c8022a35669ddcdc715a10 -28563,0x52fd600da78cb6316cdca1a12427d3289d1568b1 -28367,0x530ab34385ca1d134ffd33d267f5a2788d645039 -28484,0x536944c3a71feb7c1e5c66ee37d1a148d8d8f619 -28548,0x542ab562b6d342a35f6f63cc5a39d03fb6abdeb9 -28369,0x544a5abfd49782b68d58e69bc52204b692a86d9e -28391,0x54e6827c19b63854b7de196b84366a9421ab3411 -28472,0x5535e67d8f99c8ebe961e1fc1f6ddae96fec82c9 -28573,0x55b3fca23eddd28b1f5b4a3c7975f63efd2d06ce -28532,0x5805883423da1deb9866b4ba6b3749b163d0b472 -28407,0x58218ea7422255ebe94e56b504035a784b7aa204 -28568,0x584c7e52f94140de086e27c66379f84e65554987 -28454,0x584f57911b6eedab5503e202f8e193663c9bd3db -28438,0x596bddd7917e753243283266423e4648047479a3 -28432,0x5a8774cebbc5205fea71e2e041e9461b7e65d9d5 -28613,0x5a9072a995e072fd06d8f1eb95933955fda53c0a -28570,0x5bf7346de2f720b73cead8fe366eeb20f96ed5ee -28585,0x5cdc797accbf57ee2363fed9701262abc87a232e -28347,0x5d101824c693c70a68ffc3cdb0cc394f3a4fb9ec -28360,0x5d1345669278128b77af9662c5d6b5e0b2ffd54a -28361,0x5d7332adf81c9e7372779e5f2f255b0a6e222fe1 -28534,0x5d860ee3a9f47dfd86d40aec1ef7ddd876356f71 -28587,0x607b417df51e0e1ed3a12fdb7fc0e8307ed250f3 -28620,0x613764cfe574f6c5ac6fa21e540bd164867c8350 -28528,0x615209a932768861908161cccefccac9b582ace8 -28386,0x62f42f70ba85de1086476bb6bade926d0e0b8a4c -28618,0x63c120ac1e0354bf16ddceb5591bb7aa2fdbf5a0 -28352,0x647da0ebfd5314af3bd53ea40541c6b67169e6d0 -28461,0x64ecf089a6594be781908d5a26fc8fa6cb08a2c7 -28610,0x652fc7764ae7dfabe8a2b390283cc0beaa134eb5 -28385,0x65dd8a8576bb606a0d8fc5b864fc665f1309c5a2 -28515,0x65f2c716937fb44b2c28417a7afc2daccf1c2d61 -28439,0x663ed3d2ab9f8d5282a94ba4636e346e59bddeb9 -28477,0x66f61fee824c1df059bcccc5f21ca39e083eefdf -28394,0x6774c192defa736a16199ddcf69799467c255fb5 -28363,0x698b585cbc4407e2d54aa898b2600b53c68958f7 -28312,0x6aa7cc6fea036bd57f36e2b82878c15012c48771 -28319,0x6c71e3bcf854d3f27aa3a8a4ea28302a1d55e929 -28584,0x6d95344ba8d22a7d1c5bf1822ed80a70f411740a -28614,0x6d9d0440b5f5a2972ce1d190f3e228132d318250 -28387,0x6e7a3ceb4797d0fd7b9854b251929ad68849951a -28554,0x6f660db909dcaeffa8935d9bcad615aa93a43688 -28593,0x6f6ced6b096708c1276056fdbdb7bbde07ca462c -28516,0x6fd5e4a193459fc7dfcfc674357a123f655f6ef8 -28523,0x718a5788b89454aae3a028ae9c111a29be6c2a6f -28597,0x72155d46fd9f03af1739637f9e7db8a87c40a730 -28616,0x7303b6034c60bfaac885883d6b0ae2382c668aa0 -28504,0x73316ef731f3c7219482716682c063dbbd1602f2 -28552,0x738237f8af45b1b83c83e63bdfbadcbf29abdad9 -28478,0x73a3919a69efcd5b19df8348c6740bb1446f5ed0 -28355,0x74af0fbcea9eb75f34a00ae45a5362de05206027 -28345,0x776003ecdf644f87a95b05da549b5e646d5f2ae4 -28393,0x78f049f6da1ac1dca50d6d8f184acf47eb269852 -28609,0x799a346e7dbfa0f66ad0961259366f93a1ee34c4 -28344,0x7a18889f137b593f4e03c0a698a4360f43d1731c -28453,0x7c56d3650f9acd992b3aa635c04a311c54ad264c -28400,0x7c57dabfd89d34865f7b1853775ea6bb1009c3f7 -28331,0x7cfb4fac1a2fdb1267f8bc17fadc12804ac13cfe -28372,0x7e36b8c677d84556b4742f9d7791019bc7d408db -28378,0x7f99817d87bad03ea21e05112ca799d715730efe -28392,0x805a61d54bb686e57f02d1ec96a1491c7af40893 -28465,0x81cc0c227bf9bfb8088b14755dfca65f7892203b -28429,0x81d9a9056e9af6585010b784df5853a0fdef8b11 -28383,0x826c51a9ec42c7d634eabfbd90101a7de84406d9 -28596,0x82c3b7afea9d8c799a75573cf9b8abd0ae042f2d -28434,0x8314ccaec41244f2a6a013fd997b234ed5ee49a2 -28574,0x841b32b5309ba30cfbf4534667fc3d99edf05b7a -28513,0x85a48ded8c35d82f8f29844e25dd51a70a23c93d -28556,0x872f782a861519b3fdfb1060649f4c8343d806fe -28339,0x8788f0dbda7678244ac7ff09d963d7696d56a8a0 -28440,0x89178957e9bd07934d7792ffc0cf39f11c8c2b1f -28492,0x8afc1cc622be1cd1644579c9c7ec3fbba6bd02d2 -28399,0x8c0481da9009dd32c14db7f7b2a111c6f8438e07 -28572,0x8c7c2c3362a42308bb5c368677ad321d11693b81 -28395,0x8dba75e83da73cc766a7e5a0ee71f656bab470d6 -28611,0x8e8e6c8c4942e4963c682ff54a0d058458393dcc -28377,0x8f14546d0b960793180ee355b73fa55041a4a356 -28565,0x8f168dbe826b18c5940ece5db9668e78fc794d29 -28446,0x8f7bfb42bf7421c2b34aad619be4654bfa7b3b8b -28375,0x8f8c69975b079a283a01ea01e95b5937fe1e636e -28441,0x8fcfb87fc17cfd5775d234acfd1753764899bf20 -28470,0x903f58ee6d6c3c2ca26427c8f917f6ae515827b1 -28436,0x9075f666ae7c063a347ec8419a67e4b1e36d4d3e -28511,0x90ac3f96131699b7920004a58717c5eac8e5c9cc -28308,0x90cc16f5493894eff84a5fedd1dce297d174feef -28578,0x9270aaa75f4b9038f4c25fec665b02a150a90361 -28476,0x92c9b9c512759f5d04563efa3698fc4fbf735d59 -28547,0x92d6a41feb5d5f642b1a87dc71f21f6c8ee11c78 -28575,0x92f80fb5da8df9a7f4737f9b98ab446cb1ba4f99 -28376,0x93521aeffa5f7adec85d7bcbe51c22a1513981bd -28351,0x94a178f2c480d14f8cdda908d173d7a73f779cb7 -28307,0x98f785d32a363234799088027dd2ff11f75f1226 -28509,0x9c41500de0162cc0bc4798982c867860c1601a60 -28326,0x9d215d3088c54fb94d71258d17bd990b6246e5b9 -28442,0x9dfc79aaeb5bb0f96c6e9402671981cdfc424052 -28599,0x9fce737834500045fb07ad158991bcac3b05d5a6 -28448,0xa12cddd8e986af9288ab31e58c60e65f2987fb13 -28567,0xa1da6949ec74b44b43a16cb952e43e834b2253da -28592,0xa2a740744c8222d15b6343b71951a68f8fcc69b7 -28524,0xa2e0434e1f39196366c17b2b24aef909e4afaf95 -28580,0xa4d34ca38244f6c8ab640315d7257221408b6596 -28333,0xa57074aca7fca1a3ce8e79ecfe31c2c11be80982 -28329,0xa6d25eebae9c841c44ad01c9176556a4c2189961 -28366,0xa7f0ff864196729787cce72f78d769eca926ba1d -28406,0xa8d30dd9720f292440f9539a89495fcd63803c25 -28414,0xaa75ace4575abbe1d237d991a7461f497a56a8f0 -28449,0xaae204d73360917f4dab2404b9d4545793c8bb21 -28359,0xaae61c522e1b65e686d736d67c4eee10bcd47805 -28541,0xab164949e0db4a6b6877e1eb2045ad3af3cf2259 -28571,0xab1743696234d32c0ef06e84ac0b65fbf6cb9223 -28521,0xab544fdad5f68f0f8e53284f942d76177635a3d6 -28423,0xac37790ff4abf9483fae2d1f62fc61fe6b8e4789 -28340,0xacff41ecc31a55d117c9ffa8cfa60d1f635a19a4 -28579,0xad1b1f2a6dd55627e3893b771a00cd43f69dce35 -28588,0xad1e27afb932d835ff9829bd16534e5e2c4a6fed -28589,0xade082c91a6aecc86fc11704a830e933e1b382ea -28520,0xae33e077a02071e62d342e449afd9895b016d541 -28443,0xae4c8567c942b974af4a860380c99a8d03c6148e -28381,0xaee9c09ee9848d969e40b2919e78fee63cd5f5d0 -28590,0xb001d353633cd96b68000aa915c8a8a136d90a98 -28493,0xb14cbe04a49bf352b939576f9f9665e1d8dc02d2 -28602,0xb1b575da673d67b1d10f4a9cbfba2e106736e30b -28365,0xb22900d4d0cea5db0b3bb08565a9f0f4a831d32c -28540,0xb42f9f7c0f9997f62ae521cef14b20a58bd9e088 -28522,0xb57df82909a750f14ea32ada4c972c26a3c8a6a4 -28433,0xb6b77f1696bc4f95860228286d27f7f4df5d26e4 -28353,0xb6b7b9b2362f87f204f1ccaddd8832d3a0557dce -28418,0xb785e9aa87cdb29cc11a3f2b8bd17e6279275a35 -28321,0xb7b9a39cc63f856b90b364911cc324dc46ac1770 -28479,0xb962e54f1a1ce8eaec6de10bdca779ece17cd8fa -28323,0xb9b16330671067b1b062b9ac2efd2db75f03436e -28374,0xb9d7001115eebf7179e5f95d45711738180db1ab -28558,0xbc718e3f1a81da36c4a0e6bcabbe335f43b5996b -28342,0xbce7579e241e5d676c2371dc21891489dacda250 -28445,0xbd92c6c284271c227a1e0bf1786f468b539f51d9 -28506,0xbe2f6d0fbbf143c0844d98e7bdc9e7232a278bbe -28427,0xbe5b0013d2712dc4faf07726041c27ecfdbc35ad -28623,0xbf5384854988939729e8b76b8aece7d8d930f9f3 -28598,0xbf620cb752ba2d16bae2a3c5488cbf00b7d0cc67 -28601,0xbfbb4fe2fb71022dbfe0d4232c8c528bddf9c57f -28494,0xc021460c9dcf6a348b81a6b60d30cecf22aad199 -28368,0xc160350c8265f58c37e33df49d7b37f3c84bc216 -28447,0xc19d58652d6bfc6db6fb3691eda6aa7f3379e4e9 -28612,0xc1c4a83d4707878d6d86c8d9c3f45c4d48473aa9 -28546,0xc1cb3b7cbb3e786ab85ea28489f332f4faed5bc4 -28487,0xc2212835de6cb9ef5e26b04e64f7798472ff2812 -28539,0xc2262ca31b16ae1cfe6f7612f49e79b821e31162 -28450,0xc3a1fbab91a46aabd8ee0c6b81ad404268d31990 -28417,0xc466c5e2d59edf7fca93311ff7aef5fb018463cf -28409,0xc6066533917f034cf610c08e1fe5e9c7eade0f54 -28459,0xc663315f7af904fbbb0f785c32046dfa03e85270 -28390,0xc7d132becabe7dcc4204841f33bae45841e41d9c -28349,0xca6fa4b8cb365c02cd3ba70544efffe78f63ac82 -28489,0xca80a73840718826a7a8b6b7216bd5fda12c121a -28437,0xcb24d22af35986ac1feb8874adbbdf68f6dc2e96 -28480,0xcb42141fb3851851430a51f049fd7b998c4b4941 -28334,0xcc232dcfaae6354ce191bd574108c1ad03f86450 -28481,0xcc341634464b6fd1221e4d517cd7801155abac55 -28606,0xccb014fe9d7d7fb0e50e3ae8f0bf25b26e6596c6 -28566,0xce7d466f3224c136ea7ecbc0bef91738dbca8ff1 -28328,0xcf038ee3878c2ba0352a0dac1cfc55b0fdc13cf4 -28466,0xcf448277bf50d817fc1245cb7bb2620dad4fb508 -28398,0xd1cb03cc31caa72d34dba7ebe21897d9580c4af0 -28335,0xd1e56e7657c0e0d20c0e11c2b6ae0d90932d5665 -28545,0xd24e1cdd2f9c0a070f73081b5f79bdd0d42efa2f -28500,0xd38579f7cbd14c22cf1997575ea8ef7bfe62ca2c -28317,0xd61f4177bc81193bead7e8f6fafd0e9842e27eb9 -28332,0xd682c5f1a8eaa2389d6f8fa43067956c2386a557 -28413,0xd702dd976fb76fffc2d3963d037dfdae5b04e593 -28420,0xd87acdf6e63e00191da405e054b095744a008f58 -28562,0xd9fb89196c902d46c07ca91e492d3e0c77a5bf93 -28346,0xda6fcf88c718eceb18c2c08a543562b1146f4996 -28404,0xdd275d071137c5c157d16ffa3e0ba2117119dc58 -28531,0xe05a8c52b2e813c9605cfb8f073178ebe5a74705 -28305,0xe1011160d78a80e2eebd60c228eef7af4dfcd4d7 -28341,0xe18a4e99f019f92cd72e0c7c05599d76a89296cd -28621,0xe20196daf4bb86faf9a10943ee595fd98ca625a3 -28313,0xe4391393205b6265585250e7a026103a0d1e168d -28576,0xe470a3068302cf045eec3b800ddbff42b42e18d8 -28529,0xe4b9bcd7d0aa917f19019165eb89bdbbf36d2cbe -28549,0xe5215a65cfeb00700065fa50e4f51533b21b8a58 -28444,0xe59eba0d492ca53c6f46015eea00517f2707dc77 -28582,0xe5d13134e228d74ef8b3881618e04221d50543ba -28354,0xe5ddd474717f5fe6d6d78843bea3540a399ab8c0 -28425,0xe67a10da53fcd59fae7e47f155c290cb5defb4b0 -28561,0xe82c8e610793a72753df23e91a390676287c958f -28607,0xe98290265e4ae3758503a03e937f381a2a7afb57 -28343,0xe9b80c60a2333dca98c483a8a1efafaf17c5d4ac -28617,0xea2aed0087a620995bf609d1bcd76ea099905138 -28455,0xeaf1a9fe242aa9928faedc6ce7e09ad4875f7133 -28430,0xecef79e109e997bca29c1c0897ec9d7b03647f5e -28462,0xee35a95c9a064491531493d8b380bc40a4ccd0da -28605,0xeec819b2e155cc8feae194f5129f767409e2327c -28468,0xef89db2ea46b4ad4e333466b6a486b809e613f39 -28502,0xf030366b47eb1a9b14ad570381d29647e40955af -28564,0xf527dfef01751c4492a06ccd1ad107160cf6c22f -28557,0xf6fc60ace9b2a90a7b7416a5e9a4260d39406f6b -28491,0xf762108f4b32eafbe0b429cb5799d1403ef32138 -28512,0xf9ecc598293bd5fb4f700aeb5c4fb63b23cfe8aa -28551,0xf9fb68cd7b6bd9f94c4df3c653dca816ff8cdd3f -28348,0xfadc082c3a7d8558a54bb92b56fb0a8824624da5 -28501,0xfc7608cf76f489191cb319dd6167aeee387bb251 -28510,0xfe717e3066c18c4723b9be65837dbd112ed72e0c -28514,0xff32d7040994c71869d0f7e5607e14752542a0c1 -28324,0xff441d6626a48d4fecb81af40f305d84cde6af92 -28691,0x13561f4af9ef7064039cba7c9aaa6d1acf238025 -28653,0x13e25b709ef6d707afb699e86ca85a76f7e75be4 -28679,0x168b96194437ba18216629542bbdb5c55bbde95d -28687,0x18688a000d65b43f67ed2d92702529172e03f966 -28678,0x2656f88fd74d9d2149dfdd88f8ec74625334e54b -28654,0x2e492fde043d7e47c99a54765903b9d55705f85c -28660,0x2f3eedc9d0c3bf56d0b0618364257f2dff57c680 -28699,0x30a387e36a0fa6ff1d0386266557084725071e94 -28669,0x33fb9550b61dc7200f6c9a24b5a6e36be958826e -28688,0x3bebc3a0a97f7de155c978ca76954ad9c284bab9 -28703,0x3eece7a5fdda7c6b48c0e0d3da0bee2708626a48 -28693,0x49994fbb41c6b7c2f3066ca7301f25305de1234a -28674,0x49cad6da884b476179db105fd983566f98ff9e66 -28636,0x49dcd138ce113d48913e97988de3d08387d9d342 -28652,0x4ac11aff0b1eabb739c30e67388b0e74f685fb6f -28667,0x4ce34e11a2220fc92ec3d0b6db4658dacbb7c6dd -28681,0x4effdb9bf554d6e4cbcd18513741c19e31dda299 -28684,0x504490f7528e3d046c8f55a1750f4a325783afa6 -28658,0x54c170e912f70b4cb37d6519d0b88ae82b932477 -28689,0x56b2ffd01cd39ee34d62b328e07e2cef79a13cf6 -28634,0x578c55fa6890b30ef74ccd3af52f455bbcca4d3e -28690,0x5f0ea83eb9ef30f12759fc89ba0235f62b0dc750 -28655,0x5f9b952151654add4b21560da8f13ee5894051b2 -28682,0x6468282dbc46c289774455e3e0446d4e88e95a32 -28675,0x6596cea632b2d37f6db796f6790fbd29b906c866 -28702,0x66a6b69e41c502638973a294acf12c6f16f4c415 -28633,0x68e5cdb18301002453e21f19ee736bae2a2cda5c -28697,0x696c91cdc3e79a74785c2cdd07ccc1bf0bc7b788 -28643,0x6ece0fbb1ed61ca4186fa5345766dafd5713818d -28672,0x7037c995e573184ceccdacefbe5ceea0b8304e47 -28673,0x838a40be99cc0658e24d32172e5d16f0114a5ae0 -28651,0x83e3798b1cd45722916f3d140987a7eb1f8afda2 -28640,0x846d7373fd0fa12eecfe7898c05c71f6ba1ca7ce -28646,0x8b3fc3d7a4a3604dfb35c45099c0a527bc6af4d1 -28638,0x9780f24a4696c59fe935f33dd307884b1f05a56d -28662,0x99b123d13918e30454703fad096e01d4de89c88d -28650,0x9a2199e51a732b9ce89e663b34231ebc2b700e04 -28701,0x9ae9ae20f5c022e9edd79673101b6a0f37bbaec4 -28683,0xa153e474dd975c7fdba95a0d47839a57ebc1772f -28694,0xa25cc478c41b6864982ecc22fc058976f40f8b1b -28666,0xa73e6fd84e84c438744383bd985c047d218c7751 -28685,0xa7b2ff8eaff742b4e543d72033880d5445e1f3be -28698,0xac1638e513c716d5858cd117b1939fd5e7f72aba -28680,0xaf5457ebcc6c2afc56f044d9cc2484ec2b34142a -28637,0xb285e48f63a4882e98d9ea51e37923655c0aff29 -28670,0xb3e8cf888242e3717142b4f52e0d90ce0afc4bdc -28641,0xb56e657a52a87dacbf80d500facc4478c7865ad5 -28649,0xb818378d1c7d8a28873e60313734ad8b809c61b9 -28642,0xb9ee81698efc02083b41e27ffdea19ce7b674e9c -28648,0xc0748d32586f072e253565a791a72769db3a9f15 -28663,0xc264f05a94e0b9264eab82adcacba7289b9d8cea -28657,0xc6f01a650ce072a6afe028cd5ab37ccb974faa6e -28644,0xc9d2510c0c897bd08a893208783f8edd00db6d16 -28671,0xccd0645cb84aaad24628606581f4825825dd636c -28661,0xd04fcc255aadf72eeef511454501b5ece7fa0dba -28700,0xd1261634ef4255beb11ed38a35ebde5897b8532e -28677,0xd3b2a3a3faef1501755b290089d4daafdb6167c7 -28664,0xd8be5b63938e224415a4cd0d269034bc868347c2 -28665,0xdb7e4d4dd546a6df6f9a312761e1b63c2036a782 -28668,0xe532f879e19e3b28bcf101b1a15076f431714839 -28696,0xe546a7c1db82a1637ef5722e71eb06f483d2de0b -28635,0xe7e63c4494730c67878e6e11ad67e68ce4ad85f7 -28692,0xeb8ff19066415e797c213faa7d16161401c754cf -28647,0xed85bc160208410d122b14bef94a561701668256 -28656,0xed8877f8536781d2fc40c1e0054cbeb8fd960ee4 -28676,0xefaac5f54aabcf6124e8d15fcda717f4df9e6696 -28686,0xf26055894aeaae23d136defaa355a041a43d7dfd -28639,0xf354d46181e835441147ed102ab6b6cef0bdecf3 -28695,0xf762c3fc745948ff49a3da00ccdc6b755e44305e -28659,0xf8ef7c72ae34b21045c0c24f93b7d8a6f68f2f7e -28645,0xfa4062c4251da980575024f747646068333fa9c5 -28819,0x0509474f102b5cd3f1f09e1e91feb25938ef0f17 -28806,0x06292de88adb3b1557b034ebb1c367e65ab93e4c -28811,0x0d71d18126e03646eb09fec929e2ae87b7cae69d -28813,0x1619de6b6b20ed217a58d00f37b9d47c7663feca -28812,0x1b6a49b01176020b153d0cd5bc90ef2b75676f00 -28825,0x1bba8683e28078306db88743a387eb34fb5f34ed -28803,0x23c748fef17518b8de55065338d7fa20327472eb -28824,0x253be9f0e2a8749df6ac7466d096b264788745e0 -28816,0x3b53d2c7b44d40be05fa5e2309ffeb6eb2492d88 -28807,0x479ec366ae4ec016ce25b918bdea8f78d4fa5dd8 -28815,0x50ec9b92b3bd46969b1ae5e10543ca6fb49fba4f -28822,0x51af61b7026a0ae369acc8652ab2fb637f4689f7 -28821,0x61f85ff2a2f4289be4bb9b72fc7010b3142b5f41 -28820,0x69663eb604c7d534e9149fa855d89d9104e75363 -28805,0x6de33698e9e9b787e09d3bd7771ef63557e148bb -28827,0x9d39fc627a6d9d9f8c831c16995b209548cc3401 -28829,0xafdb9c40c7144022811f034ee07ce2e110093fe6 -28823,0xbcfef6bb4597e724d720735d32a9249e0640aa11 -28826,0xd51a026563583315a533f3910493e27187417d3f -28810,0xe3652e25f549819309d1d6fa0ce839c581a2de93 -28817,0xe80e0b928f8f3024dfefa56e7608bc3baf389881 -28804,0xea129ae043c4cb73dcb241aaa074f9e667641ba0 -28828,0xeb8bbe7599715d4a9b5bbcc53aef742fe008d6b9 -28818,0xf6c5d7da1654d9bbde0d25a5fd6776b37a2ad881 -28809,0xfd951e71a8ec5c03ad6139d35f800374ba500fa8 -28808,0xff86404162bf14fe50cedcf82a56941dc5f48c77 -28996,0x69ee459ca98cbdecf9156b041ee1621513aef0c6 -29099,0x00ffc95a1a63dbd48a1e7397a3b051ea9e3f5be9 -29095,0x0a57c6fe0ce8c165e9308f585d525a7885cae3ad -29090,0x0b2c4041d3453af54fe94cca505075f21334ff7e -29081,0x1b6bf7ab4163f9a7c1d4ecb36299525048083b5e -29094,0x1c4ceb52ab54a35f9d03fcc156a7c57f965e081e -29089,0x223fcb5cc448d16584e058be37ddd3abe4c29373 -29104,0x23b0117c3d2705d43c5d70b87ef812612e77d43e -29091,0x2e6e76df432c2217e5f9a31a4303744181fdfe14 -29100,0x2ebcc096594ee5c0bf851dcc023c0388dc7e9022 -29085,0x2f7b05742f7fa30a2c4f8c7ec944d5a4918f7741 -29102,0x3a7f44c924fdc2b2c7e89733c44edf535577d576 -29082,0x3c4fe0db16c9b521480c43856ba3196a9fa50e08 -29092,0x4b78ebd0206adc4103fd6a4efaa5b1a02d091bcc -29105,0x583371d98dc1ec18c3cbea483dfbbb29ac50b065 -29103,0x5d9309cf4bd8a7aaf0daf6f5e92d6dfb67e3c90f -29088,0x707804a9c83565199f93dbc21114cf05d62c0181 -29097,0x7af2675de2e3399c7639bb5c7ba98e2a667e3738 -29087,0x8ea6f0576ac7ee21f6b3a45c257fa35c796715ab -29079,0x96788c2546b98f071caaf1551afec65d4a99529f -29084,0xa1a910d6a2a095b41ad981fc7703db3e892e934a -29086,0xad64b3fdbe3a5f9143a27b5c4d6368aa1985d20d -29080,0xb79f25e8a6a3864937baeb82bc3aad16b4f57dc7 -29098,0xc0d75215cd57cb6cbfc3657502313329e93d9063 -29106,0xc23ac2da34e1b1debbd0f777694dc5b517631bea -29078,0xdf1a0846921c7ff01e5b3f56c06f50fb0d843e38 -29083,0xe9f64b3e40c50a6677f6527075d5924d52f57692 -29093,0xf1e61d7b21fcc5390f2b717f5897d717f694bf85 -29101,0xfabd5d0dc46d460a2cf85241409b9baf74decc30 -29096,0xfbfdb1e0728f7f7631a27c19b40d5b0fbd21ab18 -29116,0x017ff87ab312301ade54f7cf9cc5aea28c9de024 -29124,0x020bc28df8904bba7f432519d5fc86d494e708dd -29130,0x11e4bed429b239a1a0c594adeb71b99e8fa1011a -29125,0x1f5330323b12c62eb1258fa227d88c8e63fdb855 -29129,0x226d8bfb4da78ddc5bd8fd6c1532c58e88f9fd34 -29119,0x29414ec76d79ff238e5e773322799d1c7ca2443f -29126,0x4685d436ad9c7c7fc21e600c6dba6b6bb5693dc5 -29134,0x57c37416c853c2703bbbf7eabb382a9cedb523d8 -29132,0x68d97b7a961a5239b9f911da8deb57f6ef6e5e28 -29127,0x6b2b2349d5da2d260f5819cbff767ae090e9a600 -29120,0x77b3aa18d89ea24e4af7bb9bc221d49fd5534c19 -29123,0x87dd4a7ad23b95cd9ff9c26b5cf325905caf8663 -29122,0x91854b4a3c8379d5baa39f1c0917332e58bfbc7b -29133,0x9d1853a4d36bc5b0a0c4a852f3f4e439541e7cc3 -29121,0xa39e3d97df5ccc9e42ee1897bcb23a550afa5b65 -29131,0xa6b2d37b171e626ee5227322fead4272682fbf89 -29128,0xbe4a5438ad89311d8c67882175d0ffcc65dc9c03 -29117,0xd01a5051253007ae0b7123b50410e3b5a3f6cf95 -29118,0xea5fed6adb6ea4c8a2e2622de720f9ced0b22a17 -29150,0x060cb087a9730e13aa191f31a6d86bff8dfcdcc0 -29152,0x13dfeff85779118136bb9826dcad8f3bd25153a3 -29146,0x18878df23e2a36f81e820e4b47b4a40576d3159c -29148,0x22ae99d07584a2ae1af748de573c83f1b9cdb4c0 -29147,0x623164a9ee2556d524b08f34f1d2389d7b4e1a1c -29151,0xb1fa0ac44d399b778b14af0aaf4bcf8af3437ad1 -29149,0xbc9ee0d911739cbc72cd094ada26f56e0c49eeae -29177,0xdcfeaa47272738d10f88f3d80bb28212b1fa9c1a -29176,0xdebdacd6957db199d8b47b361ea4f651667cf5eb -29299,0xcd487bbd5f6f9afd3cea637a1803b6e8d71c958a -29317,0x011a7b7ae1b7a4645c0341f980d0242e152bf397 -29312,0x03bba86e68c7dd733703cbcd44072082af702d85 -29310,0x158f513096923ff2d3aab2bcf4478536de6725e2 -29315,0x15ee120fd69bec86c1d38502299af7366a41d1a6 -29309,0x30ee8faef6772b62cd595ccf7749683cfd2d0d9f -29313,0x3ef241d9ae02f2253d8a1bf0b35d68eab9925b40 -29316,0x4f9ba083132033594326a69fa90ad3f1a84e3a0e -29311,0x5029c236320b8f15ef0a657054b84d90bfbeded3 -29319,0x581ee9ad4a8f5767b0b17d3c350e4c01366004b6 -29314,0x94e92b32adf955494606b0119fd8a0d6f607de7f -29318,0xa61f98fd2bd043bb7c2d560b67204334b0641b92 -29321,0xbb5ccf0da6486319fccd03409267d96ad97d0ea5 -29308,0xc98b98d17435aa00830c87ea02474c5007e1f272 -29320,0xecbaffaa5c4e94219f4c166dac9d4a1520cad827 -29391,0x126be6a9ec71a1fea19d2288ba2ce4cdc0facb68 -29387,0x2766ec868b1fd3461b956947dd41d5bd5529bd89 -29378,0x3678b953e87e8137ae220a1560fa80401e0200ad -29374,0x3a3e4dae2d1acd16028d12fd59044e8f2c452c53 -29385,0x451f4594e12ac35929a909ce32fc6afd3eb7e50c -29375,0x4c5ccff7fa764ce150f07dd9ec9805a7c9260be1 -29381,0x6ae108dde26de9ca70e70defb1913c8726136b37 -29390,0x856cb5c3cbbe9e2e21293a644aa1f9363cee11e8 -29384,0x9bcf58cd6d22c532e77dcaa196fc45589042bc5b -29389,0x9c0dd7c93139ca6ab62470f65c173f1eefdc9ef9 -29382,0xaf8fe2c2f6ae85dde4599b9baba02f59fb736443 -29377,0xb4778f5aefeb4605ed96e893417271d4a55e32ee -29386,0xb8eaa2feebf4605b1f4212d1a308641cd3186874 -29383,0xd016e072642030fa0813ff62acda5c8feb17c448 -29392,0xd881d88a575d9d6f1b6f062d8d494588abbb0c41 -29376,0xe1a9afffadb8e095ec861272a040bd488b24c085 -29373,0xe3f8154da2aedd10f3cce0ec2bb0da1f1dec2b56 -29388,0xf18d745bb6db6dd5412113eba2c570f330f86835 -29380,0xf46c7d442bc9e279d1e7bc4bfa57813d5f32c239 -29379,0xfac79f6b18ade2e9973b590472ea9f079af9668e -29541,0x04899d51747c09dd60ff54bd4ad060235875ff80 -29521,0x09523acc78dd297ae5da00ddebe6f10ebf784565 -29531,0x11234db1ed4f9c6191028a136f45f6c9f401241f -29573,0x19dfef0a828eec0c85fbb335aa65437417390b85 -29547,0x1cbbe6cea5e4fe7d69c586b4e362a646deef105d -29570,0x230a59f4d9adc147480f03b0d3fffecd56c3289a -29534,0x239e55f427d44c3cc793f49bfb507ebe76638a2b -29525,0x26743984e3357efc59f2fd6c1afdc310335a61c9 -29560,0x299dcdf14350999496204c141a0c20a29d71af3e -29538,0x3b8ca519122cdd8efb272b0d3085453404b25bd0 -29543,0x40f7218fa50ead995c4343eb0bb46dd414f9da7a -29526,0x41b953164995c11c81da73d212ed8af25741b7ac -29550,0x453072d727aae7df68cb5c94fe48cc4170935fd8 -29574,0x4c32a8a8fda4e24139b51b456b42290f51d6a1c4 -29540,0x534c0c704a09fc70004c2616349ad46b30072d68 -29518,0x543a21679f2aad98b04c6850d227812bace171e8 -29519,0x548e2f8114ddf1c796c37e83d26db9b1cf215a62 -29549,0x56017074321ce77e2f88f2233830f5b29d4cf595 -29553,0x59d59edc3546e740adb102e1b9593692c96d69c8 -29542,0x5b043a12936777cf18fc7ba259673c795de932a6 -29537,0x5d5629fc838f5ea67d14264b7316ffd4294b0998 -29514,0x5fb05e72d395dfb33edf153c87f2dcfe081b8d4c -29527,0x6732d651eea0bc98fcf4eff8b62e0cdcb0064f4b -29559,0x6f5a2ee11e7a772aeb5114a20d0d7c0ff61eb8a0 -29558,0x72d07d7dca67b8a406ad1ec34ce969c90bfee768 -29572,0x7396f99b48e7436b152427bfa3dd6aa8c7c6d05b -29564,0x79d0011892fe558fc5a4ec7d4ca5db59069f460f -29571,0x7adbdabaa80f654568421887c12f09e0c7bd9629 -29545,0x80dd2b80fbcfb06505a301d732322e987380ecd6 -29536,0x8605c9f58a64fd60eb01ccaa99a1f7524bc37286 -29535,0x8ab784368a1883da90d8513b48801e2db1cb2d5d -29530,0x8e9aa87e45e92bad84d5f8dd1bff34fb92637de9 -29557,0x8f42adbba1b16eaae3bb5754915e0d06059add75 -29551,0x974d3ff709d84ba44cde3257c0b5b0b14c081ce9 -29548,0x9c07adc41324613a73f00bea5cd92860d5b581ad -29516,0x9f3991e247797f684672ad685ab955b40f02112e -29544,0x9fa8a5977ef1d852602ae1960971d7ac0f0aaa1a -29520,0xa331d84ec860bf466b4cdccfb4ac09a1b43f3ae6 -29533,0xa9023b9cd1f2737429d03e0df6d93a8453b5e661 -29524,0xacaac3e6d6df918bf3c809dfc7d42de0e4a72d4c -29552,0xb79c16efd530e46b7a1499c1854f13f4bd10f4df -29515,0xbb92d8db93b188eda1e16b1e19f27d0308bf8e02 -29554,0xbef13d1e54d0c79da8b0ad704883e1cea7eb2100 -29529,0xc128468b7ce63ea702c1f104d55a2566b13d3abd -29517,0xc128a9954e6c874ea3d62ce62b468ba073093f25 -29546,0xca4cdc9eed85d9ce0b8eef74457480364068af9e -29539,0xca96c4f198d343e251b1a01f3eba061ef3da73c1 -29532,0xcd7f93c86f30b90a181f81934fa4f281c7bff246 -29528,0xd50c0e1abe53f706c41b400c587ddebcadbf8045 -29523,0xd7fad3bd59d6477cbe1be7f646f7f1ba25b230f8 -29555,0xe306aab50700dfa9bdcf0dc2f69ee49aebde5825 -29522,0xed86ff0c507d3af5f35d3523b77c17415fcffacb -29556,0xf070996cf89cd3d2582705fc269f2c800e9a6a21 -29563,0xf0e5bb723460fcc9132a1b37c7f38afabb1b6ecc -29565,0xf426db04fa31001ec51a7910024fd34c925fa6d7 -29562,0xfe18c7c70b0a2c6541bede0367124278bc345dc8 -29917,0x0076b62c44a1834205d4d7de94c90a123f614090 -29757,0x00acfd3420cd652500f5ba1b90ac7c19f694c5c3 -29685,0x00dd62424a79c971d6f6a10bac531f33185e6d5b -30157,0x0105099cd3f3cccb091fda47d0ce679854e70053 -29723,0x01bff9f1b56c76a619327f681a734c48daa80e74 -30056,0x01d9cfb8a9d43013a1fdc925640412d8d2d900f0 -29833,0x020eb14f6c45d1900d2d57224a641e87a14e1742 -29854,0x0232aaee24235e21d06c0ceed51216005b9edbff -29773,0x0234ed3ed84639e930e7e49be557e2ed83bde32e -29910,0x02422d83321ff56cea22bed0eb3ccd6698ee9f3b -30071,0x02bb1ff1729ba1ba48392f6345d2472dcfa8c9d9 -30158,0x0322d5d8fe8816b532491da397f30a3084dd4ad3 -30144,0x03360fe329f44c6b0be4d8c89d2fd4c0151b226e -29873,0x033ca6926dff4f6848983731087907dcc9ddbee2 -29979,0x03480366701bac978fab9db10a0cced8146f6d49 -29669,0x03a5fea242ac9064e23c542b6b6020ab0e647400 -29884,0x044e87f30bd9bd961c04028ac69155493e1b9ed0 -29679,0x0495a700407975b2641fa61aef5ccd0106f525cc -29945,0x0513063422a956742fb690ccc7a635ec837e412c -29661,0x0521b9c8d92d39d0cabe7bb3dcad72d474ecdc7b -29866,0x052b006a2cfb2523042b3041f15adc7fa5356312 -30057,0x05cc7f3aad0cecbf926d2f4a534f55c19035718e -29780,0x062ac428a0fc55a8435499bf11a9255031ba2d90 -29981,0x062f29e2b5bd99523dd3508207eb2c2cbaf98d50 -29741,0x07d4913a1b4cc963879a2c588f3db3aac895c8b0 -30060,0x0892a178c363b4739e5ac89e9155b9c30214c0c0 -29922,0x0921e4fa2f85b6461fa83961d28dc7e1f9a32b40 -30038,0x0a2289a7ee8a1f2a6b0e2320f47d8d0ff143f436 -29870,0x0a9c850d95796e32e6ebe4966420e435ed86d3d9 -29941,0x0aa4ce32107b7101770566c4c0d7ecb1f9d8a07c -29664,0x0b76632e6f7abd7c356d185a756a36177a4b954c -29765,0x0b8caac3898e28165ba8fbb34f558b5464e0cf76 -29925,0x0bf7616889d0ae18382d9715eac00a3302e9ab92 -30083,0x0c560e328dac2fd79213828e567ee9755285f2a4 -29863,0x0da809db34dd702496a1b36254715ebaac456b00 -29934,0x0de766c30a3c02707e19a116e7d78b95b7aebb48 -29900,0x0e8a8936cb42b40af9bcc56c81065f5d20b9cb6a -30054,0x0ec8f519931e922d14131d7a68731f4fb11b9482 -29634,0x0f5333983d4b59aa5fe692fac8475dab924ec7ac -30146,0x0fa0f1d9d1533e4d74730aeacf83e313d1966350 -30129,0x0fb22a54a749c3babeed908e04a99d27725a2fae -30047,0x0fb938132d4db064d4869b2a68f8e0f366fe1371 -29815,0x1078fa50278c01eb490cca661aac6c5af99dd190 -29786,0x107dbf9c9c0ef2df114159e5c7dc2baf7c444cff -30154,0x118ac0e9854eff2f4ad47bb2695775c061c6e72e -29811,0x1198f78efd67dfc917510aaa07d49545f4b24f11 -29995,0x1236d3f26ec5b5824973fbecb356a9a5935b6a1d -29899,0x13c6bcc2411861a31dcdc2f990ddbe2325482222 -30012,0x14c6cd1a9b3d5684c8fdd0883a8b14b90f07d35c -29888,0x154656fdc48413de57565a364f828ad2818696cc -30152,0x159390e7be87a0b61fe0ad10db235690b90d2de0 -30032,0x15a5cad774b8392c11ec85e54bb05dc9fdc2134c -29684,0x165f290c9861249c59d2c1302a5c120a6a102f71 -30162,0x16ab7178b1b062a326c007a52e32a67218151b59 -29733,0x1817c0fbdeaef34ffe296f5fe8606c074b078553 -29783,0x18a054ccc4c8fd8c5ce5a266d29773dd23b1c18e -29642,0x18ecce7f6b8bb54933643751d3c2d4b3ea034463 -29657,0x1a00e13cd8cbc61c8c342afd46592f4234172005 -29985,0x1a3e5557039763425b00a2e1b0eb767b01d64756 -29958,0x1a9fda6626097e9ded33204e485def332f2b665b -29868,0x1b18ca56128eaf1c00d73019cbfdad0c3bc673b7 -29739,0x1b195fef1989f0901f1af0a8ff48987e39d84f7d -29771,0x1beca12fabbe9b6207e69742d3c2cfef348e2666 -29720,0x1c45f783fcbab05abbb93e421bc55b38b1d6c6cb -29961,0x1cfbf871f15171b1163ac043fe390290a90eedef -29843,0x1d08b64c9cf3063add347efdcd311df341384426 -29680,0x1d11b9babbe531c9bc2d34fccdd53a43f7449658 -30076,0x1d46ee713495f9f22d2498d050c9c9fddc9c6889 -29777,0x1dd0d6508ff1fc1a2433b4d37d7efb5680c6455e -29809,0x1e7317b6f5bbae0c8eb04a48902e3a6562c33b2c -29658,0x1ea73427874dccb2e01c53e25a21e92ada3dc65b -30102,0x1ed617c70b29e00f5ab729947dcb73809fcc8ca0 -29950,0x1f987b36cc15f39357616049e3b28830fd68f5d1 -29885,0x200a26572948e689d6bd19b3d3c03d87a6f3baca -29942,0x211230c44eda453157f1d094224123205766a37b -29983,0x216eee15d1e3faad34181f66dd0b665f556a638d -29889,0x21e14a13634d18857fedc01a6b7bf5b25aed087b -29992,0x21fd50161b3f51156ef18e0845f91ed3f10896b0 -30101,0x2232455bf4622002c1416153ee59fd32b239863b -29643,0x2275527112c94733081f2893de25c85f252defab -29631,0x22e277cb2961bdf00fdc6b2cb351d4413ae6ba30 -29696,0x23343d47111abc4b1699fed61276c06ff8a0d081 -30049,0x234b26b224c9f7bf571846a6b15623d3b0c81f3c -29635,0x235761c80ba5128266073c8738f0b5150e349323 -29993,0x240233b447d841c84bdfae17bde759e8c227a753 -30106,0x241cd1944dbafaa9fe86106d93dd2bb549ea20a4 -30015,0x2429c300be41c3de2cbb894a7939ff3b49166001 -29766,0x24569e59375d65817e72ecc55517aed168662e92 -29742,0x2489f2f7f972ca7ee6436666a3ab0aafb5a06c7b -29754,0x24edf005a60855efb079f8e22067e89b3ad07d7b -29779,0x25de69da4469a96974fae79d0c41366a63317fdc -29931,0x25e66f746254cd7582e65ef7de1f10d8883f4640 -30043,0x2649b0d997fc267154a542540aa90c09cbc7c480 -30085,0x27a03cdab67d59e4e16082d79f4057aca98938cf -30131,0x280075d2e08661bd67f8e0d5fa95c5ee655ad8ed -29730,0x2809385bdb8a968c3d0a20b453be7103abe00f06 -29874,0x2823709d7879b01c7d7d41bb0804a81a2611789a -30116,0x286d0acde6bfa4e624a102e8d68367b0dc582e1a -29879,0x28f4cc9552f578b5c6e3a9204d6277cba49a9416 -29805,0x29c4c22ec7df7e936cb3e6c4a7e5eae751c7d86b -29937,0x2a593c9dd08ae58df82dfdeb67bf7ba666a233e0 -29817,0x2ab8add4e367cb2ad9a497155031dad49a2432ed -30068,0x2b037456b95d37fb302ad361c2c0f50697ac37c9 -29826,0x2b6730083f2156bea9a361bf36c95f4c3973d49c -30027,0x2b6a44a6ff520aaf64588cd2af2d71a37ada0730 -30061,0x2b9e8f5e2d7e85936bdbefd12923c643b71305b6 -29929,0x2bb87d26bb4fbcad592d1d0be6665eda55d936bc -30156,0x2bbf9cfbda4293fa446e915aa12adc52ea8d5d53 -29705,0x2bdfbd329984cf0dc9027734681a16f542cf3bb4 -29637,0x2c3d263b560b2700fcf19e78f2ebd4d59eb7b3c5 -30111,0x2d78a2bbfa71c268bee36011f944901af9b9d351 -29681,0x2d9a90b88367360fb09b0d0d7d0ea0f99d688e5c -29652,0x2e0a7064a5ba7b9b7499997d91885badd5fc45f6 -29814,0x2e2cc6efcf8b6193a32a92b33747c081fe44bf1d -29896,0x2f0865133ce3ddb78953fd3b4f6505ef3adb2107 -29718,0x2ff34b3f4286623099c4d40afa162282cbe0e347 -30119,0x300c1617d77b0736a4e50ce49ef747024518839d -29760,0x3040387c46e21b453e4a8f648528368b9066628b -29819,0x3094ab4af54f5208b867125b5ccecc94bc17cbb6 -30009,0x3116a146dc16ea2d0926c430d07d2a185043236b -30055,0x3116c67a1758f82b91bb97ea503506b409a90ae1 -29656,0x3142d9195171f08f659f7a0cf3c3cc75c0ebb756 -29812,0x31616bfa57b62db91c54e3e4ced19192e8fd08ab -29956,0x31fdc28f477a8026d35ca53c8c014f7996c89501 -29946,0x3201d664e75297f407843e0469fa256e05155911 -29697,0x323818ba51c7ff001cc1daafb6143a9ab7f292a9 -29867,0x3246893c1f8c1bf928121094ea4558c7b93bdc9a -29715,0x32b2ff41e72ee243c8835bd44476375afe3a38bb -30114,0x32c9a4a4d28192892639e0373609697480d81ce5 -29686,0x32e0b5400fa4f94454d37973f172f326bb3d430b -29692,0x33a2341da45447300f6c7bbf6bef48e70d026cb1 -30021,0x340443fd271dab123d9bd209c8dcd25894b7711d -29732,0x34179b78e61db0c67e073b2f829cb71e9d2871c5 -30003,0x346967b0c7d9fd394cc7b3a9a7f10a6729c6debe -29649,0x34e5b7b4f9b7ff85e311792a2613f5308f25f379 -29965,0x35197a9816a0758c09d07a2f87dfa88d2a0fc607 -29822,0x3532b6f723948ef39d5dcf44c16855239af81082 -29988,0x358b7d1a3b7e5c508c40756242f55991a354cd41 -30120,0x367a0cb94ae395e03e421ee6b95ae35263501386 -30121,0x37d12d206c8f1e2c7b4c82a42dfd4da2fd266f15 -30160,0x37ef854cda76819edcff8f0a93bd449c6767a653 -29872,0x3983c50ff4cd25b43a335d63839b1e36c7930d41 -29891,0x3a027fccf5fbb8e26eb5541a68a6f19405f09674 -29975,0x3a579290e4ee2647beb2227bddc9e6a6046636b4 -29744,0x3a847e91d632c72a8247727cc09c63a6847f70b4 -29818,0x3a8fc02909cac1a317e95d9e2717977fc52ef36a -29857,0x3ae008c5cbb576fa91e06c81728c2fe46c7f03ed -29984,0x3af60c18c5c4f57dae157e0418d5d1c5a2f4fa91 -30004,0x3b6ed6a53a9a357a397fbc7a04c96ba8b4aebed2 -30082,0x3b9292d78b0aa958f2029e082ed104096dc20cd5 -29761,0x3cd5ae887ddf78c58c9c1a063eb343f942dbbce8 -30072,0x3dd36e64784fdaee7bd202c78322a0d8eb1bb32b -29860,0x3fa638e7b83a82016c7b18a312c991c84c35a5cb -30148,0x3fca8c2b5189329cf20f71782a989bda3826b7e5 -29858,0x40324434a0b53dd1ed167ba30dcb6b4bd7a9536d -29850,0x405ee7f4f067604b787346bc22acb66b06b15a4b -29846,0x406109987cfabf034e6b0db6350306b6348e3729 -29729,0x41c713ed0966b0cb87d1e33dd70d8e497a3b76fe -29701,0x4287b5e79436632170a1175c9c7babae1c8676ba -29759,0x43f6de3d9fb0d5eed93d7e7e14a8a526b98f8a58 -29948,0x442ca31de7e6732455e60e3641ac076fa7a0905f -29713,0x448423356726dd7dc168e714c80addb077f83738 -29706,0x44baebeb626f1ad048f0c15e84bb24196f214c23 -30064,0x44d5338bcaa6df44d26190797714e31559e6d716 -30080,0x4502f35aa67d00d4c2ca49c2e9f11901c4b317c0 -30107,0x4723b375cce9feef6fe97901a0d18d7a8d28ff02 -29719,0x477030027a0f5a883a92975e18d63b1ab7aef4b9 -30050,0x47ef4f26ddffe13283a1b77ec11c203e17cce7b0 -29689,0x48ae9c58af01b3ddd522fc3e161ac4318964c4ee -29914,0x48b3edf0d7412b11c232bd9a5114b590b7f28134 -29949,0x491e09eb736c0c4cd85df694a22d1c0dec8c4c25 -29788,0x4925ebaa523b2c2f66f980c7b8bb68207622bc3f -29796,0x49302e520187e44ac2a545d39e3dc2827859f5b7 -30130,0x499287c138aed9a107d748ddfd53668d104378b1 -29883,0x49e5bef46f59217962d166245ebaf690712a03a9 -30044,0x4a6f369b1e2ef92c991d96831f3e1b4006a4c661 -29806,0x4ab405af608b69eec4bff6667dc16d76116ea633 -30048,0x4b0a66dee9ff557f9b0c25c3e57086495f570e65 -30115,0x4bafb71d765f9adde0930da4002002648163b1aa -29663,0x4bbde7dbd03b8df31d1c3918bfa1b73d15fc4c35 -29962,0x4c16497acb2a493778831afb7818d726bbfe914d -29700,0x4d153f47f03c237f6360a6eccd185b4ae09c63d0 -29775,0x4d75ba5306875017765faa952081771235ebaeb5 -30040,0x4dd62fc76fd57175151777e845dfe9e981b87ae7 -29921,0x4ed766af5b30a4695f7b247ae1bb0f6b34bc6378 -30118,0x4eff281247576ec156d7271c46d9ef0f43c6c265 -30026,0x4fc9d78613b20c8c91121de91a35887f7139da68 -30045,0x4fcdebab04f0e7f64e992861fd948df2753b0a56 -29997,0x4fdaf14fbaf0569ea35050843bda9288f7198602 -30108,0x5064c531af73beee8e7b3835de289965b34cc189 -29893,0x50790c91da8809a3f6156da73070a598441e6e0d -29803,0x5090dc83607036e93412b7739ccf3338086e885e -30037,0x50d6945632e6d89cc9c9d38e1fe5b6250f6213af -29924,0x517b671fa1134f393d305d2fc466c4bbae6a738d -29650,0x51d95884f64abc5a38d79607b0fbecb4b5840914 -30065,0x51e61ae3da5fa7eea0310a7fb69c88e0e661fbc0 -30099,0x522871dc841ba76fd2962a47be5bf818fd3463d6 -29906,0x526c0db65206c03bce92b4b074349183c42eebfe -30109,0x528beb08ad290349677a48a2188b8e22b90f1b95 -29844,0x528cf803d9224e309dbe96a644484752613675f7 -29690,0x533b886320e197268436346a1001717f6582ed0a -29676,0x5380d7d83297e1c402bed404918256d0b815c9f5 -30084,0x548af6cb30e88c4c9b0a3fab1d3325dc9f3387b7 -29740,0x54f666a9d81b2d422d64c2ffbd20ccc6250214f5 -29749,0x55782bdf08e0e03df79d3a5430762a3fcb2dcb1b -29630,0x55f46144bc62e9af4badb71842b62162e2194e90 -29675,0x579640e5d9f4e20fc1231ec9620385f75b5784a5 -30058,0x57d64d41ca26fc2860c10c94babbf461785d12bd -30010,0x587c3e2e17c59b09b120fc2d27e0ead6edd2c71d -29987,0x588fa08029f8fe7881486f22e259885e0a073d6f -29710,0x5990002594b13e174885ba4d4ec15b8a8a4485bb -29918,0x59b2e7f6f5f745fd33dac83153eae8e42440a981 -29836,0x59f82727ce91a5784932a9340adefcb63068d869 -29968,0x5ab303a1027d46f44aa2e801696f1f0e87f91981 -29646,0x5af5d27f10dd8fb317d7766a4457becd3e7cc1c7 -29785,0x5c7fd860fc0072cfef2cc4ab17cc7ff25b639d8b -29877,0x5cd57fe9d760e6302aba167993a0d1cc03895ed9 -29953,0x5ce238a82c38d21cb3561866fc33d9e0f7243af7 -29976,0x5d1fa921eb3758bff1753106cfb89c6ff20211dd -30046,0x5d51392a1c757d7d8ab8e1657a31f4dfed66ede2 -30005,0x5d871ab3171daaf1991e9715fbcee5fa34390cfa -29804,0x5d946a0efea185df89520ec0bee29d0c3295c447 -29912,0x5dda0d7ef00e0b3a30edf9ab1132d463d7a0b355 -30110,0x5ec7f2602c449b4ac851baba20d5839ca4783314 -29709,0x5edf6b341f73f9782c9afd105fe3fff6bd84d2e8 -29743,0x5eed03a90a130cdc130dd4093e722f455a69acff -29892,0x5ef08a4555125fcafc10a0faefb63562e06e68b8 -29695,0x601cef46e7e31bda332ae370f019e9d9ce69f01f -30159,0x60243ada6f1033d389cc3d77c019abafb0b73ff7 -30132,0x60351ade4e99641397aea1886403839ef71844c4 -29682,0x6095505639c0c143412bda33606664c9141d0d12 -29767,0x609c46a524cb376bcad5f32838760bc948050709 -30097,0x613f54c8836fd2c09b910869ac9d4de5e49db1d8 -29845,0x61645ae7bb524c2ea11cf90d673079ee2abbb961 -30023,0x617b09c47c3918207fa154b7b789a8e5cdc1680a -29802,0x618af86e6d2cdba9cf18e6482cc4c32853cbd807 -29969,0x61cc42c162e0b19f521b7ab963e0bd8cc219e8aa -29952,0x61f1d5c4ba453a5a6b555967d4271392aef0f71b -29665,0x621ab9a2ef4256a43defeb37f0a1eacb276bbf97 -29926,0x6347c23cd6283e619728854d44ff852a462fbaac -30155,0x63b4d85013e0bd955c663c8cb2d2eb9a604b9897 -29781,0x643dd1f9d43b210c4ba2648145757480cb664175 -30066,0x645e7208c1ab4d1758774d5141c5217c6cf63370 -30150,0x64ad2991e7f8ff832354a89e6bef9d006f2d6311 -29704,0x64fda2f7b42b14e2714175f5b0d4552b74770189 -29996,0x651f2652ef9ab426a2c41a690c2503b0a29257eb -29750,0x6598d84f2bec8eeb2e05f71f0a012d4aec21bda3 -29653,0x65bcbaa188318dd8cd4b894fbca627aeb845e620 -30098,0x65f8a09a1c3581a02c8788a6959652e32a87fc77 -29722,0x6709b167f862e2aed920c2d98bc89e976dbd0615 -29973,0x672584379ae3082b25f794e7224057e1b8b3553d -29878,0x687e3b9b8ce7e3014b41c0d722d65aa8862c4ea4 -30133,0x688724fb44cd7eabf209ca2b225880033e9563d2 -29728,0x68edb82ca0b6c1e623cf7e41e990df5702554862 -29688,0x6934eb0f454acaaa86db0ca8e1c4c91ea3f40e8b -30126,0x6992333e5df2364eb1b6cf14dc0c86eb0865d6e2 -30067,0x6b2cfabf0703d807784d11eee07759559edeff6a -29964,0x6b88a921785b593f05c83badaf8bf7ba9e077b3e -29782,0x6bbff53cc5c422ec01bcf00ddd81b68e7994e110 -29659,0x6dfc0e12643a23f0642945d4cc54fed72d0d5a37 -29717,0x6e1e6bee8f91827807add9b18fe901c7473f9da1 -29856,0x6e2dcebf8c14c72329f224a26edfd547d3c3e09c -29784,0x6e4d1e6b78f536db6868c80d8735b4bf1551e927 -29725,0x6e943481838160d86ac26f210fb451ab8e6f211c -30052,0x6f9bf80a43be27703ed0b740d93d3f2e87e43448 -29647,0x70051ba744889e67579f9a0d89737abb82cf5ae8 -29849,0x7091e04422d1d9be48c211cf2f818a7e88efd736 -30034,0x70e793220761418a18febcc556efb24a45da123c -30019,0x7163975705123bb18bb5f03d2c4c78329a3bd0b4 -29938,0x720feec2b3e15fe5e7ab29397679df928c76018d -29977,0x7381e46d99c94e2a8632e95c36017619df2e2986 -29960,0x7381ed41f6de418dde5e84b55590422a57917886 -29727,0x739c33c453f6449810b2997a320e5ea0b79fa438 -29861,0x73ceceb51fd9d0381256f7affedfe9ddc3e70fd7 -30112,0x75a59e8d6611e90e7a8e439cb59d9f78e738d16f -29824,0x75d7de965ce777abd11fe30a1d1e9b185171559f -29668,0x7680639e51155a2c7c9cc98872f0c12a15990241 -30017,0x76913cbc80df343bae78f66cddc8d275020a12fb -30033,0x7714f5e07a5e9344f708f063558d3438676d4b58 -29864,0x78b5ff763379012ce5ee38aa870fa545fa74f4eb -30041,0x79149b500f0d796aa7f85e0170d16c7e79bad3c5 -29903,0x794c7424a2672e805fe24d1b69e73c9d2ce565ad -29752,0x797932dc73caa8027a0827680780126633217b47 -29820,0x79807cce3e75dcdb8641c834e8065dd1cee12a2a -29902,0x7a114f8e4ee7b6edbb7fe297de1356c5068d2234 -29746,0x7a2658c72f1c64a9b00be5e6c5088471314f5340 -30039,0x7afcb83e45500f8f9d5934e6690a7de3e82ac3b4 -30018,0x7b4d6d79de44fe034f75bd4525a13aaefaf4597f -29703,0x7b554337fda6677e9d2e78a565ba74f4ff4e7c46 -30073,0x7bd070d71a7d22d679a1c993fe138f22abaa1145 -29848,0x7c7b7fbcca5699175003ecbe1b41e79f40385469 -29801,0x7ddb571cb40e5fed67b1bc0300f56f9c25cde175 -29980,0x7e0b4d598bd389f07677c8414576f2b25744d19d -29753,0x7e240c368272da4bfd77bf5dd0151c8cb57ab609 -29971,0x7e4b8afe26a20fb93561c811f1121bc1210bde37 -29978,0x7e6958dd0437f687ad54557ba1ab15e45373478f -29916,0x7ee71053102d54fc843baebaf07277c2b6db64f1 -29798,0x80db135336cafde6f712b92109b076b3ee5ca59c -29954,0x81b2c85bde04b34c9b8b7e5c6bc925a2eed8e810 -29847,0x81e43596156c3036ae73b8bf9d29198086506ee5 -29638,0x81f2f132465ffb4b3c5d11f3d94582cc5c281c04 -29764,0x820ae7bf39792d7ce7befc70b0172f4d267f1938 -29670,0x829288ac86a29f1bb9561719c61eb38ca289030a -29829,0x8396f3d25d07531a80770ce3dea025932c4953f7 -29841,0x83ff748c4dad196944ded62c998ddc87a57a4198 -30042,0x852c903e5bc93f526ab254bfee0c1dff23cf82ab -29897,0x8552cc6488f2b463b867ac338b5ce97a6a41b5e7 -29772,0x856c5d1d9d00f38119efe66b413b281b983dfb8a -30087,0x85850870ab8716d652db4de4733cdbe8c41b47ca -29991,0x85f287c860f02bac3ae5023efb547b6b7aa4d9ed -30095,0x872103ee5d8e1b4e41284355b27b3d20afc3c08a -29667,0x88b549ba984d3c7725f4dc32abb5389d127f7a6f -30074,0x8a69309bb573030effeb36787651121c25646d58 -29756,0x8b20ebe6b3b5be888ba815c37724150d17083633 -29800,0x8b2b853c7b4ea9e54a5506bbc2ffa53a9fe17c23 -29791,0x8bb50940c443b832fc4079e4db53447c8aef5b90 -29920,0x8e2cdf8c6477439b7c989e86b917d80871b92339 -29834,0x8e829cfc629fcb61c1508a54d232af4612e48cc9 -29794,0x8eb23a3010795574ee3dd101843dc90bd63b5099 -29708,0x8f1d9cccd458891f87ab5b1c45fa14757e71f7f1 -30145,0x8f5454526fe627dc3c6d926cfc76f63642d8641f -30113,0x8f6a97a42ab597107f1f996079643c8f25fc50de -29672,0x8f755873546f4d0edf7d41ff8604c8a632113eb7 -30089,0x8f8126c13bba042084baa81890ef17bb61b4da27 -29827,0x9048b485006754d29f22818b0aad5aea2f22d990 -30088,0x90544834e26a33739eb014bd045cc72c1228f145 -29842,0x908657ad946c8a3b5b9fc5ad47e9617ea5c05354 -30078,0x90f2922607bc7a29db0cb6f61f09c6c8d244269e -29932,0x91ecd79fc53c6b1c1c081570c463c3c8259dd148 -29813,0x920abfa9d1c99be993e5399634c6d5e1be55a3f4 -29886,0x922f8807e781739ddefee51df990457b522cbcf5 -29936,0x92651e1bbfe0f8423119afc4b0610c747fa125a1 -29990,0x92e586d7db14483c103c2e0fe6a596f8b55da752 -29882,0x936aad8da1364b0940d0e882d3d468f4e0f65421 -29776,0x947891249a02c923787f6c2bedcf10ad959eb3de -30125,0x95a0d9c6de7fb43616e84371ee9135a1f6786736 -29807,0x96def3e0f4bebe3acdf652f09c27adf4f4a069fe -30063,0x96f990d1aaf83b09a4ba3d22caab0377a058c84f -30031,0x979a73011e7ab17363d38bee7cf0e4b5032c793e -30051,0x99097e6c293982f0e05937fdfb45f69ef8914f0e -30000,0x994ab71f95a8de4daaf6de3d9862284693fb2bdf -29957,0x9a10cc2024744681fbce1f93aeee3fd7863b1a12 -30030,0x9a785d94ddfe903ff54b7f6eab824029c0c4d59a -29677,0x9ae0b186db2378a33d63c5d8af59272db3de6b99 -29831,0x9b50b06b81f033ca86d70f0a44f30bd7e0155737 -29655,0x9cc1cda6e682a902c4e4409065ad5de85cf9d89a -30153,0x9cf26f33095b33e2c52a3d48c2a63be884bee95d -29898,0x9da5a072804ee44b6259181bde3f5393aeef2bec -29880,0x9da9f3c6c45f1160b53d395b0a982aeee1d212fe -30127,0x9f24c36585cbcbd52116952bfddf8568a1c584dc -29810,0x9f47d327bb88a57dbbbe7ff350c1ef15bc9724a0 -30142,0x9f7ed443f5c89b6f2d4fa361d55594e0387dbf57 -29640,0x9fe2921fe00e48b88bde16102627e750924b96bb -30020,0xa0a4bd251ccef8211c2d4700c81451f5a38ff983 -29909,0xa1dfd44c90b7ce937fc450fe19b691e72ee3a7d2 -30024,0xa35f008e16cc06ddb3302a65c54df584fbd82dd4 -29876,0xa4404d27cae102c00ab95654f70252c82b22260d -29683,0xa448e9833095ad50693b025c275f48b271ade882 -29711,0xa5dca91271a892fde7fff1ed3b81bec4c08659a3 -29859,0xa648630bd3c5a5fac65cd60bd61964f16bc00e88 -30022,0xa6aedc8de2c4fd14ede78b9f0d96586df2668a1f -30090,0xa77ae94fd4a89b787d638325f79294353cc23859 -29927,0xa8274b77b917f7e07131d2573f1699bd36213dd4 -30002,0xa8fd81a0ec43841b64cdae13f1f6816108fd37ff -29678,0xa970e68f9390bb735735a5f7b6ed8a1de22b8705 -30077,0xa97e92cad52ee87f11033ba2d99b9b49e6ac0bb3 -30161,0xaa01fcf32dbf4b6d539e724ec5d88589db9d2228 -29855,0xaa1acf9939e98c09f71f21ddd956ebc704a57027 -29895,0xaa496e474b32f7e6b1ef0ca4b1f381fca54da2ab -29632,0xab3104695471c983ff5b65621f76caa1a22cb3e3 -29930,0xab83f8ae9ba70501abbb5e350790a84a5cf096ea -29928,0xabc1c302030ed3d5cd485350f9c740bb1f154e00 -29787,0xae1c8d06cca5ef3d33ac2c071d6630ba3bfaf9b2 -29828,0xae92db3a5a1bbe57a336c17a6622708b4bdeae15 -29966,0xaecd33b7b048960a78cacf59f8da658e1ff0ed64 -29644,0xaee7400a6e567e133ea53e71ab018e13c555a024 -29840,0xaf9dd4c1d755402868ffe2a0b7c0e8a6664a0f2f -30070,0xafe54a0e3ab5c19aeac473b326239a714823e71a -29698,0xb01c5ff0909196b5f4e799003a89f3aa0095f380 -29660,0xb0af86f18c6155cefae1a6d6da35b05f176f6278 -30094,0xb0e6e1ca7a66dda93bdf465938125de12d55b122 -29645,0xb25eb9105549627050aab3a1c909fbd454014bea -29762,0xb289184d9746c69801946a02a3ea71356cdd9364 -29935,0xb30e6117bd2e6856265f1cbd0662c1ee0247e573 -29747,0xb37cd85871e003ab24e8553f3eda386a083f48c9 -29755,0xb5c6f353aa7ba6eea2f823c7431f3b3e7caa3f24 -30100,0xb5cc2092d3e6b270707e8b1e7588a469b65eb613 -29808,0xb6d97bc82b1c22e3eb1607df4497dabeb0b2f316 -30053,0xb6fb58eea08b5539f371a744bb9ef86283f1b3c2 -29994,0xb708038c1b4cf9f91ccb918dad1b9fd757ada5c1 -29736,0xb859f08434e6055c33f204d766d6a95898d37abc -29712,0xb8ce10759feb3b63b75557f5f9933bd22127f0ec -30062,0xb9c9bbab9eb12d8f339cf0db552e4775a1b90c5a -29726,0xb9f24e87e5298a2df17c44593ab8d8a525143d93 -29769,0xba1548b3f673950094dc00edc1eb71683f371696 -29662,0xbdbf8558ff45b9ed9572d80e5357b05834e6e496 -30164,0xbe1413f80fa4512eaa703028cfe3d28987369c06 -29998,0xc04dbaf536286d3e06bc4bd60681c47be586f44f -30075,0xc0c87c0a81a1f164ee9194db4d1b09debe55eed1 -29716,0xc0e74e2929bfa1233803e05ccadc8fe3ee03e019 -29737,0xc24c73c256d527726a7a012e1360c78d0cc02eea -29837,0xc2bbec44ce17c9fb478ab899dbec36032f4b08a0 -30006,0xc3991d99a73ed1fd9d28cd4623dd72732745963b -29907,0xc3d5c128a3e5bf60c6fb87a4b644b6a2d8093f55 -29999,0xc3ff1a784642c581adc8e3aa4f9229e959d10362 -30069,0xc40c9d1d10c5b9f84962a52c403389f3412e0ce8 -29821,0xc422261edc5db679cad9bc403e886351de540e77 -29823,0xc42bc66aff2a89efc02e84a20307045252bed000 -29651,0xc4f8ce2a495f0ebdf77b3559942fe6e9c7a97fd5 -29851,0xc57ca723ab234dfd1364c63afab610be7a7556d5 -29654,0xc5be4084b3d945fe83e1be4bd96522fa1d1c722d -30007,0xc616a6f6f4f54e224fb89b0fb97d5e7fe0d956e2 -29735,0xc68579abf282415dbabd074020ff430f96b32f6b -29905,0xc7d2c0b34e84f66e237bf7260552a645629ac753 -29862,0xc808b28a006c91523de75ea23f48be8b7a9536d1 -29795,0xc9737c178d327b410068a1d0ae2d30ef8e428754 -29751,0xca077eec87e2621f5b09afe47c42baf88c6af18c -29745,0xca838059876177077ff2b3ac9ce7c248950d2cc1 -30124,0xcadc68d5834898d54929e694ed19e833e0117694 -29671,0xcaf32063591b573fa1cef3f3e0e14483538a5c1c -29830,0xcafd999d5080d80f7e4ab9ed952a8b8afe15afe6 -29972,0xcbab6076b4b0c482e7127a201b79a13d117e2b53 -29890,0xcc2ba0401799857e4e14bfef6572f46fd5d0ac23 -30093,0xcc7f328b5e05073913ded9e7e7789901dd6ec47e -29793,0xcca4b9debfe7ea2221a8238499d66ee5f3623122 -29770,0xcccebc4551ff20aca4e0356dad8875806ce8df10 -30059,0xcd218526380baa54a9aef470ceac6703f089d949 -29967,0xce3c5adda86f5d062f51e7335de00ffefdb6af47 -30149,0xce442f708ae4a3afa731b39b955f3a65586fa41a -29693,0xce6a6b6935a1ab3af7be53fbc8a149791b418278 -29853,0xcf9a11dd1567473036be5349eee69895d5b860ce -30016,0xd019fd6267f93ebe5a92debb760135a4f02a75f7 -30123,0xd08575f5f4de7212123731088980d069cb75873d -29778,0xd0bf9106c29037ee7b322aad2dde2364e43f37ce -30147,0xd1d50c73a9e2f8d5f39c1fd745d1293f19bf95ec -29940,0xd340e02a1174696f77df3c9ca043c809453c5c83 -30025,0xd4a31a5ca49c2dacf21149f58b5afe85f3218d55 -29691,0xd4b95a7e8faa968a90fbef6bdaf870d89b4fc327 -29871,0xd4f05538e8e79f3413b4a4e693ec0299e03e3151 -29959,0xd57f1714a0928310f08fc23f882fa784d8bd0dff -29951,0xd5c954b23dfdd53d62f94d8d02e784a5062c1ef0 -29901,0xd5d634926a5b58b2952f19335537f1ed3f09bfce -29699,0xd6f88235aa09431083867c4dab92327a21003fb5 -29758,0xd80fb1be4f0df4a00f1cae4f48fd734529f599ef -30029,0xd858783942f23d9ca023d872664e5047c326ee45 -29825,0xd8b3cca9fe905497c22d500cdf76beda0a65d442 -29797,0xd8d7db8272c02fcf784a6794b4e51647258c0660 -29943,0xd8fe5ac8a68bd0a9c225f56a9cb94f8029df7489 -30001,0xd98a4923e8b8f298028bfb1874c920059605a89a -30096,0xd9c451d0518af2b40bd02232c628e2f46acf5b3e -29816,0xda2307a45d298e855415675bf388e2bd64351d5b -29702,0xda875a511860f2752b891677489d08caedac00ea -29839,0xdae62136ab9799cf80ba0e08b18a50ff5b30661f -29687,0xdc036ab31d2f105e15c1896f88d13b7bcf3e9292 -29835,0xdcc5dc90a803928dc21556590d4620030966270f -29694,0xdd63306a9792ecbd1cd6baed3f1b18bea638aace -29919,0xdd73d23a344eb895face19484f9d05c2005398da -29641,0xdd77d377506faa3bd4e70c6f9b0e4eb6925b70b2 -29982,0xdde16f9e361c344639d7ff3e28e4eb6bce3155dd -29768,0xdfcc0fe31568cb3c75a20f41a8fd705bf951538c -30011,0xe00232a57b6b39b6cabaf9222866d99751f28af2 -29707,0xe0574995c40de14a4a5590f53a67d93b5950b4b2 -30104,0xe0862e539d961577c079f0704edf46c497c35686 -29790,0xe1302317adde60239a2493559a3a136b4b483273 -29629,0xe13d33d1dd9c099613be6923210da71e1e64adff -29792,0xe18c61bf896742087b8ec509ad3fece6d4c0d5ce -30092,0xe1ac5c1adf967d0e034c9a01dd8a1deccd5e0e26 -29963,0xe200cd5ef01b52bb968402f63e3933897cff367b -29944,0xe2affc95cd0ecbfd1fe674243e5298114ee939ba -29913,0xe2f035f59de6a952ff699b4edd0f99c466f25fec -30105,0xe4b3c2b19db4fbaafbe99bd8ec869fc4c37063ae -29974,0xe4b3ccbb1e48c579ea3d764fb258bc908e46487e -29904,0xe5080c7d8d4226eeaf1b06e3559f1170068fd60e -29933,0xe536f8141d8eb7b1f096934af3329cb581bfe995 -30013,0xe5a01497bff17f4f0f7b771a2c05e78f84292b4c -29947,0xe6a9f1d76fe905629185b0c901a681fee8372377 -29639,0xe75e9cc79c2d1daa00f30991fbb413c8d4a86537 -29908,0xe7a6563073244044936734536c9fdfaa0b8583cd -29911,0xe8188b9701e6db1fe24c75783474d22e5957bbef -29838,0xe83b0bef56fff2fdf369e55a5d9e5123cf7d72b0 -29989,0xe8e26db4130f498c6f66cbed9bc2f82db74586d5 -30117,0xe900d8b8f562ceb2b56b944dabf64d285b1fafca -29714,0xe95a759946b0f9a21ef08da65b782ab6b26d2764 -29666,0xe9831aefcf8c35ba3f99239391b19eba05336c10 -29633,0xe9a792d3814c0eb20ffbc99147a3c8922c24c493 -30143,0xea1d3fae02316ad755b4dc89214bdfe923815375 -29673,0xea23b6562cd76277825263b4d58470939014d8f1 -30163,0xeacaffac2d3a4ddca6dccfdeb15111af2a4c51f9 -30035,0xeb3c00fe42e2518293719e3fb0041bcec98a25d0 -29789,0xebae4810fd13605957e7d90bd49d2fd8f8148923 -30079,0xedfbec807304951785b581db401fdf76b4bad1b0 -29774,0xee3fa2ec6053a427b9768b2b481afa31e5b555d4 -30081,0xeeb3fc62379774a2053b39bc4d48f73f4f470e4b -30014,0xeefe8171dc249960d730b19f3406e40c8c22c567 -29887,0xef1bc0761d699ff5ae0fb76e2c015128f672f0e7 -30028,0xef46ea3e79c2d75a6bcb8ba7a3869d9a6e54258b -29721,0xefa8e30ce4cc433cea1b3b6006d69731a4fbd485 -29738,0xf06019e9733f54b86bf654c8afeb4e978ddcd933 -29865,0xf0a7626eccbee00af144bb1f77cd187af85bbf41 -29881,0xf0d26842c3935a618e6980c53fda3a2d10a02eb7 -29731,0xf107c61f6c49e8650f080454807279d1672f9a5a -30151,0xf1c9962962db4b2788acfbdd356aa8694859b54e -29636,0xf25083cfdd903f82310a3c2099c9ffeab6b2216f -29939,0xf2984c27b963a14f9f3b7326096c54fb05d5b9af -30091,0xf3937d9d5178994bb5374cc6f79876ebac431eef -30122,0xf46346de9c2e5e652fcc37695d3dec3d672c45ba -29852,0xf49c523f08b4e7c8e51a44088ea2a5e6b5f397d9 -29923,0xf5507e195a6b1636d7284c3cf1746a1ac5b4def9 -29869,0xf638ff937f92502943f65340b8291c81d873f93b -29674,0xf6c80aa28ab3d5c1b608c5ffb9a260de287a8b16 -29832,0xf782e675b93d22fd810dca4149d29ab32e5b2972 -29986,0xf8e9774ae3cc7ae3ee11161cc1313252a0784e18 -29763,0xf92129fe0923d766c2540796d4ea31ff9ff65522 -29734,0xfa173706f94216689ab879d8b21d082c027936c3 -29799,0xfa68fca9c409d6922ed2f38d4e0e80074d5d9c54 -30008,0xfb9b6d7642f3549e04f11770bd2811f494e78f48 -29748,0xfbae682cf798d1be11bd55a9a1a558719a922d52 -30036,0xfc7a31e1c5c3846d36e49223fae905e28315c1e0 -30103,0xfc9e51a77c0d2755121e5a2fa4d19daf072f97b4 -29915,0xfcdd5a02c611ba6fe2802f885281500ec95805d7 -29970,0xfce74fe54ba4b3428cac457820a275240037db92 -29875,0xfd773a3c240440c40671074b1d88ad2185270d65 -29894,0xfdeffc7ad816bf7867c642df7ebc2cc5554ec265 -29955,0xfe40f6ead11099d91d51a945c145cfad1dd15bb8 -30128,0xff46d99c3af43e9a133ece2512dcbca8ed46b861 -29724,0xff504594edd93e09309d5eab775e7c35f642931b -29648,0xff8f9002a8cdb22aa70f0dce3e83b6908e0e56e2 -30086,0xff96c11f6b58d65e1206d9666469acdfb75d3cd3 -30193,0x01c3731e62a37c17e72e32d64aa47531288beec5 -30233,0x0b3c3dcf4c9db39b810f306e55cd14aed5c19c0b -30215,0x124198789ef8d82050e620de2b73332c3c6c2ed4 -30232,0x148f384df76678a5893f569ec377162118d1e4f3 -30235,0x1d5207c35f82ef87332c05c3816e4aa34ab8052e -30190,0x1dd9720693d6788e25ddbe77a6098e3b85397ec2 -30203,0x21f359d1869314175d84a18a6f5deb94672c4996 -30194,0x25c4d0056318b1ebbbd3a83be2cb7f43fcfe13b8 -30241,0x2856a18c94fd252bf3901e2d12b7f3ef419145aa -30201,0x2f3e888dd299a1043fad007091d3712a58d49696 -30220,0x30c5f742736920e741445e78a66f3a51b77ac17c -30202,0x346ac4020562acf6dff932d5c5c37b63c6eba38c -30208,0x38b6cd8b0bde79691074c0d23320babca158b352 -30205,0x3b8eae4191c9212dee1d576f57c0dfec5bb9cedf -30223,0x3f8ce025b648de7303bd12fd2a16113757614088 -30240,0x4f208866005e1f6c8dc169461506a0a973211ee9 -30196,0x58012593b11bbaf3cd50d0db8689f159b1f3fc54 -30182,0x5f9ee4b7454ebe0a1ed09426154fdd24a37f839c -30177,0x62249fcb288f0a4734072ab899b926ef9845bc8f -30224,0x698fde00ef165842a8ef69e66a75f5ae3727ef46 -30186,0x6a9cb49184c45945bf503922b742c8ecf3b8c970 -30188,0x6bfe136283bc92c7dd7237108e838c9ab32fec13 -30236,0x6c6074fedc9bca8ce2c1ff13fef33ac9590256ae -30189,0x6d4e584267b41990fe49f3417616a3ef3b9ffc7a -30226,0x70c60e41443899350e6db74b1c720e03a7f11689 -30229,0x71f5116238ef5ab877054cd8cf35133ea114fae5 -30231,0x77b75c927ba31c6e385364f2d177c82655be6049 -30204,0x79eb6f33d89bb4e4230292f55ad8f85f1537b99c -30191,0x801524c9983b916524694c9d6e2efca0c786aa20 -30234,0x80c794d7f1820fd47c6162389838cf879818e8ca -30211,0x8350b510dc83ccf638ac67034af75b6476d7f2f8 -30184,0x85267688ce7677d69d75f288ff3064431030af9f -30197,0x889aa3c5b5298d70613373f25ef66fede25b4de1 -30185,0x8a2d5a097e302a4ded891fc5befbd87adcaaf5cf -30181,0x9522888af88862512b38a8497b9d78c04fa64b8a -30192,0x989dd4e0b4f81193345dabea2a5db84fb2d639cb -30213,0x9d528fec94c7d0411f56dabaf7fb11f5db3d45b9 -30239,0x9ec39e9d3bbcd9bce6edae0d9a9aa31ebb76656e -30237,0xa5c4044c2a5f66e9aaed43c42b7a8cda3c97e346 -30225,0xa6faeee4df263cab9be09b747f4be760940c884a -30216,0xa72af4f380d5bc1fb5c7935099bec6b5051ea124 -30214,0xae97b9848a577612fa40012bcd6d14e7c291ed64 -30209,0xb15cfcc51f6f16c3cedef9340d003dbfce5f0d6b -30183,0xb392a4a5d72b3dc8d0ce15e9280eab57da9a05a0 -30207,0xb3bdf779ddeee81cacf8b09b759b53ce5d1c9971 -30200,0xb4daeeabc1ace2129072dd616bc0458d62b53280 -30219,0xb4fd07c7b63ae30579ed7b2a2a5a739cb36045e5 -30180,0xbd33ec52a0e1d5d0deb2066ffdcb6c56567b1cee -30198,0xc2cec743b5ebc2ce356e2cbca65d0abe4e13c248 -30179,0xc914891f926d25b798a3561ba30d9ffe990c0be7 -30227,0xca7cf13d93bfa93183dfaf61512f228f1c653a9c -30206,0xcd34d0c465748ff4f6f96aa4ec0220e1f1f0081b -30195,0xd2726e90e920b7c1ab8147f66f7a7035ab134797 -30228,0xd496ccca71a210a49bde96c9bb50bff34c74b734 -30238,0xd9937219ead166de2a6f65485fd957bb3a64e3dd -30212,0xdb2312c4f8e4b0a4a9801e11f9dd19592ec3261f -30199,0xddf87c859c713e1bdfdc95baf1cabb5fbc83c1f8 -30217,0xe236d136ad9f6db125e9ba95edc1d1a19423ff0f -30210,0xe87a1bf326dc0958948cb4a27e7d198cedeba69b -30178,0xeb8b7ab9eafd427d5cfc14a1dcec793228d91ea0 -30230,0xee5456ae93409e8dc298af98bc865088fe17f0e3 -30221,0xf03e89e5ae88a5c8ef7e59aad6222be0600f9af5 -30222,0xf273c166586edbe0fad945d24080bac5624d74d7 -30218,0xf96e06e8fe532338d9252e7bc72980694f98cc09 -30187,0xf9e693fc08c3cb2db5b1eeb987612460b40e3e2b -30307,0x045a3438a37de5b68c9e4ed5ea26c49cce1b5f5a -30309,0x4b8998c34810c80b5038f5928fb3a633ccbf6abb -30303,0x676c7d48ebd00735e082b0f1d11762c3ff305072 -30308,0x7a16f708cf4e23b237981ec2b9c4dde6c1d78101 -30306,0x7d57fdb645242b91e5791024bac39332c81b6523 -30305,0x7e26ff45ce22d14b88d8cb29ed66fce9b043291b -30310,0x8b17a6f80db8cbcda803201c5e5b4e9451e06ccb -30302,0xe902325fd89729fcdfb533ce5552a65924c0406a -30535,0x044df4c89bde232cd75bf62c1f5b24984bae3869 -30480,0x05042c3a25f956133bb9bdab453fbaa6ff07b6dd -30496,0x05498e3aa45ce63f47164804732248c19cca6f22 -30581,0x05ac722eadbb916a859a656db12543a1f8942f2c -30534,0x0661e71810e19da9d5626760e11291fba4785f7a -30540,0x070f68f1ebef6045a80e197dbc792f8b494082ac -30588,0x07295d3780aaa4bb495dcc6e1273dd3e666cd2fc -30461,0x08e5c47c1a529512bc9c3c098db1fa1958536ea9 -30604,0x099e0a0fe3f86d32731bd18c9d46dad4c096b782 -30513,0x09e56476f4b03191d75f5582bfb2e8e3360dd9c3 -30599,0x0aab634878e44c151f17c583ea57923d30fc6362 -30529,0x0da4509514efa804b766ed67ea73b261f743ee56 -30526,0x0f13616f10c2aea3f6769cfc03b7f2456d07b79d -30602,0x0f3c9ded32ec5592e46b7fa5fabe60b818269a6b -30585,0x13acf4862dcfe7a66750f901176761eb62f251f3 -30589,0x13d0f0f58fa4a1da0c5bccc8c7dc1d0f68e6d8ef -30527,0x140c5eb727eb39f1c15c741f469d3c9af879f6ab -30495,0x16768875a4a15c60a73bafa90549ce48d4fb1026 -30501,0x168dc3d3f82e458889d3d09f1258b42884a64329 -30500,0x17c314100609cf81f513346191993ba56aa2a18f -30507,0x1cff78b33efb6b11fc5af1ab0ac0e34ec46db037 -30505,0x1de21618b788dd55d8ecfd3c6512fca8c7a84cd5 -30478,0x2067b49189d94f04a890b51bacf1f248db697bad -30530,0x207067b039161cb50e58b1758e0e5c4cf74f0f49 -30580,0x216af315204380e14b055e130e6e7877f89959c6 -30481,0x23147b1d929c84e352fbd486aa999f38c13cc6bb -30474,0x2a0bde4cb3b3aed07f50d744dd257893f4a7bb6e -30577,0x2af168f225693fe7a1c6fe5f87ae315fea121eef -30555,0x2b2b164392541632f96b3c6590fa44dc5a4f4190 -30467,0x2b90a1c91f5b4c3ebd5bfcbfd94853f7bf30a908 -30566,0x2c549f628740a3d44ba2a6885d15a8891a4b5156 -30570,0x2cb25061c6745fd7a7cc4369831ab630193d69a3 -30559,0x2d05b2b25db42ec0bb2d0fbf0e814798f0b313a2 -30464,0x2f5c048de21f6367558c70fa364eb1df8a4ac118 -30579,0x2fd9089f03b9ba5a2f5edf2af7e3e09f062d6698 -30463,0x2feb1b039c5ebe697c5cadc83deef9de3c4b3e7b -30553,0x306b08e0a3ecd78e2c5633129ffb6f96714d6606 -30538,0x3090751612b041910bf2811d499e89d59464f655 -30519,0x32fa2a000d5eabf36034c71de8363a7e90e0e119 -30528,0x381de316d44e7c8b641365c666379158e0e95803 -30572,0x3876a22b17ad3c77c3a7fe74ff7104582699623f -30571,0x3916f90936af7a7be65059df905d7d6e2a2e8cca -30520,0x4140674748e4a0361a5dc2a53528f8c08c9e041a -30514,0x41ea61d3c2977fa941f82d2930a3d1d8125088ad -30549,0x44e6525499fea4a8b62ef7cce6dd3202f9372137 -30551,0x476e82391628fe2921a327dab278548e030d4283 -30542,0x4788043595c5da285ff01d3e9295610f02fc593b -30476,0x47bc624d53861a3b92fa12157d89f85a1096f22f -30488,0x4aa3898a8e410e468cf19795999fe1ba704c8721 -30483,0x511aea61f9eb8e7e9898da40a10d60872e93deb1 -30522,0x51ecb53f1d70dffa21191214fba0638279d4fe92 -30459,0x5217f41b133b8459858eb62952e8c74a3b5a6c16 -30503,0x58fbaafa806598c8e5dcd78118aaad4a6c3dc6f6 -30543,0x5a779fd07349983c6aac1fea9a22e562d8bf6e78 -30594,0x5d873472d463bdd6650a4cb70eba2d13408ea09c -30597,0x602195a9b44711f19c3d6a17674245deb8bc0e2d -30537,0x60337cded1193e7561de8d4f0faf46d2f658c63d -30497,0x60d449bf444dc7ba8c1c21e508ab4e1aaa2e33e1 -30545,0x6171cf9ad8a6569c91a34fc2c7f8bb6074955152 -30576,0x6bce9b12df0b18be43ce47b23ef5a04a2934ad3b -30560,0x6c3ca8dabb57779816fb10024557615579f083f2 -30506,0x72fffffe05f7916a1504da78a79f31e196d734eb -30554,0x737d8391bd0e68140a71157937af336f84fd8b5d -30509,0x73959adce16fb8b68dde190f05840f0645f538ea -30595,0x74e8c3aa1a12a67aebaa25c0b5415c9e56b659e7 -30565,0x75b48fb79b9a27c0b427e4a4e7211bbe965e1ecb -30491,0x77d0864f979f3eccb656a7ce31166e4e3febd7ba -30508,0x790306ad408ec088eef0081116c898bb9309d7aa -30518,0x7cbc2a1b25b530b84cfb7643c9ae15e3926381f7 -30510,0x7d072b631ed6fbaf9d65b807e1645282445f0d84 -30593,0x7ef500ff1acd152891dd75f3d39cdc48764d238b -30573,0x7f7cb62279b4156d9ddefe37751df2c3bf774c3e -30596,0x8137ef89cf4e427f87d582eb041158c25c7f1526 -30544,0x829736f80c13e2a0923b109154fad40961e15843 -30569,0x86033092913d14a898ecba70c147659485f8ea1a -30558,0x87067e92ef70194ab6587169dace46a6a85bf0a9 -30479,0x89a9879e8832880072b66c2b283017385c908421 -30550,0x8c96b7d7241d6301dbe668bd574db874d7a0255b -30525,0x8f22c9b054fa4f89084d895790ea2d6b818bd80e -30546,0x913af0ee4d9f7a4114fc347744b62f04cd6c7b92 -30468,0x91622a73b84911ec0fe4458fc5091e2616fe9f32 -30523,0x926efd744284644a8ce3acea765c6340e99a18ba -30605,0x93b118125d8e1d2449bad512ecf7d8a6cffa1283 -30584,0x9502c05ec8c86aaab1e06bc12ab9b9a258e3952b -30531,0x963bf81b7977766764902f63fb3b3e44b03873d6 -30524,0x982959d320bd462a011bb5eaf0833697d030bcaa -30473,0x98cecc3341307d72ab0fed85c29b2b46f00dadd1 -30552,0x9f256ab14872167bad2fa600c13408128c4d1699 -30568,0x9f6f2645b1fb5fce6854b2b229f9509336c37cac -30578,0x9fea75bda4e828ceed5ebec7b112824cfec89b22 -30482,0xa0388c8db35b5f9c3ba6ebda45eeb30e56fd6c2f -30591,0xa1dbd4eb721357c571f98d87a529ae13b1351048 -30472,0xa1ddc7055627f6816fc5ca8710c4abb7ec0fee89 -30586,0xa24a04476287670a74ad8c6f4f342f7c839d22c2 -30567,0xa40b41b3fb78af356c5b660bdbc1306b13fe0124 -30601,0xa9722d7e6bd2d2a2b6526ce2a12de4d6b737a263 -30562,0xab96459e5c3e6e43ad42131d0c84203d00ad5789 -30600,0xacf71a4df489876863b09c9a15c4b88a160b9030 -30590,0xad09dd6afbc8080aac660b7536b5e63cce1373ac -30557,0xb124fff44cdacb3dc5f0beba0f8d56f8f5f05edd -30575,0xb30da082af0dcd59bead4b2c8cc74508b11a2e5c -30490,0xb5311a83959b589876ebb364206a13d998f6c9eb -30487,0xb83efe72aba43a0a0a5bc59314312d036a347fe6 -30470,0xb8c46a8266b691419828c33c9f563c6278231895 -30494,0xb8cdccef6d187c3c64ce849ca79bafd675161849 -30516,0xbae5e485884cb5e961c8489ecc78b3842b322159 -30502,0xbd0ea4901352053ba23ae5193d2d5c84de04c35c -30460,0xbd6d68b1ee84296a31eb1c9557c95731f20f315a -30603,0xbdb4dcc9ec06e12bf577bfa67c78a6ccb8c06989 -30521,0xbe03759b60ce30301ef70cf12448679a9f10f6f7 -30485,0xc3c9f6c504e0f5ca0eb5af370550558901f042a7 -30574,0xc4d80346e9d30aa2bb608a9dc994677bf9b77d16 -30556,0xc856f79354cc446aea09d5f439785329400f6774 -30489,0xcc7e6b961368eb65c97b6b3ea1e29582e2927320 -30561,0xccd9df4bea0eb111aa6610905029ec5f6da0dbd2 -30457,0xcd17365310e8daa951b589f00230a8c6ea3293ae -30547,0xceebaa671617a58731ab2ae3852e573e6aeee641 -30512,0xcf4bd13555336065b14210b9c0466f3afefbf1a1 -30533,0xd067f7add9a114e78789f4889f6d801b9b358a45 -30504,0xd2b8ac05d56d3572995f6331fad8431a2de04568 -30484,0xd3f9ca65fcc2c72bbc53b13fdd03fbfd7c0bca1e -30499,0xd82a4963eb17867500d93ff1e7aa61df50d39029 -30536,0xd85beeca2df41d2bf5d8e815a8127b06c8c3c0c5 -30587,0xda2e757c49ed44d99b782cab1d747ab003933597 -30498,0xda7a001b254cd22e46d3eab04d937489c93174c3 -30492,0xdc4300d741798c3e652b5bd46917e86b99e6efa6 -30598,0xdcc0ce9ccdf295240e136a279d14e2ead5f87609 -30548,0xddd9650330210683a1015e8f527f662ef1d05747 -30541,0xdeec0209de597af6c1c9de7a579cc7af5498e9df -30532,0xe194c91b336cd0c175ba53022d96b1e889ce491d -30475,0xe24e6c31c71286de2784a05e6669fc12ce0bde41 -30515,0xe4dd52593be31924dffe66aa74b430a1ef291ca7 -30477,0xe59b5b3438a5e7c6403282f3a4612e5c90c548c0 -30486,0xe5c8b851b0c8c50acb57413b9f4c07b91ca8d5a2 -30462,0xe5de53af06f5f0aaadbf66e726eae8a9367d4060 -30471,0xe8da4b8e3477a9342dceae969f338baadffa211b -30511,0xe9137e8c2f99d5472b286676effb714e636ab131 -30469,0xede9886d064b3e09cb753362fae2a4ce1fde8fa5 -30583,0xeeda167d0841d06d720e8c0c01ffcd2509d8d2f1 -30563,0xf36147b6c5559073aa6f5858b97c15ecf258451a -30564,0xf38e82ba4a16b3fb34839757ed3fcab23e68838a -30517,0xf794da1f795e8fbfec211fc799b5fb3d205b3a81 -30592,0xf8190a9598d2ad0ffaa021a668fa2a8325559473 -30466,0xf953ce7b47b7c7a285e6219a8705844b61a8e0ff -30458,0xfad52512433af5672899e4bb0d5b1e8d0a3ec001 -30582,0xfcb927db6f43112f40855f6e4bbc1a69cee2d8ec -30539,0xfd2ef498e21610f9ee443523978ce9e1373c2b23 -30465,0xfea9fb84b5f18f29440ae2bd01ae07cfd2cb1749 -30493,0xfebfc6c79c6f4f91407640f97e193001c7585b5e -30673,0x007ff7eb7a45be057192d8b7f660bea70f3e141c -30641,0x02a62d61eca1a548543bd9cab686297e50685d6c -30681,0x0b46322df7087c674a22482d07fafe8b92c5aa12 -30696,0x0c8ea4fd75c1f7460bec77f792102e2f2ffbcc77 -30671,0x190d68660d2bf14ff46ad0a110833af0f7be9703 -30642,0x2136ef49b646219a5862e3e2225de12e5c2981b2 -30655,0x26825d87ce6a37d6f40753d6a71f15dabbe2464d -30690,0x2c83ace2b29cde585c6f0e0a61f6a2dfcf5ad87e -30644,0x3089b47853df1b82877beef6d904a0ce98a12553 -30643,0x309c7435a96b514886667f3085baeba6b8264e0b -30663,0x31e0913fa5779e060f921963856ee319c5999e82 -30691,0x3bffd69722073889793989eac1b98cb1b721e294 -30645,0x3f6fa504df3bf3792687885f4bb780e9ec820c6e -30661,0x3fdce766ea12923db21cf4348b5b1e013e1965d6 -30672,0x412d0a21a5c85b3513e5b89ceb2f59e5e7d7a310 -30674,0x445002490eef92e707c1647c54dbea62f68e9407 -30653,0x44ee194d81ffef099804f8e94e762f2a5d1eae88 -30657,0x488ddb549d5ca5e67d19d0758475fc9e92913850 -30648,0x4b252ca905a2f6aafda92673c1bd9d98b8ac22fd -30659,0x4b6b933d8d84d2b0246beb4293619f5435d8a159 -30692,0x4c6822204ee5e13b4281942ff231314bf05f2d3d -30678,0x5058f19761d3ea4a5f7d572d48b4ddd467847d28 -30685,0x5cd9a5a3ef8dfcec9aa29ece5de37a3de50d3d19 -30687,0x5e0548f0695ccbca40dbbac34ba8760f7c2cc73e -30665,0x5fc53c5f41c3d279d1b96db7e9f8ba0b4a77634f -30676,0x63a9addf2327a0f4b71bcf9bfa757e333e1b7177 -30697,0x65c3739a705723791041d1dea4c75fa7e3661c66 -30664,0x69b4bc4628ce3026af44dd840b57ba50a5ed33e4 -30684,0x6b9530ae8e2599eeff33f868a19f6479e7ddd3e8 -30689,0x6eee37d326db3cbe1a71a14ee1482b742a56392f -30669,0x7389f4cf98abeaa7866f49d6a887eeecf0f63302 -30675,0x7404ed040367b3eff1077c9f1803b5fa69cadd1d -30686,0x75e1925c1a368f360b6c219665dbf13a73b78974 -30652,0x767bba185abb83c2faf01539097c887cc175306c -30658,0x76a2c091225f6cebc42279f670fdd7cfddaf8893 -30662,0x7e1ac403d6b3d2f857909e44c560779a8771cad9 -30688,0x7e9d26343ac6623bfd55bbda3449d72c79fdfb98 -30694,0x868362a3e62d3b50414c2009acdf6b1ffd167a25 -30651,0x9ea526b889605097382a7ae1c2d5ca631d4c6443 -30640,0xa6f8bf368c4fa8d6a7462d3533a5072d1179d879 -30677,0xaadc8766586d442bff16728ae597d0e88e3c7b2d -30660,0xacccda2512ca315ed032fd55806f643bb044edf1 -30649,0xb7d7fe7995d1e347916faae8e16cfd6dd21a9bae -30647,0xb8f22031f1ace02da4a047950dff71a28a3d1816 -30693,0xc088b09104a57f912ffe5da31d11e8e2e27ad017 -30695,0xc132305d00ae8005a0f777e08f6b934a18f9c9a5 -30646,0xc37a73e2ce90ee073e70d95cd73cd7ac8f8ff1b1 -30656,0xc86f81ece3e92907a93c230152d2782e183dce10 -30668,0xcd45ccd6835cacc459ab0bea0c2403d493eceb28 -30666,0xd0a936410604bae4deb5a60848de40bcc76a1169 -30682,0xd475c4f637d62bd99a42bf97a2ce85a31706c451 -30679,0xda45dbff86a4eedc62b04d366c7c5a1223d4cbd6 -30698,0xdac8316f24364ffeea73a190ee7e332eaa04b8f2 -30699,0xe01194534169dc6f38c9aefea4917c623a99e7ec -30667,0xe08fbe34203ea0bfde3770cf20ea79ef462acabb -30680,0xe1b3dec0448341eba1a9780ffaf2d1a1f3e9c839 -30650,0xe1fa046fd54d154f9db51ab4cf72c8b7a9847b35 -30700,0xf1a20491d72798f009bb2ec782d28bdba2d8c27d -30670,0xf2e4850d1860dcf925f45465d8c1274faf577f53 -30683,0xf757692b13e492255595ad8afad273ac9a138819 -30654,0xfab06657fe444c15917599f978f45328e841a9de -30773,0x05823c334150a48acd5d325fba16147c21ba3653 -30772,0x1a920b29ebd437074225caee44f78fc700b27a5d -30782,0x4e7e2867d80af2114837e015261ef028bd3cf60e -30785,0x643267ed200effddf75501377ee5a61920321388 -30771,0x8a156bca562fb75385c4d41ea6903e270a34b727 -30783,0xbb96d85d19bae402fa37ecf6106f05ad71825032 -30784,0xe00bcf964f7509717c4911fe2cbd985fc2aa9728 -30775,0xfef5c90d84a1c93804496f5e7fbf98ec0c85243c -30808,0x03065e84748a9e4a1aebef15ac89da1cdf18b202 -30824,0x16c26133e747374bbd594f214ccd2f2e252aabf9 -30825,0x38325f901a698af88d855f061d0fea70825856c5 -30820,0x5167e12139ee4b2f6590f3c95e56b29d408a9048 -30819,0x51808263e311782071670a9a693936da04b7870e -30811,0x5417f117e4a2283623b3b9a07ec2b2f269d19a75 -30821,0x57d942953d416835f7b60be2a8b49870cc7bcfe1 -30818,0x5eca1457ef6531c6a0a4a9a4054789acae303db6 -30826,0x6ec6084e09f5afb8dcb4e45c2e36f2ca0bbcdf7d -30813,0x7745d489dc858d07313bc18ba48930066c019590 -30822,0x898bf9c743a436c9c3f332af445aad69c15b10b8 -30807,0x8fcbe72710185dd34a8bbba1cc05eb2628945fec -30810,0x9af2cb98a6b3d3a5cf47a93900ad49c39a008113 -30812,0xa7d0e3b8ea63c73c17713fe45e0e31dc25ed688e -30809,0xaf4286e5e9eabc0142efc529652fd05335b23dd6 -30817,0xb2f53dda7f07157f05f485f1e8719b252a925242 -30815,0xb89aadf84f118d59d5770e6fee9cf1b55244ea8b -30814,0xe24f9d529cf71075e0a33c45a8a66ad99a11dde2 -30823,0xf1ae0d98c31b1b7b784c05cb5e5bcefe6208fe1c -30816,0xf9f7489a47dd4c4815ff764b75114751ad339dc5 -30861,0x0451255563e2aca170b2552111837572e7a0bacd -30855,0x25d03062d994e358a8a90b93a19bed1d989f5e3f -30874,0x36cac20dd805d128c1a6dd16eea845c574b5a17c -30880,0x4fb47126fa83a8734991e41b942ac29a3266c968 -30857,0x51b6e0ac6d6435650748513c71db453f96749fe1 -30879,0x5cf4928a3205728bd12830e1840f7db85c62a4b9 -30887,0x5dbad78818d4c8958eff2d5b95b28385a22113cd -30884,0x6817149cb753bf529565b4d023d7507ed2ff4bc0 -30885,0x6b5da774890db7b7b96c6f44e6a4b0f657399e2e -30888,0x7372ece4c18beabc19981a53b557be90dcbd2b66 -30875,0x76578ecf9a141296ec657847fb45b0585bcda3a6 -30859,0x7e702e51142eb7658ce4ce734c9bdec74250f265 -30883,0x81cfae226343b24ba12ec6521db2c79e7aeeb310 -30858,0x8d803f7f7e26e586ee90e5a872cf7830e21f7727 -30877,0x9805dcfd25e6de36bad8fe9d3fe2c9b44b764102 -30882,0x9da18982a33fd0c7051b19f0d7c76f2d5e7e017c -30876,0xb9f8ab3ed3f3acba64bc6cd2dca74b7f38fd7b88 -30856,0xbddac3069f4e26f4e4f445926c8eeb9dcb68c26b -30881,0xc3ccace87f6d3a81724075adcb5ddd85a8a1bb68 -30889,0xc4ef943b7c2f6b387b37689f1e9fa6ecb738845d -30860,0xca6481967e9ed5faedbc5dffa1dd8368979a2160 -30878,0xe3881627b8deebccf9c23b291430a549fc0be5f7 -30871,0xe39b5e3b6d74016b2f6a9673d7d7493b6df549d5 -30873,0xe42ffa682a26ef8f25891db4882932711d42e467 -30872,0xf24917fb88261a37cc57f686ebc831a5c0b9fd39 -30854,0xfa6b857cc17740a946c9eb85c1a6896f2e0be98e -31077,0x0a7d53ff9c56a3bd6a4a369f14ba3ba523b3013e -31060,0x1535ec8c7477d38066326189f5b1c695c2846479 -31024,0x15a065b0ab032541146fa3ca1cb382a22c94fe3f -31080,0x187dbd46e895dc06dda1840eda6d9b01990dede5 -31029,0x1b6a53991f257ac9dd1d0c061c37e1be36e0a8e6 -31062,0x212f71bff67e5b7795952a916598c4b64ba48741 -31083,0x232ea11b244b1b36d034ba57b15ad514149aea2e -31055,0x24c95f333755bbedbae14b461770c9ec3f6e9d0d -31076,0x26673a336a720532ebf1d225bfc7092772b3b775 -31042,0x2b65ed2400025c48ee77b057b1c1a1fa7cd84dfb -31021,0x2ff5d1da4985113f467bbbff015e76ce8ab05f29 -31071,0x310b18daa9d351062c6d2ec0f0411d327206b4c3 -31050,0x336649aeb266f3182d63f4fad7b3cf0dba15f4c8 -31034,0x367f2dea1632ea767a5e968fefa4c0dd78dea853 -31033,0x415154e1425eedf3e335a47e8b4f5e729f7a0814 -31037,0x43667563725040febe9bf5164a38b7258105a210 -31030,0x48a151421f9ea0e720e383df14ace1def15189b0 -31040,0x499b7cbd0f84318fee1edded7b5458f6f4500ab3 -31018,0x51da89df572633d2baf6a9260d3d5a1f0d146747 -31065,0x57a2b0b2bb1e21fcb2df358fc832ae3f9f535b48 -31058,0x5c00be319ecf60ec35d00094d192e97becb4d06c -31048,0x62bdb9544a14dcd8804f7f2cc1479c82fb36c2a6 -31070,0x68f51d2aeb485ca3a65a7ff54e9266dd5ea4c58b -31041,0x7d4a0231377a6ca320ff5f084b633a2e6b688107 -31047,0x7e232aeed14446d427579b4f77251552b065df00 -31028,0x7f3013aa21c784793ae6420140d0703aac1b76be -31027,0x8044de2e2dd22ac7ff18a4d7c9fff15f12b1569f -31064,0x8582bf142be76fef830d23f590a2587f2ad7c216 -31052,0x86d62a8ad19998e315e6242b63eb73f391d4674b -31069,0x87c7c885365700d157cd0f39a7803320fe86f0f5 -31081,0x89cfc2d653b092b0cb7b6160156db5262eb51a4c -31051,0x8b24d44772a27030353bee0f252844602abbb0f1 -31082,0x8c99b6edd92b83cb123c84514ee9ec5f53afc501 -31023,0x8d1c480bca1439ae5d5a81a75d53f9bf6ec513f1 -31031,0x97551ad4c64927ff40f4ccc310d233835a110fc1 -31056,0x9ce88a56d120300061593ef7ad074a1b710094d5 -31075,0xaf0f96e8702cb1b8160e43c8c020c608cd7b134d -31061,0xaf9707c1c3b513d36002f8d87bdbd10c8414b847 -31045,0xb556f4a5ad53bbe5c6b99c0e7ee9f58bd0feb971 -31020,0xb8888ea29e2f70ad62a3b69b1a1342720612a00d -31025,0xb9bb2856e0af9d3e855b0173a40059fc29b632da -31046,0xb9bfd9807ef39821beaeab51acdf32e23e195b5d -31059,0xba8d3345a9f8400ae3efc600d22cb61fc77bb1ad -31053,0xbee2b73493f342b1abee4c747be6ad53e02c071e -31019,0xc248d6445369ed303e02d42e5375d55da75b05c2 -31054,0xc36681303c7a41b045636af915486b22214b6292 -31035,0xc56f04ec20dad27c0f4701b14977c2dbe85142ba -31044,0xccd64bf6afcd973dc2cb15314d9fc674f093c7d2 -31022,0xcd0923149a125275d175deef2ee1982b2b095c91 -31039,0xd158ccfabef917ae2f01e454d07e1f2055e44c79 -31043,0xd2bb190dd88e7af5df176064ec42f6dfa8672f40 -31049,0xd3a3fbae792c4ed0aa909ec032d3f14c999b2402 -31068,0xd68b055fb444d136e3ac4df023f4c42334f06395 -31084,0xd9723fffda369d119fbd66a15113144bf76e281c -31067,0xdbf8d32fd81a5bc1d087ba088ac449ad31330691 -31038,0xdedd02e7da507fe2ac09c509da10b94483e80aa5 -31036,0xdf50c6111f45132a1db5ec48bdcd1e8e6c7ce65d -31026,0xe03311d30bdeb60511bae8de135c6524b9576b2e -31072,0xe140e5def72fde807890d89ee4f987d316c25196 -31032,0xe49d19c155a8658f5744ed336f18b7c086979c34 -31078,0xecf6a02a8bfc046ff75e893d8a5ae993b069c38f -31079,0xf48362d1cccdc05b87556d9bb0e180967f2b47b8 -31063,0xf78daf7a425098d255bd142d71bbdd8dcf43ee6c -31057,0xfdbbd3b8fcd27c544f1a292c7163679b60a7bbdf -31066,0xff949c5dcc4f48130b3459dad15a793ab78b698a -31106,0x046fc9f35eb7cb165a5e07915d37bf4022b8de33 -31102,0x200c24184c50796fb72343c7578b6639f515757e -31109,0x2bf40eb00405d62bdf3bbef73d80bfbcc5d0b3ae -31103,0x2de7bbaaab48eac228449584f94636bb20d63e65 -31111,0x3ae354d7e49039ccd582f1f3c9e65034ffd17bad -31098,0x4de5d906326c604e707ece3b9df1ec669c47cc61 -31107,0x68847530e93789cc2e602130f6b53c55e1159bb9 -31104,0x950a8833b9533a19fb4d1b2efc823ea6835f6d95 -31100,0x9aa024d3fd962701ed17f76c17cab22d3dc9d92d -31105,0xaf17879139487d9704e61c4c726525f0c1764be8 -31099,0xc000d75d4221ba9d7a788c81dcc0a4714b4ae9e5 -31101,0xd2a34731586bd10b645f870f4c9dcaf4f9e3823c -31108,0xd417c28af20884088f600e724441a3bab38b22cc -31110,0xee7403d3f2660928b2b39bab80a820975520afe0 -31144,0x1a53b3500216d547186c833c35f39a82053b7223 -31139,0x1cd9276f727f631eccea749371a7df27b8a3d55f -31140,0x294268b71b70d9a9291594c058896f6a9f118b4a -31145,0x3e98673652f7bba325016517c54e41afcc786d1d -31146,0x3ef9f8b4edc0f6fc90dccabd677db2f582a5cb49 -31149,0x40107f542c6d7d9d9c9f7c46fb8874ef59fd3c65 -31138,0x44d195661277f25597fe6b6d9f910d5bbe9821b3 -31141,0x529dbf60ddc8b256c2bf25190de1e06d688f13eb -31150,0x808ab3fa3b1d1ec34b710f84afb313a6dd75f817 -31148,0xb04c1f42d54b609c07f92621d9986e4c65e422fb -31147,0xb7e565a9d96e4dc067f955beddebabc5f7a4cf84 -31142,0xc3dcef82a8c38c20da9feb8bb1d03e9c9d842da3 -31143,0xfe2af3ab07a08a9db276a2fee9e7d8b5d3794d51 -31167,0x0b69157f85fb767676428f0d32866ee2b53ffcc6 -31163,0x1160982721af95351a714a483275229cca10ee54 -31161,0x44ba9bbc9a37769374f76e338ddd349ee4ae60de -31162,0x56edafc97279c53a74c1c105abeebc79e0936c73 -31168,0x85e1c463d154a436da0d6437cc652283762f108b -31166,0x8ba599f4ec87987f2b24244d7e6f4331607a894f -31165,0x9e591541f27bb3b053591bd89a67690727229824 -31159,0xabda8d651de41384a05842991880be08ba0aacec -31160,0xc951a1ad8fa1c307852bcd7f0fc07501cabe89da -31164,0xdb04c87805590d38e231f69d5fd9cd3837419567 -31373,0x0331814f183d6a782e93e46810e76b5d21294b27 -31349,0x0750b6fdada48057dbc7103e1e9286f00f997d81 -31359,0x0d710512e100c171139d2cf5708f22c680eccf52 -31332,0x1033dd8415a282db52f14902e91de6e91868ac6d -31363,0x14c00080f97b9069ae3b4eb506ee8a633f8f5434 -31337,0x16cd38b1b54e7abf307cb2697e2d9321e843d5aa -31340,0x1a7e4e63778b4f12a199c062f3efdd288afcbce8 -31330,0x2859a4ebcb58c8dd5cac1419c4f63a071b642b20 -31358,0x288320624972877cd64a1cb98d1342e7e1785024 -31327,0x28c5d220278429eaf720a4b51d4b9141599039ef -31351,0x2ac724460d3dc68881a5b136f368219f5ed2c1cc -31367,0x3d53aa9e22dc0fe145bf1ea13ee8585cb4e998dd -31376,0x3ef3d8ba38ebe18db133cec108f4d14ce00dd9ae -31372,0x3f125ecd51181af1f344adf76e4271d2923707ab -31344,0x42dc54fb50db556fa6ffba765f1141536d4830ea -31346,0x5199ba2b0ee6cc970f42ab35bbed3a265c542991 -31366,0x58441e37255b09f9f545e9dc957f1c41658ff665 -31343,0x5c8e22bbc48997e59bc83dce9a9356f60bbaf2d9 -31368,0x5e9ab86e99caee6df6578e0fbbd4a915a4a294bc -31355,0x5efe48f8383921d950683c46b87e28e21dea9fb5 -31345,0x615d8b6565d92083ea3ba72639ba025bcb2cb09b -31353,0x62cba6dcdae5992d44cd9bd8989d27718eff5f73 -31338,0x658286fc9605f6ff4d0311d0b58670e26ecb27e1 -31341,0x68d483b75ffbb7fff96385f9a9fa23a7d6d5deb7 -31326,0x6cd24ac05103c2c911347a6d3628d64a9f07eaf5 -31369,0x7097aab446f0cb33381410004f80b90d87e1cc63 -31356,0x7ab641e661a9728913a44e06f6a4879481142ddb -31377,0x7db28175b63f154587bbb1cae62d39ea80a23383 -31360,0x7f27082eabdddc9dc3cc6632c9f594d210b9d43c -31335,0x840b25c87b626a259ca5ac32124fa752f0230a72 -31378,0x8bb4c975ff3c250e0ceea271728547f3802b36fd -31375,0x918261fa5dd9c3b1358ca911792e9bdf3c5cca35 -31365,0x9201cc18965792808549566e6b06b016d915313a -31350,0x9485aca5bbbe1667ad97c7fe7c4531a624c8b1ed -31352,0x97b6897aad7aba3861c04c0e6388fc02af1f227f -31370,0x9818bc9c4e09b1c542235dc969ad1c6136899ad1 -31361,0x9c215206da4bf108ae5aeef9da7cad3352a36dad -31339,0xa014a485d64efb236423004ab1a99c0aaa97a549 -31347,0xa61beb4a3d02decb01039e378237032b351125b4 -31379,0xa7c167f58833c5e25848837f45a1372491a535ed -31342,0xaec8318a9a59baeb39861d10ff6c7f7bf1f96c57 -31381,0xb2378660daebb9ba6c1ce2a38d3ee1d6a6e4dc14 -31362,0xb78ecddbb770fa270f3869777c82e81e05c18a6b -31380,0xbbef7b6921e6b0ca1aa2c32bbf54056ce319dc8a -31325,0xc1626da69573ad647b66a7bd94a65c336f632751 -31348,0xc16b81af351ba9e64c1a069e3ab18c244a1e3049 -31331,0xc2c7a0d9a9e0467090281c3a4f28d40504d08fb4 -31328,0xc7faa06d1d6c0205133635b0dd4961028fe2418c -31329,0xc8711b1206cd3e89799ec32973f583e696cb553c -31334,0xcf5a2934fe43666c24823dfd455c617edff78bb6 -31357,0xe14bfa5575d9906ba35beb15c9dbe5c77bfdd5b5 -31364,0xe9f183fc656656f1f17af1f2b0df79b8ff9ad8ed -31333,0xf1ddcaca7d17f8030ab2eb54f2d9811365efe123 -31371,0xfa5ed56a203466cbbc2430a43c66b9d8723528e7 -31354,0xfb16d8e96c0c6e9b72541bfd8d4c3d9e867c990b -31336,0xfc48e39fed51f2937c8ce7ee95ed9181c2790ab1 -31400,0x0000000000f0021d219c5ae2fd5b261966012dd7 -31427,0x0468531e2b17f03639dc5dac5eb9c534e576565c -31429,0x1f4c8223ffa6e4940527953d1646cd89503ba191 -31423,0x4a0c2d99d2b8028f0d8facde9ac0e631614f4293 -31425,0x74f3acbee8e6a2a5e1f20b57b18989fc5a63c04b -31422,0x7db0dc65dfd8cd6aa5100418dd7da38aac786123 -31424,0x8e442e46d7f009cac7c1ee82d6453353ae7a463c -31428,0xa5262d39bad2c7194dfe489fdb4074e2b5eea307 -31426,0xaab714bc8721789db092ad406517b394503733e4 -31545,0x10294d57a419c8eb78c648372c5baa27fd1484af -31523,0x10cf9d194c37b735cdc1b84bb1c3b78b94586a69 -31483,0x138e30d0f35a14d25317d83863eb7e4e0501a33d -31501,0x143a08f0c2c1820eff9d0e749683396c7bb7a4e7 -31498,0x1bce0aca8b0e4139e4390cf1a7a6eb644000a2f0 -31506,0x1dc389dd3a824cf48433dbadf719262348589f8d -31530,0x1e0bd5c0e3ab80d66b6225263d6739488a2831bd -31488,0x2680b58945a31602e4b6122c965c2849eb76dd3b -31542,0x2c68d03f5e7333bbd21202044778893919cb8126 -31522,0x318334a6dd21d16a8442ab0b7204e81aa3fb416e -31509,0x337b4b933d60f40cb57dd19ae834af103f049810 -31492,0x36033e1fd88470a5192692e01150c3593847fca5 -31526,0x36e8d12227f8c8ef38bb3aeabf816681b5440be5 -31551,0x3892a5c92eafb81cec321b4304495038202ca9e4 -31517,0x393dd19c7da959df0747f77696f88dac77a5f204 -31537,0x3e29d3a9316dab217754d13b28646b76607c5f04 -31480,0x3fa5629b403c4424711e3eb69341833ac1a7d02d -31541,0x41125d4cbe5fa0c9581cbee3fc8ff23207471602 -31484,0x4183c9c22d1ce5f3be9818e97e779e2897f688f7 -31486,0x4186eb285b1efdf372ac5896a08c346c7e373cc4 -31524,0x43a502d7e947c8a2ebbaf7627e104ddcc253abc6 -31534,0x485af43f7ed4257777a25cab7ea3c4fa6baa68bf -31516,0x4b4968a27f0490494ec15def4245942f45790d85 -31490,0x4e7d2115e4feecd802c96e77b8e03d98104415fa -31508,0x54aed38abe51bb39c8e75ac1d9d9335d6136b6a1 -31544,0x56574efc2ae8f9f208442afc8adccc6f7f87e06f -31482,0x58846efadc0819d6f1d45a71d36b48a2ea79fcc5 -31520,0x5bb79889c58612e27ea121b0f6f0f31346f812f4 -31495,0x6076a6b474f336c566e4ba551a5934e3ba5e7193 -31512,0x65c732b5bfc753975c2d92a521bcac633fdae130 -31507,0x65d288d10aedae45f8fa98776bdfd7a67e7a2157 -31546,0x6730545fecd8d5cab50507a6c1e28b181ad75483 -31550,0x6caeefdbc76f8bd2d303bc9fe2e5c08b47414018 -31499,0x7018cbaf278cab1eea3192d0d58ef520f4e5ced0 -31538,0x719fe4734faa5d589f356a8f46e5522e6dbe5e03 -31552,0x7f50923ee8e2bc3596a63998495baf2948a28f68 -31532,0x7f71951283acde80548a45ebe74276ddefaac91e -31489,0x843b66a06a4f00c0703fd2ae2e67c9399f7fad92 -31543,0x870d36b8ad33919cc57ffe17bb5d3b84f3adee4f -31514,0x89926e225a205a5b8c86f2de0be0abe6262c0edb -31487,0x8ba1e9a1ab110e52e0d57ccb013f18140916925a -31481,0x971c2d9ca059a19447bbe59f372aa21eb70e2d6d -31510,0x9d15e5230506bfb66d2a5b18117aa14bf1918d35 -31529,0x9d4c27802238c96270bd3913e5dbbadba92715e1 -31494,0xa44f69aeac480e23c0abfa9a55d99c9f098beac6 -31521,0xa5f4786eb1fd403a92086841515b71610d34259b -31513,0xa7ea9ef9e2b5e15971040230f5d6b75c68aab723 -31503,0xaf7ad0527d3b612445fd57b77d42aaf86ac472de -31496,0xb0269e3016beec7bf08b5b664ad9e2d306316fa8 -31497,0xb4294e2931b4c3cff7c9377f9a62d3d81ad82b86 -31531,0xb7c4250f83289ff3ea9f21f01aad0b02fb19491a -31549,0xba3e8437a06397430036e23ff9153408a3203afd -31554,0xc3365984110db9b84c7e3fc1cffb370c6df6380f -31511,0xc37a88cb293768eac0186c5daf1cbc190820fecf -31518,0xc56209cccd32297460383e3e032fae0636fb4302 -31519,0xc58b2bb06cb16fe99a989fe2b495734fa695533a -31539,0xc5fe32e46fd226364bff7a035e8ca2abe390a68f -31493,0xcb8fa9a76b8e203d8c3797bf438d8fb81ea3326a -31491,0xcd87ba0b93a060500c6ea6c79653c7b9e31f6cb6 -31504,0xd24c4c7cada12cb99812f2c3750b80ae7c06e382 -31479,0xd2b1d91dbddd1c026a0c849d08e1ea284866650c -31548,0xd2c921985a09be0d27e5a4027a77db55d08a1875 -31547,0xd4bd68da9bf9112cf2137d500c37bd9b842eae85 -31525,0xd8320859860f8e116e9bda13cdd60b27bcb6b058 -31485,0xdb3fe4da32c2a79654d98e5a41b22173a0af3933 -31528,0xe04bb5b4de60fa2fba69a93ade13a8b3b569d5b4 -31533,0xe9044a487c758ff8f8c92626bf484f3fb78a1e96 -31535,0xe99a9a717c60f9639b235ede422c27d60fbeb3b9 -31536,0xf0faf495905506f156b6cc2d8810cf3073e04870 -31527,0xf2b581f0ef5c54384acda512d400e3c67a0ed692 -31553,0xf34517ef82c9b512783c44013720a12759f896f9 -31505,0xf911d825a7554b7658e0601f030cd0c6b8005918 -31502,0xfa6a5d33e18cb0d52991536ab15750fb13119e45 -31515,0xfb6c56123e4c4b07a8d1cc9a808b003058235a51 -31540,0xfcd619923456e20eae298b35e3606277b391bbb4 -31500,0xffd61c9597982241fd6d7c3f8a03414a5b922e9a -31653,0x0018453ca52d1d71458ad1ad48a5560a51a94601 -31618,0x032a7c1364cd65f9f96f45a99fe9152e0bd9932b -31605,0x06512e6bab2712fca9f4d123c80fce34de2bc2d7 -31635,0x065399a1e5522af1c9044c18ce60ec70d64d74a1 -31694,0x071682832d213638f8eda67a873262e581a4006d -31637,0x09f1c103e18066e8522f6798d996e848dd41e091 -31703,0x0bba384812a64164fed62c6c40d399c0ac5d99bd -31607,0x0c8daa2760153128d1ac69f6193da05cdb508810 -31711,0x0d82b5d3a8420c285c6d353a6bdc30d164bb50f0 -31724,0x10b3667304130ecc9c972008459249e8141ced97 -31599,0x1279bd692851c32797ab527ea54e376d1f8fb99c -31674,0x16679cd8965c248e02a3459b9d12eda160d4c340 -31712,0x19e60461b1f16947c68bc4180e56273535772ac6 -31638,0x1a0561289120f816580b514d8cbd48c28e2270e1 -31659,0x1db33fc67874bb7b2e3794879f6c8e33681c0a94 -31595,0x20d1c515e38ae9c345836853e2af98455f919637 -31623,0x2399ebac94686377132dbd05fbddae9b22b2a9b9 -31642,0x241f38316199b4332a295b58295deeec60c23677 -31673,0x293c62b0640bfbc06b2e72e3ef5916a6fa284e78 -31628,0x2c9b9b9143a9ef5051a299ef3cc8039b06927093 -31701,0x2f968a73425583b9b6618345d2be9c789d01c8d9 -31619,0x306451e6605e73d04b8c73a459039dba73fc29f1 -31661,0x324cd5bd64c78f71559d2d53e5878e03b31a4283 -31652,0x3b926901b8fcbdea795448bd6eb33c594ab4e1bb -31583,0x402964551e1564aec462ec08b26e788327f5f399 -31716,0x412ea8e33e6ebe26e4433e18a7dfd5f70bb3ec42 -31702,0x418d766706f660e11ed6f5033554f81084193127 -31612,0x4282f89e15245a7f9dc97318ff5b57ce53fcd3fc -31615,0x43569d483deed4287b462747992440df5b999c2a -31692,0x4448856ea601d405d6b0433e13562a6072a02f9b -31709,0x47983df7ba4be0a06353c02bf42d23c857d2eccd -31645,0x47b606cdedfdcb5192be3c47ed8767b61966b0bb -31627,0x490b8c0bf568df81435d766b559f939fe4ff0228 -31695,0x4bad7c3b78a6d3af0d906435379409660e759f4d -31713,0x4c7f9327419452252e2c02f9587f70a8a2efbc69 -31608,0x4da38dc84285badb0d032d9dc443f571e1aefc10 -31649,0x501ccd17ce7c33072a32ed2e0e39e86e7fcf9de5 -31593,0x504e0a50b69fefb62ae20a07d218bb4a49c03e73 -31586,0x50cf95e0de70c5437d1ef0aa626274350a5a2f38 -31665,0x51693339eb4ff71dd56c903c452719b01da51470 -31667,0x51f8b486662ea4c4051eba3bc207c20be96377e0 -31681,0x528c988a9790cd4ebd8a3e64fc96686a6e81a099 -31629,0x54bf17891f32003fbb1daf0a744c091d06ebddcb -31585,0x554f306bd9cd835edb1d6a512c00d7412f8bab3d -31614,0x56549444166f61a404e8963778e7b3a81dc0fca8 -31721,0x58b48d958d324d3e8922e5bff989533cf0c01a9d -31720,0x5984c1417a6ecec89ceebc561b7fdf8af61baa73 -31693,0x5aa68f7c198938e94fdd3d307c665a292a0ca344 -31643,0x5ad323d764301e057614edb0449f470d68ea9485 -31682,0x5cf353788d671bbd81daf8727146c654a4ef3f3c -31658,0x5e231c20e914b55631b3c6dd88e739296ef83269 -31708,0x606795077cef55d61bcfff343ff0919d1dc497a3 -31678,0x60a11ce20b636d840db1f71f4da4f37f997a747a -31621,0x6709e9ca7fd540005e80ed487958f34f1587a650 -31684,0x6832e3351a9e7ac30d114e23f3420ca43d556e74 -31656,0x683a4a2609b582da6b30624b23edbba438916f4f -31600,0x69ced99b806bb61c20bf87af774e57e3dc6a5ebf -31588,0x6c4028d7fd82f29cd97a47e7342f02ca529c5531 -31582,0x6ca3741ddeb8f971465a34bd3e887324ba921c1b -31655,0x71bb2879ac6b9fd2a262c0d9b0e152804b4b2036 -31620,0x722393dddefaebb1e98bbd2fef7a2e5dafe18a6b -31611,0x724a27146a6139ff59b5c4b3283fa14e33bbe442 -31594,0x746b118e089f19d56f5b06ef8e202b5389bbe802 -31648,0x7b0260a113b8094bbb8ec5ab4a714c273cd1c119 -31592,0x7ba2ee0bfb72e768e5e3bb620f2fd2e87b8f343a -31616,0x7f78f48065957d4627a675d1346b65442e783e2c -31675,0x811c20069b5c53a5b4110b7c98834ca7ebf2579d -31662,0x81de38fbc65b07ad02517daa6281550d3661148f -31698,0x81fa06b52803788685508d2997b472045d4f2504 -31688,0x82484aa356fffc824b52df9b61d1fb485b027d44 -31696,0x82cea1306907a8e07e20e82b70d76802c3f90f4e -31685,0x83d288427e55511846877326f38c9773c19f678c -31719,0x847a3c48d0884fb15e590aa28f073a392de6190f -31644,0x86316811e2968d2cd30fe849e7b0bf094e2603ea -31689,0x89888fed5309180606137d6e17236331a3f9d3ed -31587,0x8c2651bcacc632d865b30b19bf4d5c8387f93df8 -31639,0x8c7c45f87bb9e17260eb91b2460eb27f5054bef1 -31668,0x8dce6aa3215d0f5b0960dfbe2e4b84e6d502f46a -31718,0x8fa9ecd14bbcb24ced18e16692e49b388f07257e -31580,0x8fb94e08bc984497aaaf1a545ed455be89f8c675 -31626,0x90b54b1d3218a01c6a56eaedb246add57c97753c -31636,0x93e43115bc2e054b23ae5188b342adfc8a0d2970 -31617,0x957635b87d99b3cb56003c7c5048e980afa720d1 -31715,0x964119f698e60ccb64796bf844fcc8512d81cc8d -31683,0x97260d63705d96373612db4c78fea32bd940dfeb -31687,0x97d6e13c50855d956b4550a5ed27cd2d368341f9 -31699,0x98634dce22240fc8a758032df7e9a8f892e943e2 -31714,0x9ee41cbdfaf9521c66bdd44453c8f056962ecc97 -31633,0xa0a7b727a01f50836c80ceef58b9866e84dbf141 -31609,0xa0e93e191bf33b0b2607390d846689c671929301 -31596,0xa105c597ff23555e212b3694da0d40e5cd4ded05 -31581,0xa26072540af0c5d6cc7ea0867d3d6b8ebbae1358 -31604,0xa61a5b4f77d5a1c59df6e036ae8dec6893bf1ff8 -31677,0xa72f7df6c1454096387dbb74f70b3dac4f0a61f5 -31601,0xa7f71c9c99063ee255786a04134585cadc597c22 -31680,0xab2c3e29c44f645df81b951d5f4921887e12e531 -31589,0xac5625a64e4b94bb157fe5fd9deb4d790a455bee -31672,0xae08118c1de5f67baa549c62160eb0d2f0d30296 -31646,0xaf21e166adc362465a27aedc15315dcfc0c51624 -31676,0xafff62fd985bf1764cd31097dead1078e63b1141 -31664,0xb1335b28d5e9bd5924e93b640c24f106cfb8aeb3 -31613,0xb34e47da0a612ffc5325790dd8e219d870f84898 -31657,0xb4d0b25d28fc55d56dadd6cdacba45779b9a3656 -31723,0xb4f2000e056c78705e1b615ef4f808437cf570d4 -31641,0xb76f511330876a49e57b5bee578da4450433a07b -31603,0xb87f6bbf8d6b9015884e0925c9276f27b772627f -31660,0xb94e8435dece2800a5b6ffd0b2a5bed4926bae27 -31654,0xba4accd78ef2e4e05aa89de24e5072a5222b0f0a -31625,0xbc5c8ddea45e543b20ddc6b84f6649231e35dfe7 -31669,0xbffe3a2323e059cc22944be85fc57a54c38af7f0 -31584,0xc02b55bb2fe3643e1955b13515396ce23b110f80 -31597,0xc55153cd30afdabfe415aed799028fdcef234c97 -31707,0xc7427fa5c6a0ac3541c567897b96116b5bf75d64 -31634,0xcbffe4b94c3ed5bf64938fb2b97b63307ac35bf7 -31630,0xd12b934a3170741acd3b107a631c1a5e66cf3ed4 -31686,0xd4c631e4582059cc36c4fb06f54bdabe953379e0 -31631,0xd5608dee5f0ed1a09b5cc3febb9b3fc48e68057d -31679,0xd5e5d912d7ab139b2dd56a4b9e7aa46e9558508f -31650,0xd6c67ff71a82f1d994d94fa01295467d273d7324 -31632,0xd7cf3ae174e1741f5c36184c076f94cf190394ae -31670,0xd8f8580d7f4ca8c367843e9cb14ed2168cb600f2 -31704,0xd998df250d8bc3384ba0a8458a5a6600bdf2d576 -31717,0xdad89b53e5780683ec041100158c427da02a630b -31722,0xdc6715b0d5ad3add9e9bd4b2c49df2bb220ad44a -31640,0xde9b911a2c3984ba956ae2415a3e938aac840e13 -31706,0xdf6317c47addf3e7655994330357a8fc9b44dd1b -31624,0xe29bc35b3113c550861c4a8e273717ffa25a483b -31591,0xe357dcdfbb3dbff3a7587f7ac807209d4d9f714b -31598,0xe4294c9698ca44f244575185e669bdb447daf2e5 -31725,0xe64b4eb5eec6343887c4683da31f0ca62ea39ce3 -31700,0xe79c3d7ba6a30479db6edc0b4e1295a3cd19ec91 -31651,0xe869319d5154fbb8e4f858f471179487e6cf184a -31602,0xe9eea1d5b86e7365fe6cbcea84c8851df781650a -31671,0xeaee772489648614316ab1323ee8db20890d9bf0 -31666,0xed9b52e6d2df4ad9fc258254e1e5ef5ad0b3ca3c -31697,0xeed97ae2285a6ae98c6050489404477862f067f7 -31663,0xeef314399f7d0c3e7eadd6bdcbdb8767176fc6c7 -31710,0xf00b91839ff7a6ac6dddac7e73d2f222c19a9ec3 -31691,0xf54062df69e709a32cf816589986c91f84814c8b -31647,0xf6087768675544a6218b6d5a8043d4b60301ba1d -31610,0xf73716c6721dc2f1a621923dd71f54fc6703f4e7 -31606,0xfa36d63fba328145cb333411f2da1cf49ad691d0 -31690,0xfaa828dd4eee475ad5a1c56741cfc659b53caebb -31705,0xfe48b9b01f145e9c26b20ce648b37d687740bf27 -31590,0xffd6f1b3bc223c6d3ab4af32167b4a93a148ca39 -31889,0x02b9e99a05458d763256b977e61c1d947a5a0d04 -31904,0x04432a6fd94e8bc8559d10ac1d98f15cd9c08363 -31928,0x07aa6cfd846ff4e39178f414dc27ec890d9622a2 -31931,0x19305fa7b4832b7f7545cc752c71f8cdf9f60226 -31896,0x1e634e9622808a53f3e9a9c87a1ff9dabc46455a -31894,0x1fa677ca369b97ab30707373531cb050b1c3a7c6 -31901,0x24fa2b48178f4acd577230b3ac30b935195dcaf3 -31897,0x2569f66b7acd8954a7b3a48481823e30c4990742 -31903,0x25855b3e23c770f1effc17bfb1bbb111eed668b5 -31912,0x26b65739eed146252f5fdffecabbbc72f7f581b6 -31899,0x29e146346242e3d206dd36a79e274c753bffb15e -31891,0x2c0979b0de5f99c2bde1e698aeca13b55695951e -31914,0x2d662befe161379837829944d5948ed84ced659e -31918,0x31eef1a7e20504d321e50d48fc017c04ca832f86 -31909,0x3c8bef5f8df313ea6cb874d5035d3eeb963d8dad -31930,0x4609795281f8c2d90de9fd4a18609198819513e7 -31927,0x4aec980a0daef4905520a11b99971c7b9583f4f8 -31921,0x528d21fd31b0764befbf5b584f962e3ce7dda296 -31929,0x55c1688587e6dfd52e44bf8b7028a8f7525296e7 -31908,0x61baadcf22d2565b0f471b291c475db5555e0b76 -31893,0x64839de745d09d89591511afdefce4ee2cb03924 -31906,0x689b7d709106bc488f872c50b688f058048536be -31905,0x722969a3fdc778a5cc7cbc8dc8ae3e96a288f853 -31900,0x780f70882ff4929d1a658a4e8ec8d4316b24748a -31925,0x7abdc254845300f5cb8571d65668d3f1310969a5 -31926,0x88fdc711eff5877b464d299c7ac3077135c6c5ca -31907,0x8e8e28c43e72438a0b217dd7b78440fccf682e9d -31902,0x906d2352d02d35b153349c5861d7ea4e7e9162ec -31917,0x91fcea33d02d11621b7f90ebd5e44ed393ed7b5e -31911,0x98acc73577c00053bc2c168ade5fead073313022 -31898,0xa27a77d55208cd3865e286df4698d750b99898b8 -31895,0xa621eefaa0a6b23fa4c0111e9316cca4b53469e6 -31919,0xa7a86ec3c266435c580354d8c9a33b1bc91697a7 -31922,0xbca527108bcc3de437c5bfdb1a5489de26deeae0 -31923,0xcc2a483d21c5eb783eca41b0933042ad9ccde6a8 -31915,0xe30bd5fb0a652a65eb378ab952a621f0b509521d -31910,0xe5bd5fc85d214ae28474bbe10e2dfcde52577b4f -31920,0xef1c0b40016d0ea08e4f409f48e618b41edf66d9 -31890,0xef5e00f3d204808b5d4e79b0f8fe197af2de18bd -31913,0xf02a67988adb4b92365911cd2522ef4deb1c9bc9 -31924,0xf1f2fb3c8e90152e75d7675f5a7f3f9f95e65a81 -31916,0xfa0de0e65292c12ed22c80da331ca2806a84215d -31892,0xfe757a40f3eda520845b339c698b321663986a4d -31950,0x0a7072c28a23b831292b863d3412abe897b954f7 -31959,0x0d5ba4da08f76bb898849544d73c90233ff4267f -31977,0x0f6ac9aa13709e3f1453e1f71bc47fdce1fa4e75 -31970,0x0fe2e4130e564c4e0b9f7652d7773018869b7adc -31961,0x127ee750a56a64c39c2d7abb30d221c51cedbe12 -31952,0x1d8b85d36189cab860f6e45f7835355ee9e0a391 -31969,0x2ff3791c3f933dcad359cae024bbc80847ad2deb -31972,0x449d131f2ae53a337fe5a21b3d749e603ce6454c -31975,0x4dd2886836eb5966dd2f5a223182e7889cd7f8a6 -31960,0x55558e3fd6a3bc2bfc4654d8e715eb548a0d39a4 -31955,0x56145ed571ddf7b7f4881eb00d5fed8b1846f912 -31967,0x5ab0ad95263c9ebdda6b17baed1bc28f97690f26 -31949,0x5bd138d20815a11022f285e817349dd348b933a7 -31974,0x65368b6f03cdb1d4295be2804da66eda634c2541 -31964,0x68aa498367380e2025e8e8782c50eff7766db052 -31979,0x6bb5e2f80f929ab467335f9a8ed10585619e24f5 -31963,0x6ee3b0daef79522784d8e63ebb62f10f8008eb3b -31957,0x75e47283a447a8776066a33695883b9c23979fa5 -31980,0x7cdc2c52a070762eb7000065dfe40c828acc96dd -31968,0x804511ed6c51582e8c62ce04afd824cff6b6872b -31954,0x8d0c5c82a5d4fd28a10eb43d4caf0c94386793b9 -31973,0x91bd6f598549360004d0a4d436bf4c67730850f8 -31953,0x95a851f7c0a73b3121b3a1724732c8114b490512 -31962,0xad96e6c9e5d2b5639d3490abe23274660fa876dc -31948,0xbf4b4c46a2070f2dbb60d41462ef704751c5b777 -31965,0xbfde4fdc17a53ac0ba708f1e763643179cdcc8e8 -31976,0xc2861530884607c5d2d327106ca00f252f934962 -31951,0xc752b69aab7b70ff87e01580fc6426a7d55a42b0 -31971,0xcc35ccaab98a31fc81c26ccc53da7419de9869b7 -31958,0xd850f64eda6a62d625209711510f43cd49ef8798 -31947,0xe92ea17a074643326c8b5f11f579997eabfcd428 -31956,0xf241a2de88200f05b8154c0dd9f019c7caf434a7 -31966,0xf403d00217b1c97cde7cc08b35f0d3c8f939bf14 -31978,0xfa84f31f3187b921bab4d3e3206d0b0de7a3d94b -32032,0x065103250c709a36b47469151c94fa60046ca156 -32028,0x0abf9e4dcf80198d022ed795e1ea09fee235801e -32016,0x0c5047e17105a744f3d1f1deddb4677748afe9a1 -31992,0x16c4ba98623a1b05d35b9923fb00e9fe02d0b47f -32026,0x18e6a996279e239eaf744dde7bf25e9550720c41 -31997,0x21d37ee5924a0549eb9bb58dfdb7af9a1c6fe792 -32027,0x27271ecfdc3548dfaf31b08f09baa2bbc54f705c -32013,0x2d5375be6b1f1d0f23824ef59e7d05f160882b35 -31994,0x2dd5a62a5d4e4d20622061c823f9648e3078012d -32031,0x2fca2e4edbf97db14d20654e258a25e9d9cb92ff -32029,0x331df88caaf0043eae0d5c039b77985f7bf2354e -31996,0x35e801c62464a379b09d1eed21b5187522712b6d -32006,0x37115cbfce229f3d65073dd155bc0cb4a39d9454 -31998,0x3e70db52d48b03b5ff6e3af12cbe1858901c2854 -32010,0x42a461e5e88426c8b969b43fae8d3bd47219bbf2 -32007,0x43f064971d9c851a0377438ac81da0dff3909d44 -32038,0x441e3985dab25950a7131e5afecfae83fec8ae77 -32014,0x45e9bd93a071c31aeb03ff33b3035202189935ac -32012,0x46be78760cef99dd6d7cd087a499344803063b6c -32034,0x48b98f3df754b00a8337794ed5d8f39eb2bed2e9 -32023,0x5397d5a1c9ff2bd260d0f43c972f72b72c6f839e -32005,0x53a5dd07127739e5038ce81eff24ec503a6cc479 -32036,0x555ab9f6d6677df4f7e81fc88dc3b933787a74ce -31999,0x700d012b384666f88db9630f55fd751d30fd3fef -32033,0x7dab68bb3ab8574bfc27d390c97aacbb3fa5e063 -32037,0x806aa6b58169e5f5f2921a1e7931998058fa0ba6 -32001,0x8edbb16169e4ca9e22d41426d9448c3c0f8e4961 -32039,0x935cbecf58a7a15cfa99fdbe53ca10268f000a14 -32024,0x94dc9ec5a8a62f1c1dab495d5fe65f3a070f2d27 -32025,0x9cdee51e8683bdaac6f0fff59c048025cd9acf8a -32017,0xa023e8911bf926cbdb9fcd8ffb3b49d54da1d756 -31995,0xa1fc967fda140da8ee241cca5191c746d818f75c -32008,0xa68a4e6fce6ac77f82253c8bfd9454cd0d5576ac -32018,0xbc1066cc79835434df0e0748c1b567601203c5fc -32000,0xc24205b12fb056822b2b2c42b5ee18272a0a8e74 -32015,0xc53d960a57f966ab739edccabc0231c383b6caea -32002,0xc5af896feb387fa6a3797c7748fe96467c03f800 -32009,0xdadd97fafae613f53cd225bf0ad973dedf79c43c -32030,0xdb0c3c4fc16b1781ad69b3109f38a40feef6dd50 -32019,0xddf6ca3020781d79057483b4b3facca184fa2887 -32004,0xdfa7339e1924086b4ddda6c996df5092c39d95bb -32035,0xe0a80503276ac46ab3d28f58c46c45c39a7e393e -31993,0xe10daeb5f173fcad4b91d0299e7f725ef7b31045 -32022,0xea3a4c6ab79e79733120a3fe5558b4fb05c746bc -32020,0xefdb18850b7ad0234da2d1afb5aa68c150def5b3 -32003,0xf2d7e526098c4ea311c54cff867e712306845eaf -32021,0xf3f837f7b8c4bbdbb6c6ba5398c3b1846bae1252 -32011,0xfd5f3f639ccaa0cd42b0a19f497590bd715fd414 -32086,0x072819bb43b50e7a251c64411e7aa362ce82803b -32062,0x09afd24acc170c16f4ff64bdf2a4818c515440e8 -32094,0x168144503bfda917657a6ccd079d1b7d2e6bf0e2 -32077,0x235fa7590749c0c7240926e4cd0bb8eaecdbd3f3 -32067,0x255483434aba5a75dc60c1391bb162bcd9de2882 -32065,0x278d6b1aa37d09769e519f05fcc5923161a8536d -32081,0x2bca5496f0067c1112a2e1f982d9655d26adb8b9 -32088,0x357fe84e438b3150d2f68ab9167bdb8f881f3b9a -32092,0x3bad7ad0728f9917d1bf08af5782dcbd516cdd96 -32072,0x55743451b0921b555b4aeaf878c2f3e37aa41755 -32064,0x56e2d1b8c7de8d11b282e1b4c924c32d91f9102b -32071,0x59485d57eecc4058f7831f46ee83a7078276b4ae -32078,0x5be04e53b465c6fd89ecff3d36ddf666d198e31a -32087,0x618c854204834e110eb77f092e7063d7ae2855d8 -32080,0x6f26bf09b1c792e3228e5467807a900a503c0281 -32084,0x79e5462a3544d122152595cba5eefc617c875190 -32070,0x7cc13c5c3a38718af2a7c36d8311f80ad47ef0ab -32082,0x89d63bb8add89d7da589cb2a677411dedce659a2 -32085,0x8c853a25a19371eb51e67896f228b8a5ca04acc9 -32063,0x9fac5d01c78d290f974b30fbb84016dfd526c529 -32075,0xa4199d73ae206d49c966cf16c58436851f87d47f -32061,0xa420b2d1c0841415a695b81e5b867bcd07dff8c9 -32091,0xa5b9d8a0b0fa04ba71bdd68069661ed5c0848884 -32095,0xa75c77dc60eb0f3d8ab422287656e76d83d55f34 -32079,0xa7cbbadcf7d95b34fafe317d023d4635f6cd1c58 -32089,0xad5aa9f37d5bbfcee45bd9f43cbe0018ceec7a4e -32059,0xad7c5516b25661e0a204646b08024cd82ffe6c48 -32060,0xcd5fe81278febf3a9323efc9f68aeccaeae8be2c -32093,0xce8641af1d5de04d8b252db42cc2600e33fe5980 -32090,0xd90a27eaf88c81f50e30e0e0333036838814181e -32073,0xdbbd1d5d119c415449c0651cc948ecee952fb304 -32076,0xe04487c1a3d3a100464be87482be941816bb94ca -32069,0xec88d3c08e2939562ff8188b4e30a52236c3ff09 -32068,0xef4998e4cda2232c5f1824eac8c5060f28bfaeec -32083,0xef8b46765ae805537053c59f826c3ad61924db45 -32058,0xf2be21c20e1be52697d02afe4270644bcb1f9aa5 -32096,0xfd7f000c26b7205c1a3f9b846f44dc11ff5e6eb8 -32066,0xffb14e4c6f5917586e8eaa509d3504f5722f678c -32215,0x04146736fef83a25e39834a972cf6a5c011acead -32193,0x0bf531afdf4b8d937df2a802eb43aeb608e63d66 -32235,0x0c963a595afb4609c5cc6bb0a9dad01925b91886 -32251,0x0f08217ec7426e5a8e2b519cbf44d262bd6c9d48 -32160,0x155fcaf3ed878daf7aeb259f8eb65b23d98c432f -32153,0x15f57fbcb7a443ac6022e051a46cae19491bc298 -32213,0x193d7e62f5b7b178a7d50cc7b9acfab919e2dae8 -32162,0x198892f3709a6a03f4de1165057f78a9f2741b6e -32194,0x19c08f59c4d75407652a6a970078e79d117d0fd7 -32228,0x1ca9f1a7cf93284fff9ec9a7eb627f9de04a0ea8 -32167,0x1e188dd74adf8cc95c98714407e88a4a99b759a5 -32212,0x1e217d3ca2a19f2cb0f9f12a65b40f335286758e -32159,0x1fff78fd2e139bc1315711dd3ea176bc7028635a -32173,0x233332a01ebf20f770883ebff68374b015accb9c -32168,0x2977e3346569805caa3e75cb17b00facf7bbd982 -32232,0x2af303aa6dbcc94f16a6bbc732b6575f2294b357 -32226,0x2ed641367f16f9783666409be7d083c8c49cbec2 -32245,0x35467bbd152a0f4d9166e649728c101fe4806eaa -32243,0x38091ad1880c21530d5b174b10d1ce24b40a584a -32216,0x3a163ed7044a33af1b331ea4a2fcbbc26d63b7f9 -32236,0x3e6ef9e97147c266c5bddef03e7dfba7a167d853 -32250,0x3eb5fc2a08ba67cb918758e5bcdd749c70fe7e93 -32224,0x48686c24697fe9042531b64d792304e514e74339 -32165,0x48d168ae7418d85ecbab38dbd387332551d79bb0 -32185,0x4c44b16422c4cd58a37aad4fc3b8b376393a91dc -32156,0x4d526f103307b548227f502655f7b80796b64f52 -32220,0x59be9fcb06cc9c8bee44b9861131d4614fb11bda -32161,0x5fbaf14a1c65cea90b25fd4c186a1669bf421d5d -32187,0x601c5634c8435ceea8e87d0b5404c206f1e305db -32207,0x60bbefe16dc584f9af10138da1dfbb4cdf25a097 -32240,0x610e598e5267eabe099df95d1301da85dd3f4ad4 -32198,0x640605cb9366c98b6d324d8cb04f98b363b76521 -32158,0x69bfea0307a071c3e6cd217486b16d20bd58c7b1 -32233,0x6e4358c889bb7871061904be31fe47c3b8b7f442 -32197,0x6e93686d34a9ebf7c589998a576ab603719500ef -32246,0x6eb1709e0b562097bf1cc48bc6a378446c297c04 -32241,0x70b868191b588384b96233cad7c27457026a7493 -32154,0x70becf593a0f094dd7d5f8b8b48fb8582c0ec575 -32171,0x710853aa41428c3e0f2bb9258e3e6c9555b25a51 -32170,0x741a2378a8e003acbadbc21506bc624062ace36e -32200,0x789004ede61c9f855a0ecaad7f0616bf23f0c952 -32211,0x79533f85479e04d2214305638b6586b724bec951 -32172,0x7ad0e580d8458bbef71ec6a1755c59651e1eaaa7 -32219,0x7c8fef8ea9b1fe46a7689bfb8149341c90431d38 -32196,0x7e05363e225c1c8096b1cd233b59457104b84908 -32152,0x8176c5408c5dec30149232a74ef8873379b59982 -32238,0x86e761f620b7ac8ea373e0463c8c3bcce7bd385b -32155,0x8bee5db2315df7868295c531b36baa53439cf528 -32234,0x8cbc6f9811b266268b94b84afed0e5ee26d61ddc -32189,0x8cee5b335f450933b4720b5b84e6125d4225fb62 -32188,0x8e24bc43f2dbf3105068635d50d72158bad9d419 -32199,0x8e266f8310e047b9900b60132e4767ffdd0878bc -32217,0x93503ab9f3aa708b757caf3238b7673bab2e3409 -32164,0x98bf3e7da6f5a81630730d538715e35d8c0d3ede -32186,0x9ca03febde38c2c8a2e8f3d74e23a58192ca921d -32210,0xa32d03497ff5c32bcfeebe6a677dbe4a496fd918 -32176,0xa3372cd2178c52fdcb1f6e4c4e93014b4db3b20d -32183,0xa3ba2164553d2f266863968641a9ca47525cb11d -32237,0xa454406be7690c78ee96f99790a8b509460854ea -32230,0xa5e9237b53c44d4b3de26e307c2034df1084c94c -32247,0xa9ba9852a249aa9c596c990cc409cfeaf653fac0 -32163,0xaba668a0bf9292722cb9639f132b59b4ce6929ba -32242,0xac6c439253987be9aff6aac047ec8501ea13d512 -32166,0xac9c9b9c354de58ca34420f79bbab67defd8324f -32225,0xacdd6e28b7440fe17eefd927337480dd1ea7a97b -32179,0xae031bde8582be194aeebc097710c97a538bbe90 -32244,0xb0da6e0cdb841eeb8b38fece81fd7e6e0cb5ed2b -32223,0xb2c3a9c577068479b1e5119f6b7da98d25ba48f4 -32227,0xb6957806b7fd389323628674bcdfcd61b9cc5e02 -32192,0xb69c154030ce89340cdceca8eed93283499a8276 -32182,0xb7ddfabc87c7d5b3f3c1be8559f4c4704fa57754 -32206,0xba2edc83f09b82036a071bc37425ea647dd7989c -32190,0xbb7211f6591cb763de27a1205d4678e3616409d5 -32229,0xbd73aa17ce60b0e83d972ab1fb32f7ce138ca32a -32205,0xc2fdefbd7816f7b9163196fb546e9b383eb22a2b -32201,0xc568a699c5b43a0f1ae40d3254ee641cb86559f4 -32249,0xc7a161cfd0e133d289b13692b636b8e8b5cd8d8c -32222,0xc845c5baf57f61eb925d400aebff0501c0e9d2ba -32181,0xc8f5eb8a632f9600d1c7bc91e97dad5f8b1e3748 -32157,0xcf4f8e9a113433046b990980ebce5c3fa883067f -32184,0xd076f5426bd1f4cee4054aba60331fa1d075dc49 -32203,0xd69e75c1c2a0f2838a6bba8bdff9d08c8f137cd9 -32221,0xd7f347807c82e94018b5e093cacec712c4695718 -32209,0xd825d06061fdc0585e4373f0a3f01a8c02b0e6a4 -32214,0xd8284e7bf0ac2171b5cf6ea92edd90158ed9e020 -32202,0xd8cbd5b22d7d37c978609e4e394ce8b9c003993b -32248,0xd98bfb05dd6aa37ba5624479eb4264de9a3384ee -32169,0xdfa94f2ce5bc1961f2604338f97f468592ca293d -32208,0xe685a35347ddc8d58ed2838a291c8a66d5bdb0ca -32204,0xe802823719f9d2520415854e6f95bae498ff1d52 -32195,0xe8681a8c8d8e24b65526c2f94fd5ef9d3ec18726 -32174,0xed3053971c7905c799111342dbe1a2c8883d776f -32239,0xee2f020885475759ecd6ec24d922fbb5998f143a -32218,0xef2dbdfec54c466f7ff92c9c5c75abb6794f0195 -32175,0xf1f201097189f84ef305c3720208fe8c618ad227 -32191,0xf24f4cf3e272ef412d0d4764909141b39cc878cf -32231,0xf4b36812d1645dca9d562846e3abf416d590349e -32178,0xf675bb0c1cde2179d19b2ea0e6e5e9d2053b3d87 -32180,0xf76da2d3656e1cc5ca2fbb9663c89f7d8aae20fc -32177,0xfd2281453d970964c46b58cd4ebf50b45e820d58 -32344,0x02d16bc51af6bfd153d67ca61754cf912e82c4d9 -32285,0x0761b0827849abbf7b0cc09ce14e1c93d87f5004 -32339,0x086a21a16ef1133bf58a09a77e2a9e07d3b95c1c -32319,0x0972954923a1e2b2aab04fa0c4a0797e5989cd65 -32316,0x0be2ae2f6d02a3e0e00ecb57d3e1fcbb7f8f38f4 -32329,0x0d63128d887159d63de29497dfa45afc7c699ae4 -32276,0x0e3239277501d215e17a4d31c487f86a425e110b -32288,0x1052ef3419f26bec74ed7cef4a4fa6812bc09908 -32293,0x149db7afd694722747035d5aec7007ccb6f8f112 -32321,0x14abb1f28b06272c57c37723d0e671d1c3326679 -32291,0x14c3ceee8f431ae947364f43429a98ea89800238 -32314,0x15ab173bdb6832f9b64276ba128659b0ed77730b -32338,0x16ed50f96ea655cb03638d7054e62e42afb7b4fa -32300,0x182e8d7c5f1b06201b102123fc7df0eaeb445a7b -32307,0x19dc38aeae620380430c200a6e990d5af5480117 -32306,0x1fbe9a18ccb99e57a930553e788705aa0857757a -32311,0x23ce76645ec601148fa451e751eeb75785b97a00 -32279,0x27e88aeb8ea4b159d81df06355ea3d20beb1de38 -32358,0x28efbcada00a7ed6772b3666f3898d276e88cae3 -32361,0x2a532fc8cf9a72142ea8753a0d2ab68098c19585 -32308,0x2b0860e52244f03e59f12cfe413d6a29bc30b893 -32363,0x2c1fabecd7bfbdebf27ccdb67baadb38b6df90fc -32359,0x2f2afae1139ce54fefc03593fee8ab2adf4a85a7 -32292,0x2f9db5616fa3fad1ab06cb2c906830ba63d135e3 -32362,0x2fa570e83009eaeef3a1cbd496a9a30f05266634 -32282,0x2fa5f5c96419c222cdbcec797d696e6ce428a7a9 -32278,0x33e0c225a54a0a86e9b267e90a90de8cbb0fdb8f -32336,0x398633d19f4371e1db5a8efe90468eb70b1176aa -32298,0x3a464f746d23ab22155710f44db16dca53e0775e -32281,0x3a867fcffec2b790970eebdc9023e75b0a172aa7 -32351,0x3de63b62bef9da290f51f856ca9f3db4225edc05 -32356,0x4023f865e24a8ef4d3db85858d7bfdf2feaeb5c3 -32310,0x4a7841c848442d7830a336303c196abe03f23646 -32295,0x4b44e4305b42405382b7bec717f64d0552a9d9fe -32333,0x5188731ee98892d1e7d98b869d811624dadf94eb -32318,0x552d41c0b5c774f529c956e7cc77d0e054d7afa8 -32286,0x5e01d8f34b629e3f92d69546bbc4142a7adee7e9 -32340,0x6119b76720ccfeb3d256ec1b91218eeffd6756e1 -32297,0x6119e37bd66406a1db74920ac79c15fb8411ba76 -32317,0x61ddb465eea5bc3708cf8b53156ac91a77a2f029 -32309,0x64d8b60c2d5f4ff35d61b3f039df327618fd0896 -32337,0x652e2f475af7b1154817e09f5408f9011037492a -32342,0x66dc49405ae2956f7e87feaa9fe8f506c8987462 -32350,0x6fae4d9935e2fcb11fc79a64e917fb2bf14dafaa -32355,0x7082e975fde8d85b0c56b4512b437effb46f0a09 -32327,0x719952fb48d412ca65e8d129f6248a75de770b9f -32325,0x71b2644183eca86401c13577f5332fcc5e48352a -32331,0x79b3d752cc9494ecb93800712471a7a62954c8ae -32289,0x811808dd29ba8b0fc6c0ec0b5537035e59745162 -32303,0x8202df0d3a5ba6d65fabe63122faacc1529e054f -32302,0x8249cd1275855f2bb20ee71f0b9fa3c9155e5fab -32332,0x82825c0884558c9c5a94b545e7563c95aba49197 -32323,0x8428a1a7e97fc75fb7ba5c4aec31b55e52bbe9d6 -32353,0x8b83fefd896faa52057798f6426e9f0b080fccce -32287,0x8f1e22d309baa69d398a03cc88e9b46037e988aa -32330,0x8f7454ac98228f3504bb91ea3d8adafe6406110a -32324,0x931dfcc8c1141d6f532fd023bd87dae0080c835d -32273,0x99ca8c74ce7cfa9d72a51fbb05f9821f5f826b3a -32357,0xa1ac41d8a663fd317cc3bd94c7de92dc4ba4a882 -32341,0xa6f0a37dfde9c2c8f46f010989c47d9edb3a9fa8 -32299,0xaab1d11e2063bae5eb01fa946ca8d2fde3db05d5 -32349,0xaad207a0fd7a4e3c927ccc78ac8134baf586b852 -32347,0xb3fccd379ad66ced0c91028520c64226611a48c9 -32334,0xb439336498b4e3588b2a54be774b674e64b8ae9c -32274,0xb4caf2ca864b413daa502fa18a8d48cd0740fc52 -32322,0xb57560ad9b5b608d3c24ded7a4461bb1c285d4b8 -32335,0xbc9cd961bf6c224fac51fb049ab6788e38e4a9c0 -32352,0xbdf3db1b31cf50463ae2a1f9264f9f392472353d -32284,0xc5d6acaafbccec6d7fd7d92f4509befce641c563 -32294,0xc760a72a536fdb936855612b13faaae2c123f772 -32328,0xc9c474707b587b5a2bf8f3fd51bfd6d45447651e -32290,0xca41932888d323b3d99f5ea48f86d502055c0322 -32305,0xcdaebcc592da5c982b05e95039ff5f3467420223 -32280,0xd23115b7fc4c606fa27d431ee76ec8e292ebfe10 -32346,0xd83a4f747fe80ed98839e05079b1b7fe037b1638 -32283,0xd88e9bb12f9e103f8a273aae954cd023d934f17c -32277,0xdc47edc036daae45d3f019ccfd443bf72fbd981c -32343,0xdfdf9b8b608e7d2bf7fc2e464c1cdd284ec43858 -32275,0xe047cb95fb3b7117989e911c6afb34771183fc35 -32348,0xe0c452dda7506f0f4de5c8c1d383f7ad866ea4f0 -32315,0xe10de0d4f9889597786509600a92ae597551a853 -32354,0xe1d1d8d58c4fef736c99655630f24ea48446435f -32320,0xe5f7e241f9bb6a644e88f2ca38fc373196b5392b -32364,0xea820f9bcfd5e16a0dd42071eb61a29874ad81a4 -32304,0xea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d -32360,0xeab3b53b08926182324bf7e12d30a5393c394ce3 -32301,0xeb9fcfdc9efdc17c1ec5e1dc085b98485da213d6 -32296,0xf5739a4af21346aa937bf7feb5d3b21c2d230138 -32313,0xf767d698c510fe5e53b46ba6fd1174f5271e390a -32326,0xf7af65596a16740b16cf755f3a43206c96285da0 -32312,0xfb9e40d811cea562cc8a322b029ef2bdcc3ef6ed -32345,0xfdc940d5c148ba038505daa5524730644527229a -32579,0x04a8d477ee202adce1682f5902e1160455205b12 -32548,0x0aadee9418641b5749e872edef9844200143865d -32585,0x0c2c95b24529664fe55d4437d7a31175cfe6c4f7 -32517,0x1301ad350fdcf82ba0441279b197cd7d9682b0d2 -32563,0x15196d30bc37d2fc5c718ffcd9d7687d76f3ad1f -32539,0x16623256f3c216025cbcab1280ea6b83c5e8a148 -32559,0x1d69c48a35ddd241e72a31db0e637676d89fc553 -32532,0x1ddaf95c8f58d1283e9ae5e3c964b575d7cf7ae3 -32587,0x209ad99bd808221293d03827b86cc544bca0023b -32576,0x29081f7ab5a644716efcdc10d5c926c5fee9f72b -32528,0x2a44a04cee1e6e49881e77874e3d7bde7c6a2db2 -32567,0x31a239f3e39c5d8ba6b201ba81ed584492ae960f -32560,0x398808db36da6ba4e30d4fb88d72c3473921bc6e -32570,0x3cd1dfb81c50a5300c60a181ed145a7286d81e0a -32564,0x3e05eb6e12a3c9ed5e46a710bcf052efd6d73fbd -32541,0x44b864b92043a960313f3c94bd6db4da202814f6 -32529,0x472337f1c9c1c5497c23dd8060df8729f33b5543 -32524,0x4aa694e6c06d6162d95be98a2df6a521d5a7b521 -32537,0x5124efd106b75f6c6876d1c84482d995b8ead05a -32522,0x5366898b1b520a04ea813525930b36a444ae83d8 -32583,0x5e76e98e0963ecdc6a065d1435f84065b7523f39 -32553,0x5f4d15d761528c57a5c30c43c1dab26fc5452731 -32580,0x6b4e260b765b3ca1514e618c0215a6b7839ff93e -32547,0x6dd4b295b457a26cc2646aaf2519436681afb5d4 -32533,0x6f143fe2f7b02424ad3cad1593d6f36c0aab69d7 -32543,0x70371a494f73a8df658c5cd29e2c1601787e1009 -32557,0x70bf6ec6fca41a7d08dcbb9909985ac0a4510b5e -32574,0x729b3ea8c005abc58c9150fb57ec161296f06766 -32575,0x764594f8e9757ede877b75716f8077162b251460 -32518,0x76d3030728e52deb8848d5613abade88441cbc59 -32546,0x76fb31fb4af56892a25e32cfc43de717950c9278 -32581,0x7748d38a160eeef9559e2b043eaec5cfffce3e4c -32527,0x78fd22a13d0b71e55dd0f0075189f97375db0bdc -32573,0x7cb7fdeeb5e71f322f8e39be67959c32a6a3aaa3 -32534,0x7f21433e684339c92db87d45eda05379781cb62d -32544,0x80b2a024a0f347e774ec3bc58304978fb3dfc940 -32540,0x83ff84900294ee4c3cfc3c68f6cb965c337044e2 -32520,0x86b4d2636ec473ac4a5dd83fc2beda98845249a7 -32561,0x8be473dcfa93132658821e67cbeb684ec8ea2e74 -32569,0x90127a46207e97e4205db5ccc1ec9d6d43633fd4 -32571,0x97742240d6ebe32198b315d30c73c3d2ffc9ce21 -32516,0x9b18398a9bea8bd4c86722c3f467809842aa241c -32523,0xa0737d429c549724c0be02b9b58cf268af33bb52 -32554,0xa25d9f14cfa40d3227ed9a48b124667ddffcfddd -32584,0xa3e44d830440df5098520f62ebec285b1198c51e -32525,0xa5595a5664c59e405af3455040ae759af4c225a4 -32556,0xaa84d489f71d2a8d71e992143a2de8b3aa54aaa9 -32588,0xadf86b537ef08591c2777e144322e8b0ca7e82a7 -32562,0xb04280b09f47cfd81906817505f24cefe1ba86d2 -32582,0xb1ba0787ca0a45f086f8ca03c97e7593636e47d5 -32526,0xb2289e329d2f85f1ed31adbb30ea345278f21bcf -32586,0xb77fc84a549ecc0b410d6fa15159c2df207545a3 -32578,0xbcb167bdcf14a8f791d6f4a6edd964aed2f8813b -32530,0xbce1c8d32e432025dd90bdf0137b1089cd005b7e -32542,0xbd83ddbe37fc91923d59c8c1e0bde0cccca332d5 -32545,0xbdd4f79f79fe7573a4858bf82c22599b1d805597 -32531,0xc7524b08101dbe695d7ad671a332760b5d967cbd -32558,0xd1b3e25fd7c8ae7caddc6f71b461b79cd4ddcfa3 -32572,0xd410270dadba6cab0b3523136b79ab3a19ccecc8 -32577,0xd9ca4878dd38b021583c1b669905592eae76e044 -32568,0xe63eaf6dab1045689bd3a332bc596ffcf54a5c88 -32566,0xe88fb4eaf67ea87bb458e24c94bef0eb02b5f449 -32521,0xee1bac9355eaafcd1b68d272d640d870bc9b4b5c -32538,0xf1485aa729df94083ab61b2c65eea99894aabdb3 -32565,0xf1cd4193bbc1ad4a23e833170f49d60f3d35a621 -32536,0xf75e9aa333e26be5cca13e7029bdc569229f48da -32535,0xf86c9e87d16a6556f81275d5157eb46aad4b6a25 -32555,0xfe76366a986b72c3f2923e05e6ba07b7de5401e4 -32723,0x056ad701bf8ca01992b61d6ccd5efe32b0db4217 -32745,0x0614424f670fac7d3e36f82303e57abc27c99a40 -32734,0x09d2a7b9f4c71f118305000c2ede4a94c5e4c0f2 -32707,0x0ee00ffea69e96ac954fd6f7ae6e5daf5a31e7fe -32738,0x12db37fde089928cd7931db45e24991a53af0411 -32711,0x187bdd1efe1cbcec6b1f0153204adc373bdddd2d -32743,0x2071a1ed8bfe4f4639f556b100d88e5a3c79fee3 -32732,0x2e2234db361766bb47a654aea954c687afd357bb -32729,0x2e3e3feb640f57461da59061d2463aa4f351af83 -32726,0x30555ac5e7156ece35042fbb7bd4cfb3cd4d2ff5 -32708,0x3094c033cd6b052ad3c7164db8fcbac7b4851f23 -32720,0x402c4b0fe1d2128e0c5a9d077501c9abca430e01 -32709,0x40e3e2f0a5de81fdeca5206371f1474f361b728d -32716,0x45c8d1c92f99b61de2125035365da6016cfc34be -32715,0x47a075e999f8e1811de8a90132aa8788f6faa08f -32733,0x5562abb25bc4cb013040ed0d5ea9511fa68519e9 -32747,0x5b8420df96a37655c2d7e08f0558541d8ff26b9c -32718,0x5cc8b7281161e3b12c6d4badad590006b5ae0ff0 -32719,0x5cdbf3759b00a51543cd6eed39afef87121dbc7f -32714,0x5d7fce8768924a33ad3d0af2e68da59cf7783ccb -32739,0x6a5143b943578c18b9d28e7acc123bbd7aac3282 -32712,0x6bb9ccf9a059d6702c4468e28704b674efadd60e -32735,0x721ed802c4bd7e10fb93b71162c8a7437c93b6c5 -32717,0x8cae2b70ebe571a96523d0c12315972e034b3576 -32737,0x8e7e06642fb6e2151a2acf1bcbfc9ca906686387 -32713,0x9139be00cdbc9c4fd51926357e34ba6b1d6fcaaa -32705,0xa1e10322d5d97e50f1f0b2ca5022cc899bf9bbac -32731,0xa31160c08b80c283adc7a4c7052a95d8f2fd4f8a -32730,0xabaf740f53b9418a03f11dcc68426ba6bde44409 -32742,0xb19722d490dc1de3d8c10078be1ea029b58a99dd -32746,0xba3c4ce3e1353bc2addd3426c1600e03c66531d8 -32710,0xc0179ff2cb00eb09bea3664766285c61a3958b81 -32728,0xc4ff5ce3d929385e4e91367d5948991fd04a9e42 -32727,0xc5f2a855a6a14b7197fda56d3dc0843eb972a724 -32725,0xc79e2da0e1530877d9ef63d8cb59d5b3a7b6b4a9 -32744,0xd99deb9c7d57f369c163f18da66d4d48968c2825 -32741,0xdefe4d2a749673b32fea293d0ae94cd152ee3ecd -32722,0xeecae7c8ba044100e29aa004c22c8d0c4f87d7e4 -32721,0xf18eb2944bd819b1d94dd3c53775edc02ebeb2bc -32724,0xf859e6241c9f0961ffb9ec32100cc33131050b80 -32736,0xfae2b9e29f02da0010c84c5aef295b40f0831c67 -32740,0xfda44f9af0583a0af438d567a1b8a0a1eb91c59a -32706,0xff10d8f3068ac1901556c308835f2d147d7be580 -33127,0x0312f889b8b58b0deb73a3746b7cb3e12632c80a -33094,0x0e6b8845f6a316f92efbaf30af21ff9e78f0008f -33090,0x11111112542d85b3ef69ae05771c2dccff4faa26 -33122,0x1111111254760f7ab3f16433eea9304126dcd199 -33130,0x1111111254eeb25477b68fb85ed929f73a960582 -33121,0x11431a89893025d2a48dca4eddc396f8c8117187 -33111,0x26271dfddbd250014f87f0f302c099d5a798bab1 -33103,0x29bc86ad68bb3bd3d54841a8522e0020c1882c22 -33114,0x2ec255797fef7669fa243509b7a599121148ffba -33088,0x32d12a25f539e341089050e2d26794f041fc9df8 -33117,0x3dd9dc5a2f89bb02c21552e6e3cf899de5567d89 -33084,0x454c8b4dc6a2affe669a3db1633133f7d305e305 -33129,0x4e66794586cc9c53a8c604d77b4ce3d39b1cff7c -33110,0x4f10673206c571ccf96df46e98280caed5c13e6e -33120,0x57da811a9ef9b79dbc2ea6f6dc39368a8da1cf07 -33097,0x670988ba70f9433438d1d35b53a5d83a4731a473 -33083,0x6df9fe538d1c56e953fe7f48a5ce94922aed17e7 -33081,0x7444b9d520b2d1870a2b8ef8e7935b8870a2d017 -33092,0x7810d7a3764add12b30baed494297f36ad04fbf8 -33099,0x7871769b3816b23db12e83a482aac35f1fd35d4b -33095,0x7ab5194e730c4ec4a9d1d7f1dcad9c173075888a -33080,0x7bd51251d46c484dc5ce24ca92cfce91ef941165 -33115,0x8266c553f269b2eeb2370539193bcd0eff8cc2de -33124,0x851de4ae771483bff6790f79ee78a92daf400e76 -33113,0x93131efee501d5721737c32576238f619548edda -33119,0x94bc2a1c732bcad7343b25af48385fe76e08734f -33091,0xa44f3da37e4266999f9d3cab82da6371e416d1bf -33128,0xafea5601b0a894451955355e79ad3026515e500d -33126,0xb33839e05ce9fc53236ae325324a27612f4d110d -33089,0xb54c7216189b5dce3a389ff12f82e5c96d93d54a -33132,0xb63aae6c353636d66df13b89ba4425cfe13d10ba -33131,0xb707d89d29c189421163515c59e42147371d6857 -33133,0xbaf9a5d4b0052359326a6cdab54babaa3a3a9643 -33105,0xc0077d3956f51c2840a6d5b3ea86d6643403bc5f -33123,0xc2df1f9c2f99262bd9b88a36bbdb64412d6041ba -33102,0xcb06df7f0be5b8bb261d294cf87c794eb9da85b1 -33096,0xcbdb7490968d4dbf183c60fc899c2e9fbd445308 -33107,0xd41b24bba51fac0e4827b6f94c0d6ddeb183cd64 -33082,0xd7936052d1e096d48c81ef3918f9fd6384108480 -33106,0xe1b8ff58432916ccfbf65a467b66fa4313dc04d3 -33104,0xea1e11e3d448f31c565d685115899a11fd98e40e -33079,0xf0694acc9e941b176e17b9ef923e71e7b8b2477a -33098,0xf5ab9bf279284fb8e3de1c3bf0b0b4a6fb0bb538 -33108,0xfb45fcb8428ea6487b2c5f1474d82c32c65a3e31 -33125,0xff3cdca6db902617e6ae4133cb45df41c38222b1 -470432,0x0abdd5aa61e9107519db7cd626442b905284b7eb -470434,0x0fbab7302f9351dd1db6674cc3bb855f0c55840b -470431,0x2e0c148c1aed0360df9a86112fd7c4eab8045779 -470435,0x421b6ad0cdd20be3636f3511b6ae244d8f668db1 -470433,0x824dd1ccd0824a15913b454a7e5d3bf0c6b2beed -470430,0xf0fd838913e9407edac7e51629de21eaf748b50a -471001,0x80c3806adf50efac542dd4b3657f4be2c30e24b8 -470999,0x8b9c6b2725646411d4ac5a3f8af2225751d17b73 -471000,0xcc28808c5075a112e4177096ebf643ddf2d82ab0 -471002,0xd97fcb50b760cb9209518a0a82bb68792198fdb1 -471011,0x0a252663dbcc0b073063d6420a40319e438cfa59 -471016,0x1ac17ffb8456525bff46870bba7ed8772ba063a5 -471007,0x379002701bf6f2862e3dfdd1f96d3c5e1bf450b6 -471014,0x3c9241461e11817ffc6994af3d467f056f487b67 -471015,0x94d9e02d115646dfc407abde75fa45256d66e043 -471013,0xa7216b82f9c5391ba9aba8bb42e2095b2198f919 -471009,0xb4609b8453bb07d540ec95d491b92bdecb794531 -471012,0xc739d01beb34e380461bba9ef8ed1a44874382be -471008,0xeb585163debb1e637c6d617de3bef99347cd75c8 -471010,0xffcbf84650ce02dafe96926b37a0ac5e34932fa5 -471312,0x163b09b4fe21177c455d850bd815b6d583732432 -471318,0x2a968958e6136fa0fc2ed068af8bef464c7b66ea -471311,0x31b676942d67297fa37fe4fa222b363b0f0fdc7e -471314,0x5afacbd143de0d5827d60384b53e2091ea0fb999 -471317,0x63b26031c4e0641f10578d4d3e3240c4c6fb4247 -471316,0x6f736e40e8c2f51919ee668c7a2c07bb7a007507 -471308,0x7bceac329e8b2d8ddab82bfb2b7c01b44001d6ad -471313,0x90301272e4e3be401304799796b15543f13dc23f -471320,0x9d7fca3cc4a8b0a1dcc6191eb46eb7cb29f1344c -471307,0xa2ffe4efe61f485a98983a09298ba9515c289e8b -471310,0xa387e1c3f9fade05a3b5df4049681b3bb668bf55 -471319,0xb5817e39a5bed8b44c0be4314a3c821c18bd8bab -471309,0xd0999b463bcff90d5846ad53e637de41cb5b3502 -471315,0xd8aed536bb0d59386addebc0481e01d4e09ca50f -473006,0x3f468e35f5c262a6e796bfe3be831bf8b9142e9c -474010,0xc08dcc93130ab30987dd7fe64e011402bbe5fda6 -476915,0xeb94eca012ec0bbb254722fdda2ce7475875a52b -477064,0x07e4a282f8f20032f3e766fffb73c8b86ba7e1f1 -477066,0x5faf863c89fc6dab2d61b60ee5e6ca0305acc295 -477067,0xbfc8c07468aeea87a0a1d30a23804cf4fd73eff1 -477065,0xe3f6ec0676b9ce5d759580e1d526dee31678c283 -477962,0xecc19e177d24551aa7ed6bc6fe566eca726cc8a9 -478559,0x09a6e77912a6bcfc3abfdfb841a85380bb2a8b97 -478557,0xae1138690ff1b35eb083e52a680c20a6ae919dfb -478556,0xb002b522c51a9d17d34e5fbd454c772f75b76a2b -478558,0xc2d8e5693d650828682ca8e40f71f364b0c1e5d7 -479343,0x0bde0385869433ee7716b606fd0af19c83f4248a -480412,0x2e441adc345daeb11ff9c2cae7efd461e5525850 -481200,0x2491f2679cef237e404e9252c2111fd3a6e6b536 -481201,0x5db4ca132a21aab9a29ffa069605a10067a0a931 -481202,0xaf7ba717e60ef219a0c319e4dc6172f4b25ee870 -481203,0xc21ca11599c636576656fb7dbfa2fb6ad8563472 -481469,0xae9e64886f0b09c349a1eca9bf2ec3ac03e6fd43 -481578,0x629e91a599d5f2f54326d5ba0f51c182a3025e99 -481577,0x8d029071a3fdf1ae8a44a2d28cc6263d34075a4b -482080,0x0d51a33975024e8afc55fde9f6b070c10aa71dd9 -482075,0x2589ff8614f74704741ee3b51851b4ae812f1a21 -482078,0x38449a6b7bb76638452273925c9a2ba818bd130d -482079,0x47b55748243314be6a341668ecda2066c0625f70 -482081,0x8cfffffa42407db9dcb974c2c744425c3e58d832 -482076,0xf4c47dacfda99be38793181af9fd1a2ec7576bbf -482077,0xf4cfa53df258d78aad153ac11c644703651983f5 -482946,0x06e644175b0bb36afec1f2c6d71894f4e95977fe -482943,0x0d753a1aa8eb079c30c0fb7203345030792e1254 -482942,0x31a7d116c36aa426ac129cfdb55d4ccebb8c13fa -482941,0x464a53459ffc18bef5de2b4ecbba4166f71f693f -482938,0x511f637b2d816536037e10e65c333b82a415b279 -482939,0x5ae0f2910d0a166d432afe8fb1d0d48c400ddb75 -482937,0x5d974d783f6b9edfacd7185cd4b458784d37615c -482945,0xa1b7a8c8e6e66de5f36eb297aa9c007b57ba0823 -482944,0xe2404533687429f635fc8b2b298ef2e743a719cc -482940,0xf57511d5bb0dfcc956acb2ecfdea289e099cb6f2 -484194,0x051dcd6a80f11fe68f77fb0ebde03853fa96b1fd -484217,0x16648548d12d1f71663f712211d56ae7322f58b9 -484187,0x1fbacedf510daa79dfd9b41227a7ba192d5d548d -484198,0x25d43b56e3e7c4fd119d137e081e93412995d93c -484214,0x28de02ac3c3f5ef427e55c321f73fdc7f192e8e4 -484207,0x29deaee5413a07c933c15b37d7deb6eb744ba9c8 -484193,0x2bea6bfd8fbff45aa2a893eb3b6d85d10efcc70e -484204,0x4106fc9811a792130502bb3a5b693894cca7bd8e -484215,0x418273abdeafe67c811a7671e5406e15bba10870 -484190,0x429e497eb2b4cfdbb28e63d64eef537438b222ee -484201,0x4d717868f4bd14ac8b29bb6361901e30ae05e340 -484209,0x5202f7477685686284b3f47b0a5334b15ea0393d -484219,0x59968008a703dc13e6beaeced644bdce4ee45d13 -484186,0x6131ee632f92744a5af1d228663bc08290bd4892 -484203,0x6875e4a945e498fe1b90bbb13cfbaf0b68658c9c -484211,0x6952eb0c152d683ea2f2b8b90a5e888c1550b69d -484208,0x704478dd72fd7f9b83d1f1e0fc18c14b54f034d0 -484191,0x872c9d71a5b64a9611b08f0956a3cc3deb77a317 -484205,0x8a09574b0401a856d89d1b583ee22e8cb0c5530b -484202,0x8d77771fa2518474cc83a613ef3d6140a84dbe56 -484185,0x9581931527eba1b27666a4669c51057cf57bcd4b -484192,0x963ddbb35c1ae44e2a159e3b5fb5177e0b32660d -484210,0x96a528f4414ac3ccd21342996c93f2ecdec24286 -484188,0x998cf52b2585e05494bf9f2bf502351a9c7fda8f -484200,0x9b9feca00fa6cf646c9b205e8c9c61df5807a0a5 -484195,0x9d6d509c0354aca187aac6bea7d063d3ef68e2a0 -484218,0xa12197492d3c27b69581f90cafc59de5b5159639 -484199,0xa2530b4cfbf271e2b409a05c2ce520e4cb5fcc88 -484196,0xa4fba2d2256e11e2ca8d434dc8aeaa8f2be2f0b7 -484206,0xad511b12f73c145cf158eb92e4fa256937d952a4 -484197,0xb6168f597cd37a232cb7cb94cd1786be20ead156 -484213,0xd5c47d2383fddc19596489280c0a33ac42b2bb18 -484216,0xed18de5442297a4ec1ce59c7c7d9427adc2a012b -484212,0xf3f560164979fe7f8f3eb2b64669eac00e7043a6 -484189,0xf477223b9d9bcaee81b93cc9dcdf6773269c0374 -484184,0xfc7a495389cd3454f5cc0ecaa55d30e343a1fc16 -485222,0x019cfa4d703b9f48643adc38c68b47a98651a273 -485189,0x019d17272687904f855d235dbba7fd9268088ea5 -485190,0x0275bf949922a3a06963c1556cb2e198d15647c5 -485201,0x0a0831abf5cbd7febe7c874ad96e987eed865004 -485191,0x0dd498a0c1906f7397d7756ca146ef70df97cc61 -485184,0x101a13dded54f5bde42d1dc307fb8c469b5ca9be -485248,0x12a4c847cbf31612cb25117d365863e0332f5791 -485188,0x13fcd45421fdf264a5282fa5c6af08e48e322a83 -485217,0x1471fde03a56535f57ced8e6e5ec88f4b97498cb -485262,0x1519814cd74f2ea6c5580137ccabc70ea4493426 -485196,0x157dc5daf6d8e8edcb3147b29912ea61fef98c6f -485231,0x180326708fddf39c38294b28ff89a829bcae9688 -485205,0x182ff2c75e0163cef0b893d24716b4cd9111e926 -485257,0x1ad157b970d029fb052f5b22b056152548d0513d -485194,0x1ce59e3eda14e60dd067492ccdb1a3b2bc0be9b8 -485245,0x279a30ed284d49d32de901acfc0004b2db1c091e -485195,0x28632e90b021ce4f4ee7c284c19cdbf46586ecbb -485185,0x2a760a105f557c8ebe9f00ae76b3c85b19c469b9 -485239,0x2db27a39f9016e6b85f5afdb5870b28eb8893588 -485211,0x306da290aa45dd520e7fc0fd8771d75e45331753 -485215,0x37dc6a04b7549871938c3411b73c66fc7d5ee9c3 -485247,0x3a96a7196222b29463e99cbfba672141a2bb62fd -485203,0x3cb5731ff374c8972ce85d925f9be62c21853b5b -485226,0x3ff50d3fb424fde5357c6e5ca1bf3463497c41b6 -485200,0x40df0032eb078325ddd036d96aa0d05a7792bb29 -485250,0x44409293f1c3b37f00e4b5d09a6039bf888ce5ee -485263,0x4711a4840d1be98115e08a90485c7bd1559e32b7 -485259,0x4845b883a0e1461933fd3b5bdf53da7f7dee5508 -485199,0x52ef8022cb0c6be1f8733b33965560002aec855c -485221,0x552a79cbd61235809e86ca1322c38e97d1045a9d -485232,0x58de7fcdd5d7a20c63fccb973010b95bf94fa333 -485237,0x5e3c33e152be20835b9c33071c411b616688c754 -485254,0x615fc59a02a546c79a68cf91956d9d3fa4b20d0a -485238,0x67917dc3ef976787a4bb08cba9c7f9e439897ba7 -485249,0x6f455a9ab532be0090b76610e11390377abacc6d -485202,0x7ba77113b229bb540a6fa84703220700a359664f -485209,0x82c470166bca446ed2fb90e08ab7941e3eb018f6 -485233,0x8498a2b61029c927ff7c136a430db26182cf8e5a -485251,0x85bd0a230a539f0043ba112bb51789e665063018 -485186,0x86c7c7fe25d3ecad1922fe7a20816b688abcad83 -485206,0x887564c2207146fa6700730a8ce0a8dde7270e27 -485258,0x8892031220ff16b15d436bd7634c85ccf9dab739 -485240,0x8b61903554cc5a2a11c8858844222349129c713b -485256,0x8fde86b75c61a6b76cfe67860eee246e44a10ee6 -485208,0x90d3bf3681e18654d7f4ee046edb24cd474e0734 -485224,0x9174abd39a380bf69f77c8625a97ccf36082326a -485212,0x98ae7f3fd47100b174014dcd143eb43ad7acd79a -485235,0xa491a5671bf1686c51a4fa61754a4d180b5956c9 -485229,0xa4fc2f25ca4dfec08f07ee92d3173ba21a01e9f8 -485213,0xa9866ad9449099e14f6c05155940cac6a7bbd9ce -485220,0xadf79a8315b7ab571d5dd56ef6f253c4695eaa19 -485244,0xaf016e2c070bd93de2035cf029934cb303db618c -485198,0xb5ed6ebeaec26afcd5984a3fe97aa941cd865898 -485253,0xb83e96f53458c7ac1c56eef0a98e1e86ac6a752d -485230,0xb8f254f316ffa6d53969f8d4b01cac25ac8a2d08 -485227,0xba719af222d6050a38c09328f0cf321b7d3bf396 -485193,0xbc810db8e6f4d288fc595a1778316333ad889370 -485216,0xbf3fcee0e856c2aa89dc022f00d6d8159a80f011 -485234,0xc1a775359133a2a2880a1d1dce09929ef5add3fe -485197,0xc70a9929b92ed899403e621710d227addce6e4a7 -485242,0xca86482edcb31e08225d7c1eb1bee7ca4497cc4c -485255,0xcc8e370892cf5c2627b1e642dcf2c4218583c51d -485236,0xcd6cd52e54cea501b9b92bb6cf617aa4e01be146 -485218,0xcfb186eb22bfef93d6bee0cb84350f35116cf9ae -485246,0xcfb4d82207cb944fad439feb544eb18abdef41e3 -485260,0xd037ace05f8c32bd6c684aef01ecf417c5e1c6e1 -485243,0xd2e2e443c100eac4b625b411010c34ead0515735 -485210,0xd670540fa436519511413e90fa52bdd5cc42b0a4 -485223,0xd74d8721255f382a3418a69c6dab3f6099fec0c9 -485219,0xdb50c362044571aadead2e5f097f34287a894412 -485207,0xe4e83f7083d3f9260285691aaa47e8c57078e311 -485183,0xe561451322a5efc51e6f8ffa558c7482c892bc1a -485214,0xed78e7b2ae8b3d156ec840e381d01391c4ee8659 -485182,0xf068cd5834e7b991fa8e03ce44aafbfbd70c752d -485252,0xf14af61efa05b37b5af32e96c70bca848b0f1e78 -485192,0xf23c200c1af7a436457034cb45de74f8b7efbc48 -485261,0xf586c5047967da9f62a8a308119f0811d4b356be -485228,0xf638e2362f2db6dee4c0f3c99a27e35a270e1f58 -485187,0xfbabe932fb8490e65052f308cf549821fa92a2ff -485225,0xfdb424683fc803ddd61d9c8351cfa9a881cef173 -485241,0xff6e271ae412001485edbe3db76874f05e2f96c3 -485204,0xff7104ae44f01c1be87fb9eac09772715c66d9e1 -485692,0x225b0747c4062d98e2e957752ac5d73c7dacff90 -485694,0x50075f151abc5b6b448b1272a0a1cfb5cfa25828 -485691,0x61b6f67f5f997dc31a050548285957fde1cf526d -485695,0x7ab8a2a5cdde4a8e4601879027695f5f788fabaa -485693,0x7abe38f44560775358c0590cce1057c6307195e3 -485972,0x1000270b3efe49dc83de59d9259f62dacb28841c -485982,0x4de26d7072aaa541a88d2fc5aa24871d12a8d695 -485977,0x508c8185f8d204459cda86239a600928373ff3f7 -485988,0x59d7618b1c6b575a1bed48ca8fe6aa8a1df73fbd -485986,0x6c556521ad52584cfbaae29da8f54c3da66e43e7 -485979,0x6ef0871ed810f323ea516a77b0988353b667dfa4 -485974,0x6fdac0442a336429fb8a01bc1f4d6d10963d5c54 -485981,0x7adccfb3ebbc589c3b4080dcf396d00a72e00708 -485978,0x7f0478fc86252d4e2e34ab6b338f168a0f0f1de0 -485984,0x97745c84b36cca51e1d464f70883a70bb59303d9 -485976,0x9f2b000b8912406dc392b23bf6e5ae9bfd70a128 -485983,0xcf7fa2575f70adbdf6c0f8d285b23a454094e07f -485975,0xd7d6b3d8e71809e7e779520ad44a93a7c7b997c5 -485987,0xe58494aab1da7824a1e05cf73596e9b072f60295 -485980,0xe6ba7158a440d9458fa72dcec0ea128512d0effb -485973,0xe8ebfbcf40adf07832af2f5300311597051799c0 -485985,0xf1122201ab976deab556dcf53cf8f2096b8d68ca -486089,0x361c07e1347d8e38718b70dbe3d024156d0c9b49 -486087,0x4f86555d3f73ea9db7281def8311a40f49201c79 -486086,0xa96caccf2e4366f0028cc3dde8668dd1e36b7904 -486091,0xd07a44276a5c488c5d8fc794af16f1d0b323532e -486090,0xd0bfbd8cc8ff5e6bb72b70086664166ba23277e8 -486088,0xddb61a3679bb756de226c81fecb9858dc0983238 -486425,0x15e69c618f0f4bf7a1c5522468fb547ca90ed53a -486598,0x03dcc16ddc47b33937039403db4ef553c1a37ead -486580,0x0f9b80fc3c8b9123d0aef43df58ebdbc034a8901 -486595,0x2140ea50bc3b6ac3971f9e9ea93a1442665670e4 -486582,0x3134f3ff6c0daf74b32fc683b4098f9de0086589 -486585,0x4829b4e552036006b0caececcee6dd9c6642ba1c -486593,0x4d4c71f187a330de95a01d4925691453309f7cb6 -486589,0x4ea9ee08407a06fb62dd2f4b1070c07a8a122525 -486583,0x509899b88804f740c588dc2c25159767731f045a -486586,0x5ebc985bae47400a2355ee247dd5b1cbe779a9d1 -486584,0x653b58c9d23de54e44dcbfbd94c6759cdda7f93d -486596,0x7c173d178b437287608d6105886ddc77cd40c089 -486594,0x8637725ada78db0674a679cea2a5e0a0869ef4a1 -486597,0xb5ae40de9c5744ce96282352f72c22fee0b3b1ad -486587,0xc2106ca72996e49bbadcb836eec52b765977fd20 -486590,0xd8577bbb11dd7b435db11ddf08659d8cf48eb1cf -486592,0xf1b8982ec774ae84e936bd63f372280bd534e797 -486581,0xf1f839fdd467e4087eadbbb330990077889e79c5 -486591,0xf4a3e3679a943aae9da5ca212769c057bec26f86 -486588,0xfa1c8cd6b3a5ead9499b8d09f9747c4068f88f37 -487857,0x9f40e1650bd580d0139728ac0f0ecd5591537184 -490158,0x626058b401df808fe231566b3d2de1a64a6bf764 -490157,0x9a4df6d81309c8a583c28ed8e449e320043e7a97 -490504,0x03f6dc6e616ab3a367a1f2c26b8bc146f632b451 -490502,0x10b04483d762bd4f193f35600112ad52391004a7 -490499,0x15725a8159629ca9763dec4211e309c94d9f5cb0 -490497,0x195f6f7ca2268e1dec03352786c350eb61aba307 -490501,0x33b725a1b2de9178121d423d2a1c062c5452f310 -490503,0x61294940ce7cd1bda10e349adc5b538b722ceb88 -490498,0x626e666c95f4a31439a9ca2d5ef757ea3d72a2fd -490508,0x7df905e163d54b4fe9a14ae01cf985bdc82c1a0f -490505,0x85b466a1a6e2d2acef68cb86bd3c6efd7479e55d -490500,0xa1f54fb12e1f548876d38b96d88d08dce950caa8 -490506,0xb2a20fcdc506a685122847b21e34536359e94c56 -490496,0xc9af789ae606f69cf8ed073a04ec92f2354b027d -490509,0xd3b8876073949d790ab718cad21d9326a3ada60f -490507,0xf877315cfc91e69e7f4c308ec312cf91d66a095f -492035,0x01ff075517dc7db43798751f22febdda6ee75b9f -492040,0x2e010ec4da1b4ec2f968b7625e865ba0863d74a5 -492043,0x3497556f7d0bf602d4237ecb8ae92840d09e4f63 -492036,0x56a14abe1df94af0de78aef3bd96ae9928d3b415 -492039,0x6a78df871291627c5470f7a768745c3ff05741f2 -492041,0x78a0c21f9a24284c90ac7eba63c7396db846a1df -492042,0xa40c8aaf7f47b18c1eddbe7855b580f828ed9711 -492037,0xb316940b687daa87392157e2ecfec1b9e182423d -492038,0xdf10310d2c72f5358b19bf6a7c817ec4570b270f -493367,0x15fa08599eb017f89c1712d0fe76138899fdb9db -493371,0x5bc95c6e11520d25be8c7bdf7aac3e2eeabd8564 -493359,0x62a850d7805f3ae382c6eef7eeb89a31f68ce2d5 -493364,0x843829986e895facd330486a61ebee9e1f1adb1a -493358,0x8e1bd5da87c14dd8e08f7ecc2abf9d1d558ea174 -493365,0x9086d7f2599a5be83ade8aa90fb447968f66d832 -493362,0x9a44daa3823b21df8d905abd9c06b75131f43f38 -493369,0xa8ed4d2c3f6f98a55cdded97c5ae9b932b0633a4 -493370,0xa8f02169d8fe0d48179c9d014abca1ab4bce0cb8 -493368,0xab3d19eb0f318ed0c4fa4bf45c16040d00d34c82 -493360,0xbc778313e52b1184a15d163b5d3a72aef8d510a2 -493356,0xbce556cf365e631ff50449211a6f2cb8936f40d1 -493355,0xc94abf0292ac04aac18c251d9c8169a8dd2bbbdc -493361,0xcaa9e817f02486ce076560b77a86235ef91c5d5d -493366,0xddccd602e068c763369a61ea658476b3a4a97c82 -493357,0xe1f4257396f6ece6d690677526ec52862c4842fc -493372,0xe72a7111f0a99c2fb777bbbccd40032fb021c207 -493363,0xec77fcce4f0396bab43bc66a513157ee59ee07c7 -493920,0x053f082f3bc0c48b8409970c017b4f2a6f673e16 -493915,0x09044da6e6ea891c83ee8d980bc109fda73b199a -493900,0x0938730fd85ccbe08f0ac57764dd898aaa0cf816 -493896,0x1696a141379f8b7c5ddd24d6dd0eb6e30af94c54 -493895,0x16cf71041cc88f0c98a7c8a04be4d71f8cf7d07d -493899,0x20d162b1655afcbdb19dd87679c02e1a2eafbffe -493893,0x2e3d870790dc77a83dd1d18184acc7439a53f475 -493922,0x31aa22d69270148ec63baf53fde846b45db86509 -493910,0x35c6962c221e4e8c17e2b4d59c8de79457ea66de -493888,0x37b87f9ede8003ab1a09aba5503f6d79a96d1f08 -493917,0x41044e8ef2b2d4cca3abb67e52c038bcae400511 -493891,0x449b179992f2c5d2a572a4c6e766e322c894c0b7 -493897,0x484c2d6e3cdd945a8b2df735e079178c1036578c -493908,0x59b99cf08c01a6ada0e0d819520719ca41b35c7c -493911,0x5c288de5a6f0e3d358b34bb9b4476515350f28ee -493903,0x626efc448227d794dc8c02ffea6a932bb360f72a -493905,0x66bd28d64d756bb404db11aec190db995c504a27 -493894,0x67ccea5bb16181e7b4109c9c2143c24a1c2205be -493919,0x6806411765af15bddd26f8f544a34cc40cb9838b -493906,0x7655a3dc27ae8df961939373e1df80875e23d502 -493901,0x7ef2a9aa33ab926a42f0e4f259da225a5bebda73 -493892,0x7f35dc487a5422d6946aad733c6018f163084ed0 -493923,0x8368dca5ce2a4db530c0f6e535d90b6826428dee -493916,0x9424fd0120cf1aa11cd6220527c40d2599b3d916 -493898,0xa4efc2d768c9b9b0f97dd91a1555b345f69b39c0 -493890,0xb781fcac4b8ef06891f9bad7db9c178b1ce67967 -493904,0xb84e29042bfb489439949a79aed8a0e156169ae5 -493909,0xb9a55f455e46e8d717eea5e47d2c449416a0437f -493889,0xc05f15676d524eecedd26eb073032c021b7a8e43 -493921,0xc55a7f215a18713015570ecb18bbcf8c82f83071 -493912,0xc5d43a94e26fca47a9b21cf547ae4aa0268670e1 -493913,0xd3cea6c44f745eaa584b836f92faf15fafe826a0 -493918,0xdf6b3b56b1668da507db58c64b7153756cfe8e67 -493914,0xe3da970dc8812a74c95c2e55cacfb6836bd8fd7f -493907,0xe7d6cb15abe01c681b81f46e6c289ad492c04f5c -493902,0xeba66661afc03ab95ec37383b1bfb724abe14a0f -494385,0x21dfd382c71029aa7ce43d5232475710a7875aa1 -494382,0x2692709313de4dc1ca15fe77a385c9eb2b6b77b8 -494380,0x5adf4597272394fe3edfe68961d8dc5876d5e99d -494383,0x777a1665e66218fb020fb15b0b1f0ee66621be10 -494381,0x803800a6944b000fc946979b7b4c221c20445493 -494384,0xa6c33eea9a3f78dea8e3b56da3f35f277bd312bb -494386,0xc60e33b114131e9dd577bc6bebf29c44c8c38600 -494379,0xcb766988d4209d0278b891c9d4242fd6c4bc499e -494530,0x048e71d9721f7328e68e57300803cabdde22308b -494527,0x057e3efca6f9e7094584b176f3e69a4f4b594d04 -494526,0x1768934eaedced14626334a2ce572b687fce1e4a -494529,0x40a11e2480b1e5ffd3a6524aeb4a41273e362f33 -494528,0x67b069d6e50f139ba6f0ced9c5040e96422db415 -494531,0x77e7d3bb311eb948caa277838dc33052a05045f5 -494524,0x81fc4cf70066c25b92ce1d9daae50cd607d763e0 -494523,0xbea586a167853adddef12818f264f1f9823fbc18 -494525,0xd759479b3a07fab64edb668d702293079e8c1bc1 -494522,0xea9111326e63f976e7f0e5b168d67fdafe20db21 -495503,0xb20eb9648b4a818aa621053f1aa1103c03f2df57 -496252,0xa85437470f38f004c2f4b0d75d282e47bf555a3a -497036,0x1f13ff8f5dbc661374f57d766d5fea20eef27a8f -497034,0x464b790c91a609015e3af6e0faf97b04bb59590e -497035,0x7210212b3f058d91f9d09da0bcba508455ae220c -497883,0xce1538a8f42617585cd3c42734d5079c0628c9c8 -499118,0x137b137e65f64ecc50a1069409c2c7a7a745b86f -499117,0x3224db1d2f7aba70469deee357472d2550fe7579 -499121,0x43a2df743aac63eeb7da430ac0bbff0579311b84 -499120,0x704df5613166f552b80ed5a27f3559daa30eb8c8 -499119,0xb188bd6cc347299eebb3ae93f57d90f580536b3a -499122,0xb6b7a8b59a3a0c7f73bc3f6e8996a26736afaac0 -499116,0xc40f7c8763e35fb64ab968dc812c2d24c6f8404c -500093,0x9300c7eb7e3e16ba916a7d6aafee3891236ac5fc -500094,0x97b323fc033323b66159402bcdb9d7b9dc604235 -500092,0xb4bc46bc6cb217b59ea8f4530bae26bf69f677f0 -500762,0x1c7dfaa422cacaa5b368fe22db84b8329cf88e12 -500764,0x53e66f47017af73d60510d42fd83c28db21cc103 -500767,0x71f64aeb9bfcca42ed6768a5ff30b624c4a3e72e -500765,0x843e83e1e4d3dbb7240330d5086c4f62ecf1ca3c -500766,0x9208242fc2148a21737d73de036c6a5b1e934db5 -500769,0xa120ac2dcd06903bf812f1c2671d00a16fde4d64 -500770,0xacb08c35f015cddaa7588cf29dbff935ec56943d -500763,0xc2f48bc4d8a42b82f801315696a92141f6f3413f -500768,0xdedf727f56a19d536e1fbbd1181fe1a7af1b57b6 -501318,0x11e32da61cac93b1c1bf6f7d02eceb6bc23bd2df -501646,0x07d2ceb4869dfe17e8d48c92a71edc3ae564449f -501650,0x1d91f6d917ec51de53a5789c34fff777a58759b6 -501619,0x25e9b0576f92d431882f158bb8fb4ac47bdd7b96 -501648,0x2ce9e840b69a86c965f4d732e39e389fe69fda51 -501624,0x3522df5c13a40dfaa9cec17e12f5fbd29dc811e1 -501644,0x39b9891ba3c5a8fe69c19f54db2fd90a483b780a -501654,0x3b8d59e7048c8efe3492d23e4dae1967fbbc0988 -501647,0x453f7d013ca100ade3ab77e007ffcfbdf753857c -501622,0x4bc385b1ddf0121cc40a0715cfd3befe52f905f5 -501626,0x4cd41204aa4c7438374256bd7be850ef9fcfab84 -501638,0x50763a665dc24692e25ec8e2c203a79e602d2890 -501651,0x535c5fdf31477f799366df6e4899a12a801cc7b8 -501655,0x64ab6c28423bd60611199a01c6720a0576d9a9fa -501641,0x65b87a8a6c173d9ffde1f4c1d97187820495c515 -501640,0x6ac8bab8b775a03b8b72b2940251432442f61b94 -501653,0x7f346f1eb7a65ff83f51b3fd76dcc70979e6df38 -501636,0x8638fbd429b19249bb3bcf3ec72d07a657e49642 -501632,0x891e7e4baffef0ef7bc4b1e85d122bdd7363b8b3 -501633,0x89dc63264dab74a4350d7f44bd62eec3b22c9ca0 -501639,0x89e4be1f999e3a58d16096fbe405fc2a1d7f07d6 -501631,0x8e0cfa182425ae40774a4a9a25af25bfe5b3d511 -501625,0x8eebba5f7f433a8afd2794eabd6a7fc9bb92856e -501635,0x92cb4f7e4cb623e73d5ec84a43669adc757c2bd2 -501634,0xb041f628e961598af9874bcf30cc865f67fad3ee -501642,0xb11bb8ad710579cc5ed16b1c8587808109c1f193 -501656,0xb5c3b286dd591282fe87dfab0613488e1b6b09ba -501629,0xbd39e85499944556300b93851e01082ca17ce78e -501620,0xc00e8acb7d8caaa1a9338cc42875097c319b7b19 -501630,0xca0e1b56007df7d22214aa584678618b3be96a25 -501637,0xccee73ea4c7a42491c68fea78b1bddd1a35c8d9c -501645,0xd0db41560276832c78ea7b8fe0620feeea9c0d8a -501623,0xdf4433a2b8850c49c2ef2cff6447637002d8d8dd -501652,0xe1de7bd72a568f66e60ed17570fbd725205b4baf -501649,0xec0634d79e67ce143b71f0dc8ee74cdf798a6fa7 -501627,0xf7cb77c8dcb22a1bb4435932f3515319721faf44 -501628,0xf90aafabb1a4c0ce318be12da73f0f31fabe865d -501643,0xf90ec87ba0ba9ac92f5374f112740ce291b8877e -501621,0xfe3d837317d420e9c40c30dcb49892ad9ef15e3d -502614,0x33de1f71890e5da9b963d8aa2dd12faa574c94a6 -502623,0x55d242f5ffdab5b07293884bf788395db524c26e -502619,0x6e7778f77e7ba09cac431018e17d4cd7425cf4f0 -502616,0x7ae1d4f6b727c405413fc8339145bdea61f44b47 -502618,0x8649d0fe5e5c08fdcca6ec85d9de25eb2b414ff6 -502620,0x897e9ea8da4c408e26517ee9806e52281afcb3c1 -502621,0xa8f985d317b67119f0158c1eb0b5111937e49fa9 -502622,0xc80ed325f6471d39f03b3acd1ed38dfd3a0972d7 -502615,0xdd21ae18d164711aeacd6c187a8dac5a973aa4b3 -502617,0xe973eb6e4c4ade7b351d9a5c6e0c684fbe43d797 -502682,0xc127c7a25e6d1e1999df3dc1accddef278a87303 -503180,0x03232b5ee80369a88620615f8328beec1884b731 -503185,0x0568a3aeb8e78262deff75ee68fac20ae35ffa91 -503187,0x0f36dda2b47984434051aecaa5f9587dea7f95b7 -503189,0x24bdacf6bbebaf567123da16cdb79a266597e92b -503177,0x31976dc2ea27e75cc5a3687ed594d17127f9b72e -503179,0x4393277b02ef3ca293990a772b7160a8c76f2443 -503186,0x4c68fda91bfb4683eab90017d9b76a99f2d77eed -503183,0x6bce15b789e537f3aba3c60cb183f0e8737f05ec -503181,0x78fe5d0427e669ba9f964c3495ff381a805a0487 -503178,0xac63290bc16fbc33353b14f139cef1c660ba56f0 -503184,0xc026f5dd7869e0ddc44a759ea3dec6d5cd8d996b -503191,0xc12ad8b3d242b1eddc1c8319d1d58608e67043ed -503190,0xe1dd796dbeb5a67ce37cbc52dcd164d0535c901e -503182,0xf22c8255ea615b3da6ca5cf5aecc8956bff07aa8 -503188,0xfc7b55cc7c5bd3ae89ac679c7250ab30754c5cc5 \ No newline at end of file diff --git a/indexer/src/api_demo.py b/indexer/src/api_demo.py deleted file mode 100644 index 8897d6793..000000000 --- a/indexer/src/api_demo.py +++ /dev/null @@ -1,198 +0,0 @@ -from collections import Counter -from datetime import date -from dotenv import load_dotenv -import json -import os -import random - -from supabase import create_client, Client - - -# ---------------------- DATABASE SETUP -------------------------------------- # - -def supabase_client() -> Client: - load_dotenv() - url = os.environ.get("SUPABASE_URL") - key = os.environ.get("SUPABASE_KEY") - return create_client(url, key) - -supabase = supabase_client() - - -#----------------------- GLOBALS ----------------------------------------------# - -PROJECT_LIST = [ - "ffc69c6c-dbd1-4077-9dc7-1b8be4a7315e", - "f5a2f3d4-5052-4f21-9017-a1d0ae8cb943", - "de43bcbb-612a-4ed7-b493-244a7c6483ff", - # "e4b43fa9-b807-45f4-93dd-0c523ade875f", - # "4c6b108c-0c75-49e9-8371-8b5613fe1973", - # "bc695037-a1d1-400e-b495-18097bb0c66b", - # "478a9fad-0453-4f16-aa40-4aea390f8462", - # "f2727f30-1c71-4611-9c4e-917d22b546ba", - # "beb3c122-a747-4328-8435-19d6e770609f", - # "37c6e041-ecf3-45d6-ab26-339c1efcbb25", - # "7a697c14-9953-4b09-8572-452fbe7e0d1c", - # "f16842de-4118-4d92-921f-075c6dedca1e", - # "c6789276-497a-451d-a8b5-17e684251eb6", - # "f7370593-7f26-43ee-bce8-da1badf58f8b" -] - -DEFAULT_START_DATE = date(2023, 1, 1) -DEFAULT_END_DATE = date(2023, 5, 26) - - -#----------------------- DATA FETCHES ------------------------------------------# - -def get_project_mapping(): - - response = (supabase - .table('projects') - .select('id, name, github_org, description') - .order('name') - .execute()) - - mapping = { - project['id']: { - 'name': project['name'], - 'github': project['github_org'], - 'description': project['description'] - } - for project in response.data - if project['id'] in PROJECT_LIST - } - mapping = dict(sorted(mapping.items(), key=lambda x: x[1]['name'].lower())) - - return mapping - -PROJECT_MAPPING = get_project_mapping() - - -def get_events_from_project(project_id, start_date, end_date): - - response = (supabase - .table('events') - .select('*') - .eq('project_id', project_id) - .gte('event_time', start_date) - .lte('event_time', end_date) - .execute()) - - return response.data - - -def analyze_events(events, min_contribs=1): - - project = PROJECT_MAPPING.get(events[0]['project_id']) - github_org = project.get('github') - - github_events = [] - onchain_events = [] - for e in events: - source = e['details']['source'] - if source == 'github': - github_events.append({ - 'github_org': github_org, - 'repo': e['details']['data']['repository.name'], - 'contributor': e['contributor'], - 'event_type': e['event_type'], - 'amount': e['amount'], - 'timestamp': e['event_time'] - }) - elif source == 'zerion': - onchain_events.append({ - 'event_type': e['event_type'], - 'buy_address': e['details']['data']['Buy Currency Address'], - 'sell_address': e['details']['data']['Sell Currency Address'], - 'amount': e['amount'], - 'timestamp': e['event_time'] - }) - else: - continue - - if min_contribs > 1: - contrib_counts = Counter([e['contributor'] for e in github_events]) - github_events = [ - e for e in github_events - if contrib_counts[e['contributor']] > min_contribs - ] - - return { - 'project': project, - 'github_events': github_events, - 'onchain_events': onchain_events - } - - -#----------------------- ANALYSIS ----------------------------------------------# - - -def generate_kpis(events_dict): - - project = events_dict['project'] - github_org = project.get('github') - project_name = project.get('name') - - git_events = events_dict['github_events'] - contrib_counts = Counter([e['contributor'] for e in git_events]) - repo_counts = Counter(e['repo'] for e in git_events) - event_counts = Counter(e['event_type'] for e in git_events) - - first_date = min([e['timestamp'] for e in git_events]) - last_date = max([e['timestamp'] for e in git_events]) - - income = sum([ - e['amount'] for e in events_dict['onchain_events'] - if e['event_type'] == "funds received" - ]) - - # dummy figures - num_users = random.randint(1000,20000) - - return { - "project": { - "name": project_name, - "status": "verified", - "start_period": first_date, - "end_period": last_date, - - # these are dummy figures - "smart_contracts_monitored": random.randint(1,10), - "project_wallets_monitored": random.randint(1,5) - }, - "metrics": { - "github_contributors_count": len(contrib_counts), - "github_contributions_count": len(git_events), - "github_active_repos_count": len(repo_counts), - "onchain_income": f"${income:,.0f}", - - # these are dummy figures - "contract_interactions_count": random.randint(20,65000), - "unique_onchain_users": num_users, - "active_onchain_users": int(num_users * random.uniform(.05,.5)) - } - } - - -def sample_api_call(project_ids=PROJECT_LIST): - - data = [] - for project_id in project_ids: - events = get_events_from_project(project_id, DEFAULT_START_DATE, DEFAULT_END_DATE) - events_dict = analyze_events(events) - kpis = generate_kpis(events_dict) - data.append(kpis) - - result = { - "success": True, - "message": "query excecuted successfully", - "data": data - } - - pretty_result = json.dumps(result, indent=2) - print(pretty_result) - - return result - -if __name__ == '__main__': - sample_api_call() diff --git a/indexer/src/app.py b/indexer/src/app.py deleted file mode 100644 index 41753e7a3..000000000 --- a/indexer/src/app.py +++ /dev/null @@ -1,347 +0,0 @@ -#----------------------- STANDARD DASH DEPENDENCIES ---------------------------# - -import dash -from dash import dcc, html -import dash_bootstrap_components as dbc -from dash.dependencies import Input, Output -from dash.exceptions import PreventUpdate - -#----------------------- THIS APP'S DEPENDENCIES ------------------------------# - -from collections import Counter -from datetime import date -import pandas as pd -import plotly.express as px -import random - -from dotenv import load_dotenv -import os -from supabase import create_client, Client - - -# ---------------------- DATABASE SETUP -------------------------------------- # - -def supabase_client() -> Client: - load_dotenv() - url = os.environ.get("SUPABASE_URL") - key = os.environ.get("SUPABASE_KEY") - return create_client(url, key) - -supabase = supabase_client() - - -#----------------------- GLOBALS ----------------------------------------------# - -PROJECT_LIST = [ - "ffc69c6c-dbd1-4077-9dc7-1b8be4a7315e", - "f5a2f3d4-5052-4f21-9017-a1d0ae8cb943", - "e4b43fa9-b807-45f4-93dd-0c523ade875f", - "4c6b108c-0c75-49e9-8371-8b5613fe1973", - "bc695037-a1d1-400e-b495-18097bb0c66b", - "de43bcbb-612a-4ed7-b493-244a7c6483ff", - "478a9fad-0453-4f16-aa40-4aea390f8462", - "f2727f30-1c71-4611-9c4e-917d22b546ba", - "beb3c122-a747-4328-8435-19d6e770609f", - "37c6e041-ecf3-45d6-ab26-339c1efcbb25", - "7a697c14-9953-4b09-8572-452fbe7e0d1c", - "f16842de-4118-4d92-921f-075c6dedca1e", - "c6789276-497a-451d-a8b5-17e684251eb6", - "f7370593-7f26-43ee-bce8-da1badf58f8b" -] - -DEFAULT_PROJECT_ID = random.choice(PROJECT_LIST) -DEFAULT_START_DATE = date(2023, 1, 1) -DEFAULT_END_DATE = date(2023, 5, 26) - - -#----------------------- DATA FETCHES ------------------------------------------# - -def get_project_mapping(): - - response = (supabase - .table('projects') - .select('id, name, github_org, description') - .order('name') - .execute()) - - mapping = { - project['id']: { - 'name': project['name'], - 'github': project['github_org'], - 'description': project['description'] - } - for project in response.data - if project['id'] in PROJECT_LIST - } - mapping = dict(sorted(mapping.items(), key=lambda x: x[1]['name'].lower())) - - return mapping - -PROJECT_MAPPING = get_project_mapping() - - -def get_events_from_project(project_id, start_date, end_date): - - response = (supabase - .table('events') - .select('*') - .eq('project_id', project_id) - .gte('event_time', start_date) - .lte('event_time', end_date) - .execute()) - - return response.data - -DEFAULT_EVENTS = get_events_from_project(DEFAULT_PROJECT_ID, DEFAULT_START_DATE, DEFAULT_END_DATE) - - -def analyze_events(events, min_contribs=1): - - project = PROJECT_MAPPING.get(events[0]['project_id']) - github_org = project.get('github') - - github_events = [] - onchain_events = [] - for e in events: - source = e['details']['source'] - if source == 'github': - github_events.append({ - 'github_org': github_org, - 'repo': e['details']['data']['repository.name'], - 'contributor': e['contributor'], - 'event_type': e['event_type'], - 'amount': e['amount'], - 'timestamp': e['event_time'] - }) - elif source == 'zerion': - onchain_events.append({ - 'event_type': e['event_type'], - 'buy_address': e['details']['data']['Buy Currency Address'], - 'sell_address': e['details']['data']['Sell Currency Address'], - 'amount': e['amount'], - 'timestamp': e['event_time'] - }) - else: - continue - - if min_contribs > 1: - contrib_counts = Counter([e['contributor'] for e in github_events]) - github_events = [ - e for e in github_events - if contrib_counts[e['contributor']] > min_contribs - ] - - return { - 'project': project, - 'github_events': github_events, - 'onchain_events': onchain_events - } - -DEFAULT_EVENT_ANALYSIS = analyze_events(DEFAULT_EVENTS) - - -#----------------------- DATAVIZ ----------------------------------------------# - - -def generate_treemap(events_dict, groupby='repo'): - - if groupby == 'repo': - nodes = ['github_org', 'repo', 'contributor'] - else: - nodes = ['github_org', 'contributor', 'repo'] - - df = pd.DataFrame(events_dict['github_events'])[nodes + ['amount']] - df.dropna(inplace=True) - - fig = px.treemap( - data_frame=df, - path=nodes, - values='amount', - color='contributor', - ) - - fig.update_layout( - margin=dict(t=20, l=10, r=10, b=10) - ) - - return fig - - -def generate_kpis(events_dict): - - project = events_dict['project'] - github_org = project.get('github') - project_name = project.get('name') - - git_events = events_dict['github_events'] - contrib_counts = Counter([e['contributor'] for e in git_events]) - repo_counts = Counter(e['repo'] for e in git_events) - event_counts = Counter(e['event_type'] for e in git_events) - events_string = ", ".join([f"{k}s - {v}" for k,v in event_counts.items()]) - - income = sum([ - e['amount'] for e in events_dict['onchain_events'] - if e['event_type'] == "funds received" - ]) - - return [ - dcc.Markdown( - f"### {project_name} \n"\ - f"A total of {len(contrib_counts)} contributors made " \ - f"{len(git_events)} contributions ({events_string}) in {len(repo_counts)} repos.\n\n" \ - f"The project also received ${income:,.0f} in on-chain funding over this period.", - style={'margin-bottom': "0px"} - ) - ] - - -#----------------------- SIDEBAR ----------------------------------------------# - -SIDEBAR_STYLE = { - "position": "fixed", - "top": 0, - "left": 0, - "bottom": 0, - "width": "16rem", - "padding": "2rem 1rem", - "background-color": "#f8f9fa", -} - -sidebar = html.Div( - [ - html.Div( - html.Img(src="assets/os-observer.png", style={"width": "50px"}), - style={"textAlign": "center"} - ), - html.Br(), - html.Div( - [ - html.P("Select an open source project:"), - dcc.Dropdown( - id="project-id", - options=[ - {"label": pdata['name'], "value": pid} - for (pid, pdata) in PROJECT_MAPPING.items() - ], - value=DEFAULT_PROJECT_ID, - style={"margin-left": "1px", "width": "14rem", "font-size": "12px"} - ), - html.Br(), - html.P("Group by:"), - dcc.Dropdown( - id="groupby-select", - options=[ - {"label": x, "value": x} - for x in ['repo', 'contributor'] - ], - value='repo', - style={"margin-left": "1px", "width": "14rem", "font-size": "12px"} - ), - html.Br(), - html.P("Select a time period:", style={"display": "inline-block"}), - dcc.DatePickerRange( - id="date-picker", - min_date_allowed=date(2018, 1, 1), - max_date_allowed=date(2023, 5, 26), - start_date=DEFAULT_START_DATE, - end_date=DEFAULT_END_DATE, - calendar_orientation='horizontal', - style={"margin-left": "1px", "width": "14rem", "font-size": "12px"} - ), - html.Br(), - html.Br(), - html.P("Min. contributions:"), - dcc.Input( - id="min-contribs", - type="number", - min=0, - step=1, - value=1, - style={"margin-left": "1px", "width": "14rem", "font-size": "12px"} - ) - ] - ), - html.Br(), - #html.Hr(), - html.H6("Project description"), - html.P( - PROJECT_MAPPING.get(DEFAULT_PROJECT_ID).get('description'), - id="project-description", - style={"fontSize": "small"} - ), - ], - style=SIDEBAR_STYLE -) - -#----------------------- CONTENT ----------------------------------------------# - -CONTENT_STYLE = { - "margin-left": "16rem", - "margin-right": "0rem", - "margin-top": "10px", - "margin-bottom": "10px", - "padding": "0rem", -} - -content = html.Div( - id="dashboard", - children=[ - html.P( - id='kpi-list', - children=generate_kpis(DEFAULT_EVENT_ANALYSIS), - style={'height': '15vh', 'margin-left': "10px", 'margin-bottom': "0px"}, - ), - dcc.Graph( - id='treemap', - figure=generate_treemap(DEFAULT_EVENT_ANALYSIS), - style={'height': '80vh', 'width': '100%'}, - ) - ], - style=CONTENT_STYLE -) - -#----------------------- APP SET-UP---------------------------------------------# - -app = dash.Dash(__name__, suppress_callback_exceptions=False, - external_stylesheets=[dbc.themes.BOOTSTRAP]) -server = app.server -app.title = "open source observer" -app.layout = html.Div([content, sidebar]) - -#----------------------- CALLBACKS --------------------------------------------# - -@app.callback( - [ - Output('treemap', 'figure'), - Output('project-description', 'children'), - Output('kpi-list', 'children') - ], - [ - Input('project-id', 'value'), - Input('groupby-select', 'value'), - Input('date-picker', 'start_date'), - Input('date-picker', 'end_date'), - Input('min-contribs', 'value'), - ] -) -def update_figure(project_id, groupby, start_date, end_date, min_contribs): - - if project_id is None: - raise PreventUpdate - - events = get_events_from_project(project_id, start_date, end_date) - if not len(events): - raise PreventUpdate - - events_dict = analyze_events(events, min_contribs) - - fig = generate_treemap(events_dict, groupby) - descr = PROJECT_MAPPING[project_id]['description'] - kpis = generate_kpis(events_dict) - - return [fig, descr, kpis] - -#----------------------- RUN APP ----------------------------------------------# - -if __name__ == '__main__': - app.run_server(debug=True) diff --git a/indexer/src/cacher/time-series.test.ts b/indexer/src/cacher/time-series.test.ts deleted file mode 100644 index 5fdffc10f..000000000 --- a/indexer/src/cacher/time-series.test.ts +++ /dev/null @@ -1,201 +0,0 @@ -import * as path from "path"; -import * as os from "os"; -import { rimraf } from "rimraf"; -import { mkdirp } from "mkdirp"; -import { - Cacheable, - TimeSeriesCacheLookup, - TimeSeriesCacheManager, - TimeSeriesCacheWrapper, -} from "./time-series.js"; -import { - Range, - findMissingRanges, - rangeFromISO, - rangeToString, - rangesEqual, -} from "../utils/ranges.js"; - -function randomCacheable(range: Range): Cacheable<{ x: number }, string> { - return { - raw: { x: Math.round(Math.random() * 100000) }, - hasNextPage: false, - cacheRange: range, - }; -} - -describe("TimeSeriesCaching", () => { - let tempDir: string; - let manager: TimeSeriesCacheManager; - let cache: TimeSeriesCacheWrapper; - - // Set up a temporary directory before each test - beforeEach(async () => { - // Create a temporary directory in the system's temporary directory - tempDir = path.join(os.tmpdir(), "cache-test-dir"); - - // Ensure the directory does not exist before creating it - await rimraf(tempDir); - - // Create the temporary directory - await mkdirp(tempDir); - - manager = new TimeSeriesCacheManager(tempDir); - cache = new TimeSeriesCacheWrapper(manager); - }); - - // Tear down the temporary directory after each test - afterEach(async () => { - // Remove the temporary directory and its contents - await rimraf(tempDir); - }); - - describe("without cache initialized", () => { - it("should be able to read and write to the cache", async () => { - const lookup = TimeSeriesCacheLookup.fromRaw({ - range: rangeFromISO("2023-01-01T00:00:00Z", "2023-01-02T00:00:00Z"), - bucket: "test", - keys: ["tester"], - normalizingUnit: "day", - }); - const cacheable = randomCacheable(lookup.range); - await manager.write(lookup, cacheable); - - const loaded = await manager.load(lookup); - expect(await loaded.missing()).toEqual([]); - - let pages = 0; - for await (const group of loaded.groups()) { - for await (const page of group.load()) { - expect(page.raw).toEqual(cacheable.raw); - pages += 1; - } - } - expect(pages).toEqual(1); - }); - }); - - describe("with cache initialized", () => { - // Used to compare the randomly generated data - let cachedRangeToItems: Record[]> = - {}; - - beforeEach(async () => { - // Setup the cache with some initial data - cachedRangeToItems = {}; - - // Ranges to create data - const cachedRanges = [ - { - ...rangeFromISO("2022-01-01T00:00:00Z", "2022-01-02T00:00:00Z"), - hasCompletePages: true, - }, - { - ...rangeFromISO("2022-01-04T00:00:00Z", "2022-01-05T00:00:00Z"), - hasCompletePages: true, - }, - { - ...rangeFromISO("2022-01-10T00:00:00Z", "2022-01-12T00:00:00Z"), - hasCompletePages: false, - }, - ]; - - for (const r of cachedRanges) { - const rangeStr = rangeToString(r); - const pagesToGenerate = Math.round(Math.random() * 9) + 1; - const pages = []; - const lookup = TimeSeriesCacheLookup.new("bucket", "key", r); - - for (let i = 0; i < pagesToGenerate; i++) { - const item = randomCacheable(r); - if (i != pagesToGenerate - 1 || !r.hasCompletePages) { - item.hasNextPage = true; - } - await manager.write(lookup, item, i); - pages.push(item); - } - - cachedRangeToItems[rangeStr] = pages; - } - }); - - it("should work to retrieve data using the wrapper", async () => { - let called = false; - const lookup = TimeSeriesCacheLookup.new( - "bucket", - "key", - rangeFromISO("2022-01-01T00:00:00Z", "2022-01-05T00:00:00Z"), - "day", - ); - const responses = cache.loadCachedOrRetrieve<{ x: number }>( - lookup, - async (missing) => { - const expectedRange = rangeFromISO( - "2022-01-01T00:00:00Z", - "2022-01-05T00:00:00Z", - ); - expect(rangesEqual(expectedRange, missing.range)).toBeTruthy(); - called = true; - return randomCacheable(missing.range); - }, - ); - - const ranges: Range[] = []; - - for await (const res of responses) { - ranges.push(res.cacheRange); - } - expect(called).toBe(true); - - const missingRanges = findMissingRanges( - lookup.range.startDate, - lookup.range.endDate, - ranges, - ); - expect(missingRanges).toEqual([]); - }); - - it("should call the retreiver when pages are missing", async () => { - let called = false; - const inputRange = rangeFromISO( - "2022-01-10T00:00:00Z", - "2022-01-12T00:00:00Z", - ); - const lookup = TimeSeriesCacheLookup.fromRaw({ - range: inputRange, - normalizingUnit: "day", - bucket: "bucket", - keys: ["key"], - }); - const pageQueue = [0, 0]; - const pageQueueSize = pageQueue.length; - const expectedCacheables = cachedRangeToItems[rangeToString(inputRange)]; - let lastCacheable = expectedCacheables.slice(-1)[0]; - const responses = cache.loadCachedOrRetrieve<{ x: number }>( - lookup, - async (missing, lastPage) => { - expect(lastPage?.raw.x).toBe(lastCacheable.raw.x); - const cacheable = randomCacheable(missing.range); - if (pageQueue.length > 0) { - called = true; - pageQueue.pop(); - cacheable.hasNextPage = true; - } - lastCacheable = cacheable; - return cacheable; - }, - ); - - const ranges: Range[] = []; - - for await (const res of responses) { - ranges.push(res.cacheRange); - } - expect(called).toBe(true); - - expect(ranges.length).toEqual( - expectedCacheables.length + pageQueueSize + 1, - ); - }); - }); -}); diff --git a/indexer/src/cacher/time-series.ts b/indexer/src/cacher/time-series.ts deleted file mode 100644 index 1ab2cf07b..000000000 --- a/indexer/src/cacher/time-series.ts +++ /dev/null @@ -1,627 +0,0 @@ -import { DateTime } from "luxon"; -import { GenericError } from "../common/errors.js"; -import * as path from "path"; -import * as fs from "fs/promises"; -import { fileExists } from "../utils/files.js"; -import { mkdirp } from "mkdirp"; -import { - doRangesIntersect, - findMissingRanges, - Range, - rangeFromISO, - rangeFromObj, -} from "../utils/ranges.js"; -import { createHash } from "crypto"; -import _ from "lodash"; -import { logger } from "../utils/logger.js"; - -export class CacheDoesNotExist extends GenericError {} -export class InvalidCache extends GenericError {} - -const CACHE_FILE_PATTERN = /\d+.json/; -const CACHE_DIRECTORY_PATTERN = - /(?\d{4}-\d{2}-\d{2}-\d{2})-(?\d{4}-\d{2}-\d{2}-\d{2})/; -const DATE_TO_NAME_FORMAT = "yyyy-LL-dd-HH"; - -// Types for a generic caching mechanism -export type Cacheable = { - raw: T; - - cacheRange: Range; - - cursor?: C; - - hasNextPage: boolean; -}; - -export type CacheableMeta = { - cacheRange: Range; - input: string[]; -}; - -type RawCacheableMeta = { - cacheRange: { - startDate: string; - endDate: string; - }; - input: string[]; -}; - -type RawCacheable = { - raw: unknown; - cacheRange: { - startDate: string; - endDate: string; - }; - cursor?: any; - hasNextPage: boolean; -}; - -async function cacheableFromFile( - path: string, -): Promise> { - const raw = JSON.parse( - await fs.readFile(path, { encoding: "utf-8" }), - ) as RawCacheable; - return { - raw: raw.raw as T, - cacheRange: rangeFromISO(raw.cacheRange.startDate, raw.cacheRange.endDate), - cursor: raw.cursor, - hasNextPage: raw.hasNextPage, - }; -} - -export interface ICacheGroup { - range: Range; - - load(): AsyncGenerator>; - - validForKey(key: string): Promise; -} - -class CachePageDirectory implements ICacheGroup { - range: Range; - path: string; - private inputMap: Record; - private meta: CacheableMeta | undefined; - private initialized: boolean; - - private constructor(range: Range, path: string) { - this.range = range; - this.path = path; - this.inputMap = {}; - this.initialized = false; - } - - static async load(range: Range, path: string): Promise { - const dir = new CachePageDirectory(range, path); - await dir.loadMeta(); - return dir; - } - - private async loadMeta(): Promise { - const metaPath = path.join(this.path, "meta.json"); - if (!(await fileExists(metaPath))) { - throw new InvalidCache(`${path} is not a valid cache directory`); - } - - // Load the meta file - const rawMeta = JSON.parse( - await fs.readFile(metaPath, { encoding: "utf-8" }), - ) as RawCacheableMeta; - - // Attempt to parse the metadata - const range = rangeFromObj(rawMeta.cacheRange); - this.meta = { - input: rawMeta.input, - cacheRange: range, - }; - return; - } - - async validForKey(key: string): Promise { - if (!this.initialized) { - this.inputMap = this.meta!.input.reduce>( - (acc, curr) => { - acc[curr] = true; - return acc; - }, - {}, - ); - this.initialized = true; - } - return this.inputMap[key]; - } - - private async cachePagesPaths(): Promise { - const files = await fs.readdir(this.path); - const pageNumbers: number[] = files - .filter((f) => { - return f.match(CACHE_FILE_PATTERN); - }) - .map((f) => { - return parseInt(f.split(".")[0]); - }); - pageNumbers.sort((a, b) => a - b); - return pageNumbers.map((n) => path.join(this.path, `${n}.json`)); - } - - async *load(): AsyncGenerator> { - // yield all of the cache directories' pages in order duplicates can occur - // if the mechanism that has written into the directory structure allows - // duplicates. We will assume that's fine. - for (const pagePath of await this.cachePagesPaths()) { - yield await cacheableFromFile(pagePath); - } - } -} - -export interface ITimeSeriesCacheLookup { - // Used to put caches in buckets for lookup. - bucket: string; - - // Usually a hash of the input for the cache - keys: string[]; - - // Cache range to search for - range: Range; - - // What is the unit of we should normalize to (only days or hours at this time) - normalizingUnit: TimeSeriesLookupNormalizingUnit; -} - -export type TimeSeriesLookupNormalizingUnit = "day" | "hour"; - -export class TimeSeriesCacheLookup implements ITimeSeriesCacheLookup { - private _bucket: string; - private _keys: string[]; - private _range: Range; - private _normalizingUnit: TimeSeriesLookupNormalizingUnit; - - private constructor( - bucket: string, - keys: string[], - range: Range, - normalizingUnit: TimeSeriesLookupNormalizingUnit, - ) { - this._bucket = bucket; - this._keys = keys; - this._range = range; - this._normalizingUnit = normalizingUnit; - } - - static new( - bucket: string, - keys: string | string[], - range: Range, - normalizingUnit?: TimeSeriesLookupNormalizingUnit, - ) { - const sortedKeys = Array.isArray(keys) ? keys : [keys]; - sortedKeys.sort(); - - return new TimeSeriesCacheLookup( - bucket, - sortedKeys, - range, - normalizingUnit || "day", - ); - } - - static fromRaw(raw: ITimeSeriesCacheLookup) { - const sortedKeys = _.clone(raw.keys); - sortedKeys.sort(); - - return new TimeSeriesCacheLookup( - raw.bucket, - sortedKeys, - raw.range, - raw.normalizingUnit, - ); - } - - get bucket() { - return this._bucket; - } - - get keys() { - return this._keys; - } - - get range() { - return this._range; - } - - get normalizingUnit() { - return this._normalizingUnit; - } -} - -export type PageRetreiver = ( - lookup: ITimeSeriesCacheLookup, - lastPage?: Cacheable, -) => Promise>; - -export type IteratorRetriever = ( - lookup: ITimeSeriesCacheLookup, -) => Promise>; - -export interface ITimeSeriesCacheManager { - load( - lookup: ITimeSeriesCacheLookup, - page?: number, - ): Promise; - write( - lookup: ITimeSeriesCacheLookup, - item: Cacheable, - page?: number, - ): Promise; -} - -/** - * TimeSeriesCacheManager - * - * Uses a combination of database pointers and files to manage a time series - * cache. - */ -/* -export class TimeSeriesCacheManager implements ITimeSeriesCacheManager { - async load(lookup: TimeSeriesCacheLookup, page?: number | undefined): Promise { - return new TimeSeriesCacheCollection([], lookup); - } - - async write(lookup: TimeSeriesCacheLookup, item: Cacheable, page?: number | undefined): Promise { - - } -}*/ - -/** - * TimeSeriesCacheManager - * - * Caches responses so that we can query anything within a specific time range. - * The keys are stored on disk as a way to determine missing data. - * - * {cache-dir}/{bucket}/{startDate}-{endDate}/{keysHash}/{pageNumber}.json - * - * Minimum resolution of start/end times is hours, anything less is ignored. - */ -export class TimeSeriesCacheManager implements ITimeSeriesCacheManager { - private cacheDir: string; - - constructor(cacheDir: string) { - this.cacheDir = cacheDir; - } - - async load( - lookup: ITimeSeriesCacheLookup, - _page?: number, - ): Promise { - const startDateSearch = lookup.range.startDate.startOf( - lookup.normalizingUnit, - ); - const endDateSearch = lookup.range.endDate.startOf(lookup.normalizingUnit); - const normalizedRange = { - startDate: startDateSearch, - endDate: endDateSearch, - }; - - // Check if the directory exists. If not, return an empty set - if (!(await this.fileExists(lookup.bucket))) { - return new TimeSeriesCacheCollection([], lookup); - } - - // List files in the cache. See if any match what we need. - const allCacheDirectories = await this.listAvailableCacheDirectories( - lookup.bucket, - ); - const cacheDirectories = allCacheDirectories.filter((cd) => { - return doRangesIntersect(cd.range, normalizedRange); - }); - return new TimeSeriesCacheCollection(cacheDirectories, lookup); - } - - keysHash(lookup: ITimeSeriesCacheLookup): string { - const hash = createHash("sha256"); - lookup.keys.forEach((key) => { - hash.update(key); - }); - return hash.digest("hex"); - } - - async write( - lookup: ITimeSeriesCacheLookup, - item: Cacheable, - page?: number, - ): Promise { - const startDateWrite = lookup.range.startDate.startOf( - lookup.normalizingUnit, - ); - const endDateWrite = lookup.range.endDate.startOf(lookup.normalizingUnit); - const cacheDirParts = [ - lookup.bucket, - `${startDateWrite.toFormat(DATE_TO_NAME_FORMAT)}-${endDateWrite.toFormat( - DATE_TO_NAME_FORMAT, - )}`, - this.keysHash(lookup), - ]; - await this.ensureDirectory(...cacheDirParts); - - const pageCachePath = this.cachePath( - ...[...cacheDirParts, `${page || 0}.json`], - ); - - const pageCacheMetaPath = this.cachePath( - ...[...cacheDirParts, `meta.json`], - ); - - // This does not validate page numbers so it will just overwrite things. - const toCache: RawCacheable = { - cacheRange: { - startDate: item.cacheRange.startDate.setZone("utc").toISO()!, - endDate: item.cacheRange.endDate.setZone("utc").toISO()!, - }, - cursor: item.cursor, - hasNextPage: item.hasNextPage, - raw: item.raw, - }; - - // Add the meta file if this is the first page - if (!page) { - const metaOnly: RawCacheableMeta = { - cacheRange: { - startDate: item.cacheRange.startDate.setZone("utc").toISO()!, - endDate: item.cacheRange.endDate.setZone("utc").toISO()!, - }, - input: lookup.keys, - }; - await fs.writeFile(pageCacheMetaPath, JSON.stringify(metaOnly), { - encoding: "utf-8", - }); - } - return await fs.writeFile(pageCachePath, JSON.stringify(toCache), { - encoding: "utf-8", - }); - } - - private async ensureDirectory(...paths: string[]) { - return await mkdirp(this.cachePath(...paths)); - } - - private async fileExists(...paths: string[]): Promise { - return await fileExists(this.cachePath(...paths)); - } - - private async listAvailableCacheDirectories(...paths: string[]) { - const files = await this.listCacheCompatibleDirectories(...paths); - const cacheDirectories = await Promise.all( - files.flatMap((f) => { - try { - return this.loadCachePageDirectories( - f, - this.cachePath(...[...paths, f]), - ); - } catch (err) { - if (err instanceof InvalidCache) { - return []; - } else { - throw err; - } - } - }), - ); - return cacheDirectories.flat(1); - } - - private async listCacheCompatibleDirectories(...paths: string[]) { - const names = await fs.readdir(this.cachePath(...paths)); - - const filtered = names.filter((f) => { - if (!f.match(CACHE_DIRECTORY_PATTERN)) { - return false; - } - return true; - }); - return filtered; - } - - private cachePath(...paths: string[]) { - return path.join(this.cacheDir, ...paths); - } - - private async loadCachePageDirectories( - name: string, - dirPath: string, - ): Promise { - const match = CACHE_DIRECTORY_PATTERN.exec(name); - - if (!match?.groups) { - throw new InvalidCache(`Unexpected cache directory name ${name}`); - } - - const range = { - startDate: DateTime.fromFormat( - match.groups.startDate, - DATE_TO_NAME_FORMAT, - { zone: "utc" }, - ), - endDate: DateTime.fromFormat(match.groups.endDate, DATE_TO_NAME_FORMAT, { - zone: "utc", - }), - }; - // List the pages by the keysHash - const dirs: CachePageDirectory[] = []; - const names = await fs.readdir(dirPath); - for (const name of names) { - try { - const dir = await CachePageDirectory.load( - range, - path.join(dirPath, name), - ); - dirs.push(dir); - } catch (err) { - if (err instanceof InvalidCache) { - continue; - } - throw err; - } - } - return dirs; - } -} - -export interface ITimeSeriesCacheCollection { - missing(): Promise; - groups(): AsyncGenerator; -} - -/** - * A set of cached files matching a lookup - */ -export class TimeSeriesCacheCollection implements ITimeSeriesCacheCollection { - private _groups: ICacheGroup[]; - private lookup: ITimeSeriesCacheLookup; - - constructor(groups: ICacheGroup[], lookup: ITimeSeriesCacheLookup) { - this._groups = groups; - this.lookup = lookup; - } - - async missing(): Promise { - // Iterate through the range of things that are available. This algorithm - // isn't super smart. It's mostly for best case scenarios. Instead, we - // should try to keep the input to this quite consistently within some time - // ranges. Essentially if _any_ data is missing we will make attempts to - // query for that within this time range. So this shouldn't be used if you - // want to get large sections of time where you'll likely have sparse - // caches. Instead, you should queue up multiple jobs for each of those time - // periods. - - // Iterate through all inputs. If that input is missing _anything_. Then we - // add it to the "missing" queue for this period. - - const keys = this.lookup.keys; - - const missingKeys: string[] = []; - for (const key of keys) { - // Does this key have all date ranges? If not add it to the missing lookup - const rangesForKey: Range[] = []; - for (const group of this._groups) { - if (await group.validForKey(key)) { - rangesForKey.push(group.range); - } - } - const missing = findMissingRanges( - this.lookup.range.startDate, - this.lookup.range.endDate, - rangesForKey, - ); - if (missing.length > 0) { - missingKeys.push(key); - } - } - if (missingKeys.length > 0) { - return [ - { - bucket: this.lookup.bucket, - keys: missingKeys, - range: this.lookup.range, - normalizingUnit: this.lookup.normalizingUnit, - }, - ]; - } else { - return []; - } - } - - async *groups(): AsyncGenerator { - for (const dir of this._groups) { - yield dir; - } - } -} - -export class TimeSeriesCacheWrapper { - private manager: ITimeSeriesCacheManager; - - constructor(manager: ITimeSeriesCacheManager) { - this.manager = manager; - } - - /** - * Loads from cache or through some retreiver callback - * - * @param lookup Lookup parameters for the time series cache - * @param retriever callback to use to load any additional things and automatically cache - */ - async *loadCachedOrRetrieve( - lookup: ITimeSeriesCacheLookup, - retriever: PageRetreiver, - ): AsyncGenerator> { - logger.debug("attempting to load from cache"); - // Determine if the lookup matches anything in the cache. If not then call the retreiver - const currentPage = 0; - - const missingQueue: Array< - [ITimeSeriesCacheLookup, Cacheable | undefined] - > = []; - const response = await this.attemptCacheLoad(lookup, currentPage); - if (response) { - // Load everything from cache that we can and then queue the missing pages as needed - for await (const dir of response.groups()) { - let lastPage: Cacheable | undefined = undefined; - - for await (const page of dir.load()) { - yield page; - lastPage = page; - } - - if (lastPage && lastPage.hasNextPage) { - // We're missing pages... queue this up for retrieval - missingQueue.push([lookup, lastPage]); - } - } - const misses = await response.missing(); - for (const missing of misses) { - missingQueue.push([missing, undefined]); - } - } else { - missingQueue.push([lookup, undefined]); - } - - for (const missing of missingQueue) { - let lastPage = missing[1]; - let pageNumber = 0; - - while (true) { - const page = await retriever(missing[0], lastPage); - - // Write the page to cache - await this.manager.write(lookup, page, pageNumber); - - // Yield the page - yield page; - - lastPage = page; - - if (!page.hasNextPage) { - break; - } - pageNumber += 1; - } - } - } - - async attemptCacheLoad( - lookup: ITimeSeriesCacheLookup, - page: number, - ): Promise { - try { - const response = await this.manager.load(lookup, page); - return response; - } catch (e) { - if (!(e instanceof CacheDoesNotExist)) { - throw e; - } - } - return; - } -} diff --git a/indexer/src/cli.ts b/indexer/src/cli.ts deleted file mode 100644 index a07dbbd7a..000000000 --- a/indexer/src/cli.ts +++ /dev/null @@ -1,342 +0,0 @@ -#!/usr/bin/env node -import yargs from "yargs"; -import { hideBin } from "yargs/helpers"; -import { handleError } from "./utils/error.js"; -import { - ImportOssDirectoryArgs, - importOssDirectory, -} from "./collectors/import-oss-directory.js"; -import { initializeDataSource } from "./db/data-source.js"; -import { - SchedulerArgs, - SchedulerManualArgs, - SchedulerQueueAllArgs, - SchedulerQueueBackfill, - SchedulerQueueJobArgs, - SchedulerWorkerArgs, - configure, -} from "./scheduler/index.js"; -import { logger } from "./utils/logger.js"; -import { csvCommandGroup } from "./scripts/manual-csv-import-helper.js"; -import { duneCommandGroup } from "./scripts/manual-dune-tools.js"; -import { artifactsCommandGroup } from "./scripts/artifact-management.js"; -import { IExecutionSummary } from "./scheduler/types.js"; -import { coerceDateTime, coerceDateTimeOrNow } from "./utils/cli.js"; -import { dbUtilitiesCommandGroup } from "./scripts/db-utilities.js"; - -//const callLibrary = async ( -// func: EventSourceFunction, -// args: Args, -//): Promise => { -// TODO: handle ApiReturnType properly and generically here -// const result = await func(args); -// console.log(result); -//}; - -function outputError(err: unknown) { - logger.info(err); - try { - logger.error(JSON.stringify(err)); - // eslint-disable-next-line no-restricted-properties - console.error(err); - } catch (_e) { - logger.error("Cannot stringify error."); - logger.error(err); - // eslint-disable-next-line no-restricted-properties - console.error(err); - } -} - -function handleExecutionSummary(execSummary: IExecutionSummary) { - logger.info(`--------------Completed manual run---------------`); - logger.info(" Collection Stats:"); - logger.info(` ${execSummary.errorCount()} errors`); - logger.info( - ` ${execSummary.successfulArtifacts()} artifacts committed`, - ); - logger.info(` ${execSummary.failedArtifacts()} artifacts failed`); - if (execSummary.hasErrors()) { - if (execSummary.errors.length > 0) { - logger.info(`General errors (not by artifact):`); - for (const err of execSummary.errors) { - outputError(err); - } - } - if (execSummary.failedArtifacts() > 0) { - for (const artifactSummary of execSummary.artifactSummaries) { - const artifact = artifactSummary.artifact; - logger.info( - `Errors for Artifact[name=${artifact.name}, namespace=${artifact.namespace}]`, - ); - for (const err of artifactSummary.results.errors) { - outputError(err); - } - } - } - process.exit(1); - } - process.exit(0); -} - -/** - * When adding a new fetcher, please remember to add it to both this registry and yargs - */ -export const FETCHER_REGISTRY = [ - //NpmDownloadsInterface, -]; -const cli = yargs(hideBin(process.argv)) - .middleware(async () => { - // Initialize the database before operations - await initializeDataSource(); - }) - .option("yes", { - type: "boolean", - describe: "Automatic yes to all prompts", - default: false, - }) - .option("cache-dir", { - type: "string", - describe: "sets the path to the cache directory", - default: "/tmp/oso/cache", - }) - .option("run-dir", { - type: "string", - describe: "sets the path to the run directory", - default: "/tmp/oso/run", - }) - .command( - "importOssDirectory", - "Import projects and collections from 'oss-directory'", - (yags) => { - yags.option("skip-existing", { - type: "boolean", - default: false, - }); - }, - (argv) => handleError(importOssDirectory(argv)), - ) - .command>( - "csv ", - "subcommand for csv related tools", - (yargs) => { - csvCommandGroup(yargs); - }, - ) - .command>( - "dune ", - "subcommand for dune related tools", - (yargs) => { - duneCommandGroup(yargs); - }, - ) - .command>( - "artifacts ", - "subcommand for artifacts related tools", - (yargs) => { - artifactsCommandGroup(yargs); - }, - ) - .command>( - "db-utils ", - "subcommand for artifacts related tools", - (yargs) => { - dbUtilitiesCommandGroup(yargs); - }, - ) - .command( - "scheduler ", - "scheduler commands", - (yags) => { - yags.option("overwrite-existing-events", { - type: "boolean", - default: false, - }); - yags.option("recorder-timeout-ms", { - type: "number", - default: 600000, - }); - yags.option("recorder-connections", { - type: "number", - default: 10, - }); - yags - .command( - "manual ", - "manually execute a scheduler run", - (yags) => { - yags - .positional("collector", { - describe: "the name of the collector to execute", - }) - .option("start-date", { - type: "string", - describe: "start-date for the manual run", - }) - .coerce("start-date", coerceDateTime) - .option("end-date", { - type: "string", - describe: "start-date for the manual run", - }) - .coerce("end-date", coerceDateTime) - .option("execution-mode", { - type: "string", - choices: ["all-at-once", "progressive"], - }) - .option("reindex", { - type: "boolean", - default: false, - }); - }, - async (args) => { - const scheduler = await configure(args); - - const execSummary = await scheduler.executeCollector( - args.collector, - { - range: { - startDate: args.startDate, - endDate: args.endDate, - }, - mode: args.executionMode, - reindex: args.reindex, - }, - ); - handleExecutionSummary(execSummary); - }, - ) - .command( - "worker ", - "run the worker", - (yags) => { - yags - .positional("group", { - describe: "the group to execute", - type: "string", - }) - .option("resume-with-lock", { - describe: "resume with the lock on disk?", - type: "boolean", - default: false, - }); - }, - async (args) => { - const scheduler = await configure(args); - const execSummary = await scheduler.runWorker( - args.group, - args.resumeWithLock, - ); - handleExecutionSummary(execSummary); - }, - ) - .command( - "queue [base-date]", - "schedule workers into the queue", - (yags) => { - yags - .positional("base-date", { - describe: "the date to start scheduling from", - type: "string", - }) - .coerce("base-date", coerceDateTimeOrNow); - }, - async (args) => { - const scheduler = await configure(args); - await scheduler.queueAll(args.baseDate); - }, - ) - .command("job ", "job tools", (yags) => { - yags - .command( - "create ", - "queue a job manually", - (yags) => { - yags - .positional("collector", { - describe: "the collector", - type: "string", - }) - .option("base-date", { - type: "string", - describe: "start-date for the manual run", - }) - .coerce("base-date", coerceDateTime) - .option("start-date", { - type: "string", - describe: "start-date for the manual run", - }) - .coerce("start-date", coerceDateTime) - .option("end-date", { - type: "string", - describe: "start-date for the manual run", - }) - .coerce("end-date", coerceDateTime) - .demandOption(["base-date", "start-date", "end-date"]); - }, - async (args) => { - const scheduler = await configure(args); - await scheduler.queueEventJob(args.collector, args.baseDate, { - startDate: args.startDate, - endDate: args.endDate, - }); - }, - ) - .command( - "backfill ", - "create a backfill job", - (yags) => { - yags - .positional("collector", { - describe: "the collector", - type: "string", - }) - .option("start-date", { - type: "string", - describe: "start-date for the backfill scheduling", - }) - .coerce("start-date", coerceDateTime) - .option("end-date", { - type: "string", - describe: "end-date for the backfill scheduling", - }) - .coerce("end-date", coerceDateTimeOrNow) - .option("backfill-interval-days", { - type: "number", - default: 0, - }) - .demandOption(["start-date"]); - }, - async (args) => { - const scheduler = await configure(args); - await scheduler.queueBackfill( - args.collector, - args.startDate, - args.backfillIntervalDays, - ); - }, - ) - .command( - "clean-lock", - "clean the lock for a job execution if it exists", - (_yags) => {}, - async (args) => { - const scheduler = await configure(args); - await scheduler.cleanLock(); - }, - ); - }); - }, - ) - .demandCommand() - .strict() - .help("h") - .alias("h", "help"); - -function main() { - // This was necessary to satisfy the es-lint no-floating-promises check. - const promise = cli.parse() as Promise; - promise.catch((err) => { - logger.error("error caught running the cli", err); - }); -} - -main(); diff --git a/indexer/src/collectors/dependents.ts b/indexer/src/collectors/dependents.ts deleted file mode 100644 index af931de85..000000000 --- a/indexer/src/collectors/dependents.ts +++ /dev/null @@ -1,1037 +0,0 @@ -import { In, Repository } from "typeorm"; -import { CollectResponse, IPeriodicCollector } from "../scheduler/types.js"; -import { Artifact, ArtifactType, Collection } from "../index.js"; -import { BigQuery, Dataset, Table } from "@google-cloud/bigquery"; -import { Storage } from "@google-cloud/storage"; -import { sha1FromArray } from "../utils/source-ids.js"; -import { logger } from "../utils/logger.js"; -import { TransformCallback, TransformOptions, Writable } from "stream"; -import { randomUUID } from "crypto"; -import { - ArtifactNamespace, - CollectionType, - Project, - RepoDependency, -} from "../db/orm-entities.js"; -import _ from "lodash"; -import stream from "stream"; -import { UniqueArray, asyncBatch } from "../utils/array.js"; -import { GithubBaseMixin } from "./github/common.js"; -import { MultiplexGithubGraphQLRequester } from "./github/multiplex-graphql.js"; -import { IEventPointerManager } from "../scheduler/pointers.js"; -import { DateTime } from "luxon"; -import { Brand } from "utility-types"; -import { DataSource } from "typeorm/browser"; - -type DependentRawRow = { - package_name: string; - dependent_name: string; - minimum_depth: number; -}; - -type CollectionQueueStorage = { - name: string; - collection: Collection; - relations: UniqueArray; -}; - -function repositoryDependencies() { - return { - dependencyGraphManifests: { - totalCount: true, - nodes: { - filename: true, - }, - edges: { - node: { - blobPath: true, - parseable: true, - filename: true, - dependencies: { - totalCount: true, - nodes: { - packageName: true, - requirements: true, - hasDependencies: true, - packageManager: true, - }, - }, - }, - }, - }, - }; -} - -type TempCollectionType = { artifactOwnerId: number; id: number }; - -class DependentsRecorder extends Writable { - private collectionRepository: Repository; - private collectionTypeRepository: Repository; - private projectRepository: Repository; - private batchSize: number; - private batch: { - packageNames: string[]; - dependentNames: string[]; - minimumDepths: number[]; - }; - private packages: Artifact[]; - private uuid: string; - private packageMap: Record; - private tempCollectionQueue: Record; - private collectionTypes: Record; - private artifactNameToProjectsMap: Record>; - private repoDeps: Record>; - private count: number; - private initialized: boolean; - private dataSource: DataSource; - - constructor( - packages: Artifact[], - dataSource: DataSource, - collectionRepository: Repository, - collectionTypeRepository: Repository, - projectRepository: Repository, - batchSize: number, - opts?: TransformOptions, - ) { - super({ - ...{ - objectMode: true, - readableObjectMode: true, - writableObjectMode: true, - highWaterMark: 1, - }, - ...opts, - }); - this.batchSize = batchSize; - this.dataSource = dataSource; - this.collectionRepository = collectionRepository; - this.collectionTypeRepository = collectionTypeRepository; - this.projectRepository = projectRepository; - this.resetBatch(); - this.packages = packages; - this.uuid = randomUUID(); - this.tempCollectionQueue = {}; - this.collectionTypes = {}; - this.artifactNameToProjectsMap = {}; - this.packageMap = _.keyBy(packages, "name"); - this.repoDeps = {}; - this.count = 0; - this.initialized = false; - } - - addRow(row: DependentRawRow) { - this.batch.packageNames.push(row.package_name); - this.batch.dependentNames.push(row.dependent_name); - this.batch.minimumDepths.push(row.minimum_depth); - } - - get batchLength() { - return this.batch.packageNames.length; - } - - _write( - row: DependentRawRow, - _encoding: BufferEncoding, - done: TransformCallback, - ): void { - logger.debug( - `[DEPS: ${this.count++}] ${row.package_name} -> ${row.dependent_name}`, - ); - if (row !== null) { - this.addRow(row); - } - - if (this.batchLength < this.batchSize && row !== null) { - done(); - return; - } else { - // Save things to the db - this.writeBatch() - .then(() => { - if (row === null) { - this.commitAll() - .then(() => { - done(); - }) - .catch((err) => { - done(err); - }); - } else { - done(); - } - }) - .catch((err) => { - done(err); - }); - } - } - - _final(done: (error?: Error | null | undefined) => void): void { - logger.debug("Committing all temporary collections"); - this.writeBatch() - .then(() => { - return this.commitAll(); - }) - .then(() => { - done(); - }) - .catch((err) => { - done(err); - }); - } - - async commitAll() { - // Many of the things in this method would be much faster if we just added - // the artifact/collection/project table to bigquery. For now, this is a - // punt until we figure out the proper design for that architecture. - - // Start calculating the artifact ids - await this.dataSource.query(` - CREATE TABLE ${this.dependentsToArtifactTable} ( - package_artifact_id int, - dependent_artifact_id int - ) - `); - - logger.debug(`materialize deps as artifacts`); - await this.dataSource.query(` - INSERT INTO ${this.dependentsToArtifactTable} ("package_artifact_id", "dependent_artifact_id") - SELECT DISTINCT - a_p.id AS package_artifact_id, - a_d.id AS dependent_artifact_id - FROM - ${this.importedTableName} d - INNER JOIN artifact a_p ON a_p."namespace" = 'NPM_REGISTRY' AND a_p."name" = d.dependent_name - INNER JOIN artifact a_d ON a_d."namespace" = 'NPM_REGISTRY' AND a_d."name" = d.package_name - `); - - logger.debug( - `resolving from repo dependencies to higher order dependencies. this will take a while`, - ); - await this.dataSource.query(` - INSERT INTO ${this.dependentsToArtifactTable} ("package_artifact_id", "dependent_artifact_id") - SELECT - dta.package_artifact_id as package_artifact_id, - rd."repoId" as dependent_artifact_id - FROM repo_dependency rd - INNER JOIN ${this.dependentsToArtifactTable} dta ON dta.dependent_artifact_id = rd."dependencyId" - `); - - logger.debug(`copy deps from repo_dependency table`); - await this.dataSource.query(` - INSERT INTO ${this.dependentsToArtifactTable} ("package_artifact_id", "dependent_artifact_id") - SELECT - rd."dependencyId" AS package_artifact_id, - rd."repoId" AS dependent_artifact_id - FROM repo_dependency rd - `); - - // Creating temporary collections - logger.debug(`create dependent temp collections`); - const convertTempCollectionTypeToDBInput = ( - input: TempCollectionType[], - ) => { - return input.reduce<{ artifactOwnerIds: number[]; ids: number[] }>( - (a, c) => { - a.artifactOwnerIds.push(c.artifactOwnerId); - a.ids.push(c.id); - return a; - }, - { artifactOwnerIds: [], ids: [] }, - ); - }; - - const dependenciesCollections = (await this.dataSource.query( - ` - WITH distinct_dependents AS ( - SELECT DISTINCT - dtp.dependent_artifact_id AS dependent_artifact_id, - a.id AS "name" - FROM ${this.dependentsToArtifactTable} dtp - INNER JOIN artifact a ON dtp.dependent_artifact_id = a.id - ) - INSERT INTO collection("name", "slug", "artifactOwnerId", "typeId") - SELECT - FORMAT('temp-%s-%s-%s', $1::text, $2::text, dd."name"), - FORMAT('temp-%s-%s-%s', $1::text, $2::text, dd."name"), - dd.dependent_artifact_id, - $3::int - FROM distinct_dependents dd - RETURNING "id", "artifactOwnerId" - `, - [ - this.uuid, - // The naming of things is opposite of what it might seem. If it showing - // up as a dependent in the DB that means it has dependencies. Hence this - // being DEPENDENCIES - "ARTIFACT_DEPENDENCIES", - this.collectionTypes["ARTIFACT_DEPENDENCIES"].id, - ], - )) as TempCollectionType[]; - const dependenciesCollectionsDBInput = convertTempCollectionTypeToDBInput( - dependenciesCollections, - ); - - logger.debug(`create dependencies temp collections`); - const dependentsCollections = (await this.dataSource.query( - ` - WITH distinct_dependencies AS ( - SELECT DISTINCT - dtp.package_artifact_id AS package_artifact_id, - a.id AS "name" - FROM ${this.dependentsToArtifactTable} dtp - INNER JOIN artifact a ON dtp.package_artifact_id = a.id - ) - INSERT INTO collection("name", "slug", "artifactOwnerId", "typeId") - SELECT - FORMAT('temp-%s-%s-%s', $1::text, $2::text, dd."name"), - FORMAT('temp-%s-%s-%s', $1::text, $2::text, dd."name"), - dd.package_artifact_id, - $3::int - FROM distinct_dependencies dd - RETURNING "id", "artifactOwnerId" - `, - [ - this.uuid, - // The naming of things is opposite of what it might seem. If it showing - // up as a dependency in the DB that means it has dependents. Hence this - // being DEPENDENTS - "ARTIFACT_DEPENDENTS", - this.collectionTypes["ARTIFACT_DEPENDENTS"].id, - ], - )) as { artifactOwnerId: number; id: number }[]; - const dependentsCollectionsDBInput = convertTempCollectionTypeToDBInput( - dependentsCollections, - ); - - logger.debug("writing dependents to collections"); - await this.dataSource.query( - ` - WITH dependents_to_projects AS ( - SELECT - paa_p."projectId" as package_project_id, - dta."package_artifact_id", - paa_d."projectId" as dependent_project_id, - dta."dependent_artifact_id" - from ${this.dependentsToArtifactTable} dta - inner join project_artifacts_artifact paa_p on paa_p."artifactId" = dta.package_artifact_id - inner join project_artifacts_artifact paa_d on paa_d."artifactId" = dta.dependent_artifact_id - ), dependent_collections AS ( - SELECT * FROM UNNEST( - $1::int[], - $2::int[] - ) AS t(artifact_id, collection_id) - ), insert_dependents AS ( - INSERT INTO collection_projects_project ("collectionId", "projectId") - SELECT DISTINCT dc.collection_id, dtp.dependent_project_id - FROM dependent_collections dc - INNER JOIN dependents_to_projects dtp ON dtp.package_artifact_id = dc.artifact_id - RETURNING "collectionId", "projectId" - ) - select count(*) from insert_dependents - `, - [ - dependentsCollectionsDBInput.artifactOwnerIds, - dependentsCollectionsDBInput.ids, - ], - ); - - logger.debug("writing dependencies to collections"); - await this.dataSource.query( - ` - WITH dependents_to_projects AS ( - SELECT - paa_p."projectId" as package_project_id, - dta."package_artifact_id", - paa_d."projectId" as dependent_project_id, - dta."dependent_artifact_id" - from ${this.dependentsToArtifactTable} dta - inner join project_artifacts_artifact paa_p on paa_p."artifactId" = dta.package_artifact_id - inner join project_artifacts_artifact paa_d on paa_d."artifactId" = dta.dependent_artifact_id - ), dependency_collections AS ( - SELECT * FROM UNNEST( - $1::int[], - $2::int[] - ) AS t(artifact_id, collection_id) - ), insert_dependencies AS ( - INSERT INTO collection_projects_project ("collectionId", "projectId") - SELECT DISTINCT dc.collection_id, dtp.package_project_id - FROM dependency_collections dc - INNER JOIN dependents_to_projects dtp ON dtp.dependent_artifact_id = dc.artifact_id - RETURNING "collectionId", "projectId" - ) - select count(*) from insert_dependencies - `, - [ - dependenciesCollectionsDBInput.artifactOwnerIds, - dependenciesCollectionsDBInput.ids, - ], - ); - - // In a transaction delete the old collection if it exists and rename the current one - await this.commitCollections(dependenciesCollections); - await this.commitCollections(dependentsCollections); - - // Drop temp tables - await this.dataSource.query(` - DROP TABLE ${this.dependentsToArtifactTable} - `); - await this.dataSource.query(` - DROP TABLE ${this.importedTableName} - `); - } - - async commitCollections(tempCollections: TempCollectionType[]) { - for (const { id } of tempCollections) { - const collection = await this.collectionRepository.findOneOrFail({ - relations: { - artifactOwner: true, - type: true, - }, - where: { - id: id as Brand, - }, - }); - - await this.collectionRepository.manager.transaction(async (manager) => { - const repo = manager.withRepository(this.collectionRepository); - - // Find the canonical one if it exists - const artifact = collection.artifactOwner; - if (!artifact) { - throw new Error( - "invalid temporary collection. it has no artifact owner", - ); - } - const canonicalSlug = - `${artifact.name}_${artifact.namespace}_${collection.type.name}` - .toLowerCase() - .replace("@", "at__") - .replace("/", "__"); - - const existing = await repo.findOne({ - relations: { - projects: true, - }, - where: { - slug: canonicalSlug, - }, - }); - - if (existing) { - logger.debug(`removing existing ${canonicalSlug}`); - // delete relations - await repo - .createQueryBuilder() - .relation(Collection, "projects") - .of(existing.id) - .remove(existing.projects); - // delete the collection - - await repo.delete({ - id: existing.id, - }); - } - logger.debug( - `renaming temporary collection[slug=${collection.slug}] to be ${canonicalSlug}`, - ); - - const namePrefix = - collection.type.name === "ARTIFACT_DEPENDENTS" - ? "Dependents of" - : "Dependencies for"; - const name = `${namePrefix} ${artifact.name}`; - - // Rename the current one to the canonical slug - await repo.update( - { - id: collection.id, - }, - { - name: name, - slug: canonicalSlug, - }, - ); - }); - } - } - - async getProjectsForRepos(dependency: Artifact) { - let repos = this.repoDeps[dependency.id]; - if (!repos) { - const projectsForRepos = await this.projectRepository.find({ - where: { - packageDependencies: { - dependencyId: dependency.id, - }, - }, - }); - const uniq = new UniqueArray((p) => p.id); - projectsForRepos.forEach((p) => uniq.push(p)); - this.repoDeps[dependency.id] = uniq; - repos = uniq; - } - return repos.items(); - } - - async resolveDepToProjects(dep: Artifact) { - logger.debug(`resolving to project ${dep.name}`); - console.log(dep.name); - if (Object.keys(this.artifactNameToProjectsMap).length === 0) { - const allRelatedProjects = await this.projectRepository.find({ - relations: { - artifacts: true, - }, - where: { - artifacts: { - id: In(this.packages.map((p) => p.id)), - }, - }, - }); - // Build the mapping - this.artifactNameToProjectsMap = allRelatedProjects.reduce< - Record> - >((acc, p) => { - p.artifacts.forEach((a) => { - const arr = acc[a.name] || new UniqueArray((p) => p.id); - arr.push(p); - acc[a.name] = arr; - }); - return acc; - }, {}); - } - let map = this.artifactNameToProjectsMap[dep.name]; - if (!map) { - this.artifactNameToProjectsMap[dep.name] = new UniqueArray( - (p) => p.id, - ); - map = this.artifactNameToProjectsMap[dep.name]; - } - return map; - } - - get importedTableName() { - return `bq_npm_dependents_${this.uuid.replace(/-/g, "_")}`; - } - - get dependentsToArtifactTable() { - return `npm_dependents_as_artifacts_${this.uuid.replace(/-/g, "_")}`; - } - - resetBatch() { - this.batch = { - packageNames: [], - dependentNames: [], - minimumDepths: [], - }; - } - - async writeBatch() { - logger.debug("writing a batch of dependencies"); - const toWrite = this.batch; - this.resetBatch(); - - if (!this.initialized) { - await this.dataSource.query(` - CREATE TABLE ${this.importedTableName} ( - package_name text, - dependent_name text, - minimum_depth smallint - ) - `); - this.initialized = true; - } - - if (Object.keys(this.collectionTypes).length === 0) { - // Get the collection types - const types = await this.collectionTypeRepository.find({ - where: { - name: In(["ARTIFACT_DEPENDENTS", "ARTIFACT_DEPENDENCIES"]), - }, - }); - if (types.length !== 2) { - throw new Error("required collection types do not exist"); - } - this.collectionTypes = _.keyBy(types, "name"); - } - - await this.dataSource.query( - ` - INSERT INTO ${this.importedTableName} ("package_name", "dependent_name", "minimum_depth") - SELECT * FROM unnest( - $1::text[], $2::text[], $3::smallint[] - ) - `, - [toWrite.packageNames, toWrite.dependentNames, toWrite.minimumDepths], - ); - } - - async getTemporaryCollection( - dep: Artifact, - typeName: "ARTIFACT_DEPENDENTS" | "ARTIFACT_DEPENDENCIES", - ) { - const type = this.collectionTypes[typeName]; - const key = `${dep.name}:${typeName}`; - let collectionQueue = this.tempCollectionQueue[key]; - if (!collectionQueue) { - // Create the collection - const tempName = `temp-${this.uuid}-${dep.name}-${typeName}`; - const collection = Collection.create({ - name: tempName, - slug: tempName, - type: type, - artifactOwner: dep, - }); - await this.collectionRepository.insert(collection); - collectionQueue = { - name: tempName, - collection: collection, - relations: new UniqueArray((p) => p.id), - }; - this.tempCollectionQueue[key] = collectionQueue; - } - return collectionQueue; - } - - getTemporaryDependentsCollection(dep: Artifact) { - return this.getTemporaryCollection(dep, "ARTIFACT_DEPENDENTS"); - } - - getTemporaryDependenciesCollection(dep: Artifact) { - return this.getTemporaryCollection(dep, "ARTIFACT_DEPENDENCIES"); - } -} - -export interface DependentsPeriodicCollectorOptions { - dependentsTableId?: string; -} - -type Repo = { - owner: string; - name: string; -}; - -type DependencyResponse = { - dependencyGraphManifests: { - totalCount: number; - nodes: Array<{ - filename: true; - }>; - edges: Array<{ - node: { - blobPath: string; - parseable: boolean; - filename: string; - dependencies: { - totalCount: number; - nodes: Array<{ - packageName: string; - requirements: string; - hasDependencies: boolean; - packageManager: string; - }>; - }; - }; - }>; - }; -}; - -export class DependentsPeriodicCollector - extends GithubBaseMixin - implements IPeriodicCollector -{ - private artifactRepository: Repository; - private collectionRepository: Repository; - private collectionTypeRepository: Repository; - private projectRepository: Repository; - private repoDepsRepository: Repository; - private bq: BigQuery; - private gcs: Storage; - private gcsBucket: string; - private datasetId: string; - private options: DependentsPeriodicCollectorOptions; - private eventPointerManager: IEventPointerManager; - private dataSource: DataSource; - - constructor( - dataSource: DataSource, - eventPointerManager: IEventPointerManager, - repoDepsRepository: Repository, - artifactRepository: Repository, - collectionRepository: Repository, - collectionTypeRepository: Repository, - projectRepository: Repository, - bq: BigQuery, - datasetId: string, - gcs: Storage, - gcsBucket: string, - options?: DependentsPeriodicCollectorOptions, - ) { - super(); - this.eventPointerManager = eventPointerManager; - this.repoDepsRepository = repoDepsRepository; - this.artifactRepository = artifactRepository; - this.collectionRepository = collectionRepository; - this.collectionTypeRepository = collectionTypeRepository; - this.projectRepository = projectRepository; - this.datasetId = datasetId; - this.bq = bq; - this.gcs = gcs; - this.dataSource = dataSource; - this.gcsBucket = gcsBucket; - this.options = _.merge({}, options); - } - - async ensureDataset() { - const ds = this.bq.dataset(this.datasetId); - - try { - if (!(await ds.exists())) { - throw new Error( - `dataset ${this.datasetId} does not exist. please create it`, - ); - } - } catch (err) { - throw new Error( - `dataset ${this.datasetId} does not exist. please create it`, - ); - } - return ds; - } - - private async getManyRepositoryDeps(locators: Repo[]) { - const multiplex = new MultiplexGithubGraphQLRequester< - Repo, - DependencyResponse - >( - "getManyRepositories", - { - owner: "String!", - name: "String!", - }, - (vars) => { - return { - type: "repository", - definition: { - __args: { - owner: vars.owner, - name: vars.name, - }, - ...repositoryDependencies(), - }, - }; - }, - ); - return await this.rateLimitedGraphQLGeneratedRequest(multiplex, locators); - } - - private async refreshDependenciesForAllRepositories() { - // We are going to abuse the event pointers to implement a TTL for pulling dependency data. - - const artifacts = await this.artifactRepository.find({ - where: { - type: ArtifactType.GIT_REPOSITORY, - namespace: ArtifactNamespace.GITHUB, - }, - }); - - const validArtifacts = artifacts.filter((a) => { - try { - this.splitGithubRepoIntoLocator(a); - return true; - } catch (_e) { - return false; - } - }); - - const today = DateTime.now().toUTC().startOf("day"); - const tomorrow = today.plus({ day: 1 }); - const todayRange = { - startDate: today, - endDate: tomorrow, - }; - const ttlRange = { - startDate: today, - endDate: today.plus({ day: 30 }), - }; - const collectorName = "dependents"; - const missingArtifacts = - await this.eventPointerManager.missingArtifactsForRange( - collectorName, - todayRange, - validArtifacts, - ); - - let count = 0; - // GH doesn't like double digit batched requests it seems. - await asyncBatch(missingArtifacts, 8, async (batch) => { - const locators = batch.map((a) => { - const parsed = this.splitGithubRepoIntoLocator(a); - return { - owner: parsed.owner, - name: parsed.repo, - }; - }); - const res = await this.getManyRepositoryDeps(locators); - for (let i = 0; i < res.items.length; i++) { - const artifact = batch[i]; - const deps = res.items[i]; - if (!deps) { - count += 1; - await this.eventPointerManager.commitArtifactForRange( - collectorName, - ttlRange, - artifact, - ); - continue; - } - - const npmArtifacts = deps.dependencyGraphManifests.edges.flatMap( - (manifest) => { - // One day we can remove the filter for now. Let's keep it. - return manifest.node.dependencies.nodes - .filter((d) => d.packageManager === "NPM") - .map((dep) => { - return { - name: dep.packageName.toLowerCase(), - namespace: ArtifactNamespace.NPM_REGISTRY, - type: ArtifactType.NPM_PACKAGE, - }; - }); - }, - ); - - const uniqNpmArtifacts = _.uniqBy(npmArtifacts, "name"); - - logger.debug(`upserting ${npmArtifacts.length} npm artifacts`); - await this.artifactRepository.upsert(uniqNpmArtifacts, { - conflictPaths: ["namespace", "name"], - upsertType: "on-conflict-do-update", - }); - - const allDeps = await this.artifactRepository.find({ - where: { - name: In(uniqNpmArtifacts.map((a) => a.name)), - namespace: ArtifactNamespace.NPM_REGISTRY, - type: ArtifactType.NPM_PACKAGE, - }, - }); - - const repoDeps = allDeps.map((a) => { - return { - repo: artifact, - dependency: a, - }; - }); - - logger.debug(`updating all ${repoDeps.length} repository dependencies`); - await this.repoDepsRepository.manager.transaction(async (manager) => { - const repo = manager.withRepository(this.repoDepsRepository); - - // Delete all of the deps for this artifact - await repo.delete({ - repo: { - id: artifact.id, - }, - }); - - // Add new ones - await repo.insert(repoDeps); - }); - - await this.eventPointerManager.commitArtifactForRange( - collectorName, - ttlRange, - artifact, - ); - count += 1; - } - logger.debug( - `completed dependency collection [${count}/${missingArtifacts.length}]`, - ); - }); - } - - async collect(): Promise { - logger.debug("collecting dependents for all npm packages"); - - await this.refreshDependenciesForAllRepositories(); - - // Get a list of all `NPM_PACKAGES` in our database - const npmPackages = await this.artifactRepository.find({ - where: { - type: ArtifactType.NPM_PACKAGE, - }, - order: { - id: { direction: "ASC" }, - }, - }); - - logger.debug(`found ${npmPackages.length} npm packages`); - - try { - const dependents = await this.getOrCreateDependentsTable(npmPackages); - await new Promise((resolve, reject) => { - dependents - .createReadStream({ autoPaginate: true }) - .pipe( - new DependentsRecorder( - npmPackages, - this.dataSource, - this.collectionRepository, - this.collectionTypeRepository, - this.projectRepository, - 100000, - ), - ) - .on("close", () => { - logger.debug("completed dependent/dependency collection"); - resolve(); - }) - .on("error", (err) => { - logger.debug("caught an error reading/writing from the stream"); - reject(err); - }); - }); - } catch (err) { - logger.error(`caught error collecting dependencies`, JSON.stringify(err)); - throw err; - } - } - - private async getOrCreateDependentsTable(packages: Artifact[]) { - if (this.options.dependentsTableId) { - logger.debug("using supplied table id"); - const dataset = await this.ensureDataset(); - return dataset.table(this.options.dependentsTableId); - } - - const packagesSha1 = sha1FromArray( - packages.map((a) => { - return `${a.id},${a.name}`; - }), - ); - - // Check if the dataset's table already exists - const tableId = `npm_dependents_${packagesSha1}`; - - logger.debug(`checking for table ${tableId}`); - - const dataset = await this.ensureDataset(); - const destinationTable = dataset.table(tableId); - - const [destinationTableExists] = await destinationTable.exists(); - if (destinationTableExists) { - logger.debug("table exists. no need to query BQ"); - return destinationTable; - } - - await this.queryWithPackages( - dataset, - packagesSha1, - packages, - destinationTable, - ); - - return destinationTable; - } - - private async ensurePackageTable( - dataset: Dataset, - packagesSha1: string, - packages: Artifact[], - ) { - logger.debug(`ensuring the package table exists`); - const bucket = this.gcs.bucket(this.gcsBucket); - const tableId = `oso_npm_packages_${packagesSha1}`; - const file = bucket.file(`${tableId}.csv`); - - const passThroughStream = new stream.PassThrough(); - passThroughStream.write("id,package_name\n"); - packages.forEach((p) => passThroughStream.write(`${p.id},${p.name}\n`)); - passThroughStream.end(); - - await new Promise((resolve, reject) => { - passThroughStream - .pipe(file.createWriteStream()) - .on("finish", () => { - resolve(); - }) - .on("error", (err) => { - reject(err); - }); - }); - - // Create a BQ table with the newly uploaded .csv - const uploadMeta = { - sourceFormat: "CSV", - skipLeadingRows: 1, - schema: { - fields: [ - { name: "id", type: "INT64" }, - { name: "package_name", type: "STRING" }, - ], - }, - location: "US", - }; - const table = dataset.table(tableId); - const [job] = await table.load(file, uploadMeta); - const errors = job.status?.errors; - if (errors && errors.length > 0) { - logger.debug(`failed to upload table to ${tableId} on job ${job.id}`); - throw errors; - } - const [metadata] = await table.getMetadata(); - console.log(metadata); - logger.debug( - `uploaded table to ${tableId} on job ${job.id} ${table.projectId}.${table.dataset.id}.${table.id}`, - ); - return `${metadata.tableReference.projectId}.${table.dataset.id}.${table.id}`; - } - - private async queryWithPackages( - dataset: Dataset, - packagesSha1: string, - packages: Artifact[], - destinationTable: Table, - ) { - const packageTableName = await this.ensurePackageTable( - dataset, - packagesSha1, - packages, - ); - - // Query the bigquery public dataset into a temporary table - // - // TODO: For now this is hardcoded to the snapshot of deps from 2023-10-16 - // to reduce the number of results to scan on BQ - const query = ` - SELECT - Name as package_name, - Dependent.Name as dependent_name, - MIN(MinimumDepth) as minimum_depth - FROM - \`bigquery-public-data.deps_dev_v1.Dependents\` AS d - INNER JOIN \`${packageTableName}\` AS pp - ON Lower(pp.package_name) = Lower(d.Name) - INNER JOIN \`${packageTableName}\` as pd - ON Lower(pd.package_name) = Lower(d.Dependent.Name) - WHERE - TIMESTAMP_TRUNC(SnapshotAt, DAY) = TIMESTAMP('2023-10-16') - AND System = 'NPM' - AND MinimumDepth < 4 - GROUP BY 1,2 - `; - - const options = { - query: query, - location: "US", - destination: destinationTable, - }; - const [job] = await this.bq.createQueryJob(options); - // Wait for the job to complete - await job.getQueryResults({ maxResults: 0 }); - logger.debug(`biqquery job complete`); - } -} diff --git a/indexer/src/collectors/dune-daily-contract-usage.ts b/indexer/src/collectors/dune-daily-contract-usage.ts deleted file mode 100644 index 87ab5ff1e..000000000 --- a/indexer/src/collectors/dune-daily-contract-usage.ts +++ /dev/null @@ -1,420 +0,0 @@ -import { DateTime } from "luxon"; -import { CommonArgs } from "../utils/api.js"; -import { logger } from "../utils/logger.js"; -import { - Artifact, - ArtifactNamespace, - ArtifactType, -} from "../db/orm-entities.js"; -import fsPromises from "fs/promises"; -import { - IDailyContractUsageClientV2, - DailyContractUsageRow, - Contract, -} from "./dune/daily-contract-usage/client.js"; -import { - IArtifactGroup, - IArtifactGroupCommitmentProducer, -} from "../scheduler/types.js"; -import _ from "lodash"; -import { Range } from "../utils/ranges.js"; -import { - IEventRecorderClient, - IncompleteArtifact, - IncompleteEvent, -} from "../recorder/types.js"; -import { - TimeSeriesCacheLookup, - TimeSeriesCacheWrapper, -} from "../cacher/time-series.js"; -import { sha1FromArray } from "../utils/source-ids.js"; -import { BaseEventCollector, BasicArtifactGroup } from "../scheduler/common.js"; -import { UniqueArray } from "../utils/array.js"; -import { ProjectRepository } from "../db/project.js"; -import { In } from "typeorm"; -import { ArtifactGroupRecorder } from "../recorder/group.js"; - -/** - * Entrypoint arguments - */ -export type ImportDailyContractUsage = CommonArgs & { - skipExisting?: boolean; - baseDate?: DateTime; -}; - -export interface DailyContractUsageSyncerOptions { - // The number of days to sync. More days is more efficient use of API credits - // within dune. However, we will likely need to set larger datapoint limits - // per response for many days - intervalLengthInDays: number; - - // To ensure we only get complete data we should not index 2 days prior to today. - offsetDays: number; - - knownUserAddressesSeedPath: string; - - // A cache directory for responses which will be used by github actions to - // load any response we get from a previous run (tbd). This will hopefully - // reduce our API credit usage within dune - cacheOptions: { - bucket: string; - }; - - // The query id in dune to call. The default is below - contractUsersQueryId: number; - - // User address create batch size - batchCreateSize: number; - - // batch read size - batchReadSize: number; - - // Janky... currently passed into the DailyContractUsageResponse object to - // dictate how many contracts we process in "parallel". This would essentially - // dictate how many postgres connections we made (in theory?) - parallelism: number; - - blockchain: ArtifactNamespace; - - mode: "csv" | "api"; -} - -export const DefaultDailyContractUsageSyncerOptions: DailyContractUsageSyncerOptions = - { - intervalLengthInDays: 7, - offsetDays: 2, - cacheOptions: { - bucket: "contract-daily-usage", - }, - - knownUserAddressesSeedPath: "/tmp/known-addresses-seed.json", - - // This default is based on this: https://dune.com/queries/2835126 - contractUsersQueryId: 2835126, - - batchCreateSize: 2500, - - batchReadSize: 10000, - - parallelism: 10, - - blockchain: ArtifactNamespace.OPTIMISM, - - mode: "api", - }; - -export class DailyContractUsageCollector extends BaseEventCollector { - private client: IDailyContractUsageClientV2; - private projectRepository: typeof ProjectRepository; - private recorder: IEventRecorderClient; - private cache: TimeSeriesCacheWrapper; - private options: DailyContractUsageSyncerOptions; - private rowsProcessed: number; - - constructor( - client: IDailyContractUsageClientV2, - projectRepository: typeof ProjectRepository, - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - options: Partial = DefaultDailyContractUsageSyncerOptions, - ) { - super(); - this.client = client; - this.projectRepository = projectRepository; - this.options = _.merge(DefaultDailyContractUsageSyncerOptions, options); - this.recorder = recorder; - this.cache = cache; - this.rowsProcessed = 0; - } - - async allArtifacts(): Promise { - const artifacts: Artifact[] = []; - for await (const group of this.groupedArtifacts()) { - artifacts.push(...(await group.artifacts())); - } - return artifacts; - } - - async *groupedArtifacts(): AsyncGenerator> { - // Get all contracts - const projects = await this.projectRepository.find({ - relations: { - artifacts: true, - }, - where: { - artifacts: { - type: In([ - ArtifactType.CONTRACT_ADDRESS, - ArtifactType.FACTORY_ADDRESS, - ]), - namespace: ArtifactNamespace.OPTIMISM, - }, - }, - }); - const allArtifacts = projects.flatMap((p) => p.artifacts); - - const uniqueArtifacts = new UniqueArray((a: Artifact) => a.id); - allArtifacts.forEach((a) => uniqueArtifacts.push(a)); - yield new BasicArtifactGroup("ALL_CONTRACTS", {}, uniqueArtifacts.items()); - } - - async collect( - group: IArtifactGroup, - range: Range, - committer: IArtifactGroupCommitmentProducer, - ): Promise { - logger.info("loading contract usage data"); - const artifacts = await group.artifacts(); - const contractsByAddressMap = _.keyBy(artifacts, "name"); - - // There seems to be some duplicates. Let's keep track for logging for now. - const uniqueEvents = new UniqueArray((s) => s); - - const groupRecorder = new ArtifactGroupRecorder(this.recorder); - try { - if (this.options.mode === "api") { - await this.collectFromApi( - groupRecorder, - artifacts, - range, - contractsByAddressMap, - uniqueEvents, - ); - } else { - await this.collectFromCsv( - groupRecorder, - range, - contractsByAddressMap, - uniqueEvents, - ); - } - } catch (err) { - logger.error("error collecting contract usage"); - logger.error(err); - throw err; - } - - logger.debug("done processing contract calls"); - committer.commitGroup(groupRecorder); - } - - protected async collectFromApi( - groupRecorder: ArtifactGroupRecorder, - artifacts: Artifact[], - range: Range, - contractsByAddressMap: _.Dictionary, - uniqueEvents: UniqueArray, - ) { - const contracts: Contract[] = artifacts.map((a) => { - return { - id: a.id, - address: a.name.toLowerCase(), - }; - }); - - const responses = this.cache.loadCachedOrRetrieve( - TimeSeriesCacheLookup.new( - this.options.cacheOptions.bucket, - contracts.map((c) => c.address), - range, - ), - async (missing) => { - const rows = await this.client.getDailyContractUsage(missing.range, { - contracts: contracts, - }); - return { - raw: rows, - hasNextPage: false, - cacheRange: missing.range, - }; - }, - ); - - for await (const page of responses) { - for (const row of page.raw) { - const contract = contractsByAddressMap[row.contractAddress]; - await this.createEvents(groupRecorder, contract, row, uniqueEvents); - } - } - } - - protected async collectFromCsv( - groupRecorder: ArtifactGroupRecorder, - range: Range, - contractsByAddressMap: _.Dictionary, - uniqueEvents: UniqueArray, - ) { - let currentTime = range.startDate; - while (currentTime < range.endDate) { - logger.debug(`loading ${currentTime.toISODate()}`); - const rows = await this.client.getDailyContractUsageFromCsv(currentTime); - - currentTime = currentTime.plus({ day: 1 }); - for (const row of rows) { - const contract = contractsByAddressMap[row.contractAddress]; - await this.createEvents(groupRecorder, contract, row, uniqueEvents); - } - } - } - - protected async loadKnownUserAddressesSeed(): Promise< - { id: number; name: string }[] - > { - const knownAddressesSeedPath = this.options.knownUserAddressesSeedPath; - - const usersRaw = await fsPromises.readFile(knownAddressesSeedPath, { - encoding: "utf-8", - }); - const users = JSON.parse(usersRaw) as Array<[string, string]>; - - return users.map((u) => { - return { - id: parseInt(u[0]), - name: u[1], - }; - }); - } - - // protected retrieveFromDune( - // range: Range, - // ) { - // logger.debug("retrieving data from dune"); - // const response = this.client.getDailyContractUsage( - // range.startDate, - // range.endDate, - // "da1aae77b853fc7c74038ee08eec441b10b89570" - // ); - - // return response; - // } - - protected async createEvents( - groupRecorder: ArtifactGroupRecorder, - contract: Artifact, - row: DailyContractUsageRow, - uniqueTracker: UniqueArray, - ): Promise { - this.rowsProcessed += 1; - const eventTime = DateTime.fromISO(row.date); - - if (!row.userAddress && !row.safeAddress) { - throw new Error("unexpected: no address"); - } - - const from: IncompleteArtifact = !row.safeAddress - ? { - name: row.userAddress!.toLowerCase(), - type: ArtifactType.EOA_ADDRESS, - namespace: ArtifactNamespace.OPTIMISM, - url: `https://optimistic.etherscan.io/address/${row.userAddress}`, - } - : { - name: row.safeAddress.toLowerCase(), - type: ArtifactType.SAFE_ADDRESS, - namespace: ArtifactNamespace.OPTIMISM, - url: `https://optimistic.etherscan.io/address/${row.safeAddress}`, - }; - - if (!row.contractAddress) { - console.log(row); - throw new Error("no artifact to record to"); - } - const recorderContract = - contract !== undefined - ? contract - : { - name: row.contractAddress, - type: ArtifactType.CONTRACT_ADDRESS, - namespace: ArtifactNamespace.OPTIMISM, - url: `https://optimistic.etherscan.io/address/${row.contractAddress}`, - }; - - const eventTimeAsStr = eventTime.toISODate(); - if (eventTimeAsStr === null) { - throw new Error("event time must be a date"); - } - - const sourceId = sha1FromArray([ - "CONTRACT_INVOCATION", - eventTimeAsStr, - recorderContract.name, - recorderContract.namespace, - recorderContract.type, - from.name, - from.namespace, - from.type, - ]); - - // Convert gasCost to a bigint - let l2GasAsFloat = 0; - try { - l2GasAsFloat = parseFloat(row.l2GasUsed); - } catch (err) { - console.warn( - `Could not get gasCost for ${sourceId}. Value ${row.l2GasUsed} is not a number`, - ); - } - - let l1GasAsFloat = 0; - try { - l1GasAsFloat = parseFloat(row.l1GasUsed); - } catch (err) { - console.warn( - `Could not get gasCost for ${sourceId}. Value ${row.l1GasUsed} is not a number`, - ); - } - - // Check which users already have data written for that day. - // We'll need to update those - const countEvent: IncompleteEvent = { - time: eventTime, - type: { - name: "CONTRACT_INVOCATION_DAILY_COUNT", - version: 1, - }, - to: recorderContract, - from: from, - amount: row.txCount, - sourceId: sourceId, - }; - - const l2GasUsedEvent: IncompleteEvent = { - time: eventTime, - type: { - name: "CONTRACT_INVOCATION_DAILY_L2_GAS_USED", - version: 1, - }, - to: recorderContract, - from: from, - amount: l2GasAsFloat, - sourceId: sourceId, - }; - - const l1GasUsedEvent: IncompleteEvent = { - time: eventTime, - type: { - name: "CONTRACT_INVOCATION_DAILY_L1_GAS_USED", - version: 1, - }, - to: recorderContract, - from: from, - amount: l1GasAsFloat, - sourceId: sourceId, - }; - - const beforeAddLen = uniqueTracker.length; - uniqueTracker.push(countEvent.sourceId); - if (uniqueTracker.length === beforeAddLen) { - logger.debug( - `duplicates for sourceId=${countEvent.sourceId} found for contract[${ - countEvent.to.name - }] from address[${ - countEvent.from?.name - }] on ${countEvent.time.toISODate()}`, - ); - } - await groupRecorder.record(countEvent); - await groupRecorder.record(l2GasUsedEvent); - await groupRecorder.record(l1GasUsedEvent); - } -} diff --git a/indexer/src/collectors/dune-funding-events.ts b/indexer/src/collectors/dune-funding-events.ts deleted file mode 100644 index 8b5376ebb..000000000 --- a/indexer/src/collectors/dune-funding-events.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { DateTime } from "luxon"; -import { logger } from "../utils/logger.js"; -import { - FundingPoolAddress, - IFundingEventsClient, - ProjectAddress, -} from "./dune/funding-events/client.js"; -import { ArtifactNamespace, ArtifactType } from "../db/orm-entities.js"; -import { - IEventRecorderClient, - IncompleteArtifact, - IncompleteEvent, -} from "../recorder/types.js"; -import { - TimeSeriesCacheLookup, - TimeSeriesCacheWrapper, -} from "../cacher/time-series.js"; -import _ from "lodash"; -import { - IArtifactGroup, - IArtifactGroupCommitmentProducer, -} from "../scheduler/types.js"; -import { Range } from "../utils/ranges.js"; -import { ProjectRepository } from "../db/project.js"; -import { - Batch, - BatchedProjectArtifactsCollector, -} from "../scheduler/common.js"; -import { ArtifactGroupRecorder } from "../recorder/group.js"; -import { In } from "typeorm"; - -export interface FundingEventsCollectorOptions { - cacheOptions: { - bucket: string; - }; -} - -export const DefaultFundingEventsCollectorOptions: FundingEventsCollectorOptions = - { - cacheOptions: { - bucket: "funding-events", - }, - }; - -export class FundingEventsCollector extends BatchedProjectArtifactsCollector { - private client: IFundingEventsClient; - private options: FundingEventsCollectorOptions; - - constructor( - client: IFundingEventsClient, - projectRepository: typeof ProjectRepository, - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - batchSize: number, - options?: Partial, - ) { - super(projectRepository, recorder, cache, batchSize, { - type: In([ArtifactType.EOA_ADDRESS, ArtifactType.SAFE_ADDRESS]), - }); - this.client = client; - this.projectRepository = projectRepository; - this.recorder = recorder; - this.options = _.merge(DefaultFundingEventsCollectorOptions, options); - this.cache = cache; - } - - // async *groupedArtifacts(): AsyncGenerator> { - // logger.debug("gathering artifacts"); - // const projects = - // await this.projectRepository.allFundableProjectsWithAddresses(); - - // for (const project of projects) { - // const artifacts: Artifact[] = project.artifacts - // .filter((a) => a.name.length == 42) - // .map((a) => { - // return a; - // }); - // yield ProjectArtifactGroup.create(project, artifacts); - // } - // } - - async collect( - group: IArtifactGroup, - range: Range, - committer: IArtifactGroupCommitmentProducer, - ): Promise { - logger.debug("running funding events collector"); - const artifacts = await group.artifacts(); - const filteredArtifacts = artifacts.filter((a) => { - return a.name.length === 42; - }); - - const groupRecorder = new ArtifactGroupRecorder(this.recorder); - // Super pragmatic hack for now to create the funding addresses. Let's just make them now - const fundingAddressesRaw: Array<[string, string, string, string]> = [ - [ - "Optimism", - "0x19793c7824be70ec58bb673ca42d2779d12581be", - "RPGF2", - "optimism", - ], - [ - "DAO Drops", - "0xafe5f7a1d1c173b311047cdc93729013ad03de0c", - "DAO Drops 1", - "mainnet", - ], - [ - "Ethereum Foundation", - "0x9ee457023bb3de16d51a003a247baead7fce313d", - "Grants Provider", - "mainnet", - ], - [ - "Gitcoin", - "0x2878883dd4345c7b35c13fefc5096dd400814d91", - "GR14", - "mainnet", - ], - [ - "Gitcoin", - "0xf63fd0739cb68651efbd06bccb23f1a1623d5520", - "GR13", - "mainnet", - ], - [ - "Gitcoin", - "0xab8d71d59827dcc90fedc5ddb97f87effb1b1a5b", - "GR12", - "mainnet", - ], - [ - "Gitcoin", - "0x0ebd2e2130b73107d0c45ff2e16c93e7e2e10e3a", - "GR11", - "mainnet", - ], - [ - "Gitcoin", - "0x3ebaffe01513164e638480404c651e885cca0aa4", - "GR10", - "mainnet", - ], - [ - "Gitcoin", - "0x3342e3737732d879743f2682a3953a730ae4f47c", - "GR9", - "mainnet", - ], - [ - "Gitcoin", - "0xf2354570be2fb420832fb7ff6ff0ae0df80cf2c6", - "GR8", - "mainnet", - ], - [ - "Gitcoin", - "0x7d655c57f71464b6f83811c55d84009cd9f5221c", - "Bulk Checkout", - "mainnet", - ], - ]; - - const fundingAddressesAsContributors: IncompleteArtifact[] = - fundingAddressesRaw.map((r) => { - return { - name: r[1], - namespace: - r[3] === "optimism" - ? ArtifactNamespace.OPTIMISM - : ArtifactNamespace.ETHEREUM, - type: ArtifactType.CONTRACT_ADDRESS, - url: - r[3] === "optimism" - ? `https://optimistic.etherscan.io/address/${r[1]}` - : `https://etherscan.io/address/${r[1]}`, - details: { - fundingPoolName: r[2], - blockchain: r[3], - }, - }; - }); - - const fundingAddressesInput: FundingPoolAddress[] = fundingAddressesRaw.map( - (r, i) => { - return { - id: i, - groupId: i, - address: r[1], - }; - }, - ); - - const fundingAddressMap = fundingAddressesAsContributors.reduce< - Record - >((acc, curr) => { - acc[curr.name] = curr; - return acc; - }, {}); - - const projectAddressesInput: Array< - ProjectAddress & { namespace: ArtifactNamespace; type: ArtifactType } - > = filteredArtifacts.map((a, i) => { - return { - id: i, - projectId: i, - address: a.name.toLowerCase(), - namespace: a.namespace, - type: a.type, - }; - }); - const addressLookupMap = projectAddressesInput.reduce< - Record - >((a, c) => { - a[c.address] = c; - return a; - }, {}); - - const projectAddressesMap = projectAddressesInput.reduce< - Record - >((acc, curr) => { - acc[curr.address] = { - name: curr.address, - namespace: curr.namespace, - type: curr.type, - }; - return acc; - }, {}); - - // Create a lookup - const responses = this.cache.loadCachedOrRetrieve( - TimeSeriesCacheLookup.new( - this.options.cacheOptions.bucket, - projectAddressesInput.map((a) => a.address), - range, - ), - async (missing) => { - return this.client.getFundingEvents( - missing.range.startDate, - missing.range.endDate, - fundingAddressesInput, - missing.keys.map((a) => addressLookupMap[a]), - ); - }, - ); - - for await (const res of responses) { - for (const row of res.raw) { - const artifact = projectAddressesMap[row.to]; - if (!artifact) { - logger.warn(`found an empty artifact for ${row.to}`); - continue; - } - const contributor = fundingAddressMap[row.from]; - let amountAsFloat = 0.0; - try { - amountAsFloat = parseFloat(row.value); - } catch (e) { - logger.error( - "failed to parse amount as a float. The value is still stored as a string", - ); - } - - const event: IncompleteEvent = { - time: DateTime.fromISO(row.blockTime, { zone: "utc" }), - type: { - name: "FUNDING", - version: 1, - }, - to: artifact, - from: contributor, - sourceId: row.txHash, - - // Worried this could fail on very large values - amount: amountAsFloat, - details: { - amountAsString: row.value, - txHash: row.txHash, - token: row.token, - blockchain: row.blockchain, - }, - }; - - await groupRecorder.record(event); - } - } - - committer.commitGroup(groupRecorder); - } -} diff --git a/indexer/src/collectors/dune/daily-contract-usage/client.test.ts b/indexer/src/collectors/dune/daily-contract-usage/client.test.ts deleted file mode 100644 index bb49a5f1a..000000000 --- a/indexer/src/collectors/dune/daily-contract-usage/client.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { - parseDuneContractUsageCSVRow, - DuneRawRow, - parseDuneCSVArray, - SafeAggregate, - DailyContractUsageRow, -} from "./client.js"; -import { parse } from "csv/sync"; - -// CSV Snippet that's easy to read -const contractCsvSnippet0 = ` -date,contract_id,usage -2022-08-28 00:00:00.000 UTC,123,[[0x1111111111111111111111111111111111111111 0x1111111111111111111111111111111111111111 1000000 1000000 1] [0x2222222222222222222222222222222222222222 2000000000000 2000000000000 2] [ 0x3333333333333333333333333333333333333333 3000000000000000000000000 3000000000000000000000000 3]] -`.trim(); - -const expectedContractCsvSnippet0: DuneRawRow = { - date: "2022-08-28 00:00:00.000 UTC", - contract_id: 123, - usage: [ - [ - "0x1111111111111111111111111111111111111111", - "0x1111111111111111111111111111111111111111", - "1000000", - "1000000", - 1, - ], - [ - "0x2222222222222222222222222222222222222222", - null, - "2000000000000", - "2000000000000", - 2, - ], - [ - null, - "0x3333333333333333333333333333333333333333", - "3000000000000000000000000", - "3000000000000000000000000", - 3, - ], - ], -}; - -describe("resolveDailyContractUsageResults", () => { - test("should parse the csv rows from dune", () => { - const parsed = parse(contractCsvSnippet0) as string[][]; - const result = parseDuneContractUsageCSVRow(parsed[1]); - expect(result).toEqual(expectedContractCsvSnippet0); - }); - - test("should parse generic csv array from dune", () => { - const csvArray0 = "[1234 test0]"; - const expectedArray0 = ["1234", "", "test0"]; - const csvArray1 = "[4567 test1 ]"; - const expectedArray1 = ["4567", "test1", ""]; - // Nested Array - const csvArrayNested0 = `[${csvArray0} ${csvArray1}]`; - expect(parseDuneCSVArray(csvArray0)).toEqual(expectedArray0); - expect(parseDuneCSVArray(csvArray1)).toEqual(expectedArray1); - expect(parseDuneCSVArray(csvArrayNested0)).toEqual([ - expectedArray0, - expectedArray1, - ]); - }); - - test("should throw error when parsing generic csv array from dune that is poorly formatted", () => { - const csvArray0 = "[1234 test0"; - // Nested Array - expect(() => { - parseDuneCSVArray(csvArray0); - }).toThrow(); - }); -}); - -describe("SafeAggregate", () => { - const safesToAggregate: DailyContractUsageRow[] = [ - { - date: "2023-10-11T00:00:00Z", - contractAddress: "0x1111111111111111111111111111111111111111", - userAddress: null, - safeAddress: "0x1111111111111111111111111111111111111111", - l2GasUsed: "10000", - l1GasUsed: "10000", - txCount: 11, - }, - { - date: "2023-10-11T00:00:00Z", - contractAddress: "0x1111111111111111111111111111111111111111", - userAddress: null, - safeAddress: "0x1111111111111111111111111111111111111111", - l2GasUsed: "20000", - l1GasUsed: "20000", - txCount: 22, - }, - { - date: "2023-10-11T00:00:00Z", - contractAddress: "0x1111111111111111111111111111111111111111", - userAddress: null, - safeAddress: "0x1111111111111111111111111111111111111111", - l2GasUsed: "30002", - l1GasUsed: "30001", - txCount: 33, - }, - ]; - - test("should aggregrate safe values in the csv", () => { - const aggregator = new SafeAggregate(safesToAggregate[0]); - - for (const safe of safesToAggregate.slice(1)) { - aggregator.add(safe); - } - const agg = aggregator.aggregate(); - expect(agg.contractAddress).toEqual( - "0x1111111111111111111111111111111111111111", - ); - expect(agg.safeAddress).toEqual( - "0x1111111111111111111111111111111111111111", - ); - expect(agg.userAddress).toBeNull(); - expect(agg.l2GasUsed).toEqual("60002"); - expect(agg.l1GasUsed).toEqual("60001"); - expect(agg.txCount).toEqual(66); - }); -}); diff --git a/indexer/src/collectors/dune/daily-contract-usage/client.ts b/indexer/src/collectors/dune/daily-contract-usage/client.ts deleted file mode 100644 index 4568d43ef..000000000 --- a/indexer/src/collectors/dune/daily-contract-usage/client.ts +++ /dev/null @@ -1,520 +0,0 @@ -/** - * A client for our daily contract usage query on Dune - * - * The client will resolve everything in the request to raw addresses as we - * currently use a method for querying dune that involves passing in - * known-user-addresses + ids and known-contract-addresses + ids. This reduces - * the burden of the api calls on our quota by about 20% in testing. - */ -import { DateTime } from "luxon"; -import fs from "fs"; - -import { IDuneClient } from "../../../utils/dune/type.js"; -import { parse } from "csv"; -import { assert } from "../../../utils/common.js"; -import { QueryParameter } from "@cowprotocol/ts-dune-client"; -import { Range } from "../../../utils/ranges.js"; -import path from "path"; -import _ from "lodash"; -import { logger } from "../../../utils/logger.js"; -import { UniqueArray } from "../../../utils/array.js"; -import { sha1FromArray } from "../../../utils/source-ids.js"; -import { DuneCSVUploader } from "../utils/csv-uploader.js"; - -export function stringToBigInt(x: string) { - try { - return BigInt(x); - } catch (_err) { - return BigInt(parseFloat(x)); - } -} - -export function stringToInt(x: string) { - // If it contains a scientific notation. parse as a float. - if (x.indexOf("e") !== -1) { - return parseFloat(x); - } - - return parseInt(x); -} - -export class SafeAggregate { - rows: DailyContractUsageRow[]; - id: string; - - constructor(first: DailyContractUsageRow) { - this.rows = [first]; - } - - aggregate(): DailyContractUsageRow { - return this.rows.slice(1).reduce((agg, curr) => { - const row = curr; - const l2GasUsed = - stringToBigInt(agg.l2GasUsed) + stringToBigInt(row.l2GasUsed); - const l1GasUsed = - stringToBigInt(agg.l1GasUsed) + stringToBigInt(row.l1GasUsed); - return { - date: agg.date, - contractAddress: agg.contractAddress, - userAddress: agg.userAddress, - safeAddress: agg.safeAddress, - txCount: agg.txCount + row.txCount, - l2GasUsed: l2GasUsed.toString(10), - l1GasUsed: l1GasUsed.toString(10), - }; - }, this.rows[0]); - } - - add(row: DailyContractUsageRow) { - this.rows.push(row); - } - - get count() { - return this.rows.length; - } -} - -export type DailyContractUsageRow = { - date: string; - contractAddress: string; - userAddress: string | null; - safeAddress: string | null; - l2GasUsed: string; - l1GasUsed: string; - txCount: number; -}; - -export type DailyContractUsageRawRow = { - date: string; - contractId: number; - userAddress: string | null; - safeAddress: string | null; - l2GasUsed: string; - l1GasUsed: string; - txCount: number; -}; - -export type Contract = { - id: number; - address: string; -}; - -export type ContractTableReference = { - contracts?: Contract[]; - tableId?: string; -}; - -export interface IDailyContractUsageClientV2 { - getDailyContractUsage( - range: Range, - contractTableReference: ContractTableReference, - ): Promise; - getDailyContractUsageFromCsv( - date: DateTime, - ): Promise; -} - -export interface DailyContractUsageClientOptions { - queryId: number; - - // Tables directory - tablesDirectoryPath: string; - - // Load from CSV Path - csvDirPath: string; -} - -export const DefaultDailyContractUsageClientOptions: DailyContractUsageClientOptions = - { - // This default is based on this: https://dune.com/queries/3198847 - queryId: 3198847, - - tablesDirectoryPath: "", - - csvDirPath: "", - }; - -export type Addresses = string[]; - -export type DuneRawUsageItem = [ - // eoa - string | null, - // safe - string | null, - // totalL2GasUsedForUser - string, - // totalL1GasUsedForUser - string, - // txCount - number of transactions - number, -]; - -export interface DuneRawRow { - date: string; - contract_id: number; - usage: DuneRawUsageItem[]; -} - -export function parseDuneContractUsageCSVRow(row: string[]): DuneRawRow { - // Parse the array of values from inside of the dune row - const usageArray = parseDuneCSVArray(row[2]); - const usage: DuneRawUsageItem[] = usageArray.map((u) => { - // Check the types - assert(!Array.isArray(u[0]), "eoa address should be a string"); - assert(!Array.isArray(u[1]), "safe address should be a string"); - assert(!Array.isArray(u[2]), "l2 gas used should be a string"); - assert(!Array.isArray(u[3]), "l1 gas used should be a string"); - assert(!Array.isArray(u[4]), "call count should be a string"); - - return [ - u[0] === "" ? null : (u[0] as string), - u[1] === "" ? null : (u[1] as string), - u[2] as string, - u[3] as string, - stringToInt(u[4] as string), - ]; - }); - return { - date: row[0], - contract_id: stringToInt(row[1]), - usage: usage, - }; -} - -export function transformDuneRawRowToUsageRows( - row: DuneRawRow, - contractsMap: Record, -): DailyContractUsageRow[] { - const rows = transformDuneRawRowToUsageRawRows(row); - - return rows.map((r) => { - if (!contractsMap[r.contractId]) { - throw new Error(`missing contract address for ${r.contractId} in map`); - } - return { - date: r.date, - contractAddress: contractsMap[r.contractId], - userAddress: r.userAddress, - safeAddress: r.safeAddress, - l2GasUsed: r.l2GasUsed, - l1GasUsed: r.l1GasUsed, - txCount: r.txCount, - }; - }); -} - -export function transformDuneRawRowToUsageRawRows( - row: DuneRawRow, -): DailyContractUsageRawRow[] { - const date = DateTime.fromSQL(row.date).toUTC().toISO(); - if (date === null) { - throw new Error("invalid date"); - } - - // Expand raw rows into more rows - return row.usage.map((usage) => { - return { - date: date, - contractId: row.contract_id, - userAddress: usage[0], - safeAddress: usage[1], - l2GasUsed: usage[2], - l1GasUsed: usage[3], - txCount: usage[4], - }; - }); -} - -type ParsedArrayValue = string | ParsedArrayValue[]; - -export function parseDuneCSVArray(arrayString: string): ParsedArrayValue[] { - // Simple recursive scan to parse - const recursiveParser = ( - index: number, - depth: number, - ): [ParsedArrayValue[], number] => { - index += 1; - - const result: ParsedArrayValue[] = []; - let buffer = ""; - - while (index < arrayString.length) { - if (arrayString[index] == "]") { - if (buffer !== "") { - result.push(buffer); - } - return [result, index + 1]; - } - if (arrayString[index] == "[") { - const [res, nextIndex] = recursiveParser(index, depth + 1); - index = nextIndex; - result.push(res); - continue; - } - if (arrayString[index] != " ") { - buffer += arrayString[index]; - } - // We've reached the end of this string. - if (arrayString[index] == " " && buffer !== "") { - result.push(buffer); - buffer = ""; - } - index += 1; - } - throw new Error("Invalid data. An array is unterminated"); - }; - return recursiveParser(0, 0)[0]; -} - -type ContractTableRow = { - id: string; - address: string; -}; - -export function loadContractsTable(path: string): Promise { - const stream = fs.createReadStream(path); - const result: ContractTableRow[] = []; - - return new Promise((resolve, reject) => { - return stream - .pipe(parse({ delimiter: ",", fromLine: 2 })) - .on("data", (row) => { - result.push({ - id: row[0], - address: row[1], - }); - }) - .on("error", (err) => { - reject(err); - }) - .on("finish", () => { - resolve(result); - }); - }); -} - -export interface IDailyContractUsageResponse { - uniqueUserAddresses(): string[]; - contractAddresses(): string[]; - toJSON(): string; - mapRowsByContractAddress( - cb: (contractAddress: string, rows: DailyContractUsageRow[]) => R, - ): Array; -} - -export class DailyContractUsageClient implements IDailyContractUsageClientV2 { - private client: IDuneClient; - private uploader: DuneCSVUploader; - private options: DailyContractUsageClientOptions; - - constructor( - client: IDuneClient, - uploader: DuneCSVUploader, - options?: Partial, - ) { - this.client = client; - this.uploader = uploader; - this.options = _.merge(DefaultDailyContractUsageClientOptions, options); - } - - async uploadContractTable(contracts: Contract[]) { - const uniqueContracts = new UniqueArray((c) => c.address); - contracts.forEach((c) => uniqueContracts.push(c)); - const sortedUniqueContracts = uniqueContracts.items(); - - console.log(`SORTED UNIQUE COUNT: ${sortedUniqueContracts.length}`); - // Sort by creation - sortedUniqueContracts.sort((a, b) => a.id - b.id); - - const rows = ["id,address"]; - rows.push( - ...sortedUniqueContracts.map((a) => { - return `${a.id},${a.address}`; - }), - ); - const artifactsCsv = rows.join("\n"); - - const contractsCsvSha1 = sha1FromArray([artifactsCsv]); - console.log(`sha1=${contractsCsvSha1}`); - - logger.debug("about to upload contracts table to dune"); - logger.debug(`sha1=${contractsCsvSha1}`); - logger.debug(`count=${sortedUniqueContracts.length}`); - - const tableName = `oso_optimism_contracts_${contractsCsvSha1}`; - - const response = await this.uploader.upload( - tableName, - `OSO monitored optimism contracts: ${contractsCsvSha1}.`, - rows, - ); - if (response.status !== 200) { - throw new Error("failed to upload contracts table to dune"); - } - logger.debug(`uploaded to ${tableName}`); - return contractsCsvSha1; - } - - /** - * Refreshes our dune [query](https://dune.com/queries/3083184) to resolve the - * daily contract usage. This uses a pre-uploaded version of the contract - * addresses that we store in this database. When the monitored contracts - * changes, we need to upload a new version to dune. - * - * @param range The date range for the query execution - * @param contractsSha1 sha1 of the data used for the uploaded csv - */ - async getDailyContractUsage( - range: Range, - contractTableReference: ContractTableReference, - ): Promise { - let contractSha1 = contractTableReference.tableId; - let contractsMap: Record = {}; - if (!contractSha1) { - if (!contractTableReference.contracts) { - throw new Error( - "contracts have not been specified. cannot complete query", - ); - } - const contracts = contractTableReference.contracts; - // If the sha1 isn't set we will upload our own table; - contractSha1 = await this.uploadContractTable(contracts); - contractsMap = contracts.reduce>((a, c) => { - a[c.id] = c.address; - return a; - }, {}); - } else { - // Load the contracts table - contractsMap = await this.loadContractsTable(contractSha1); - } - - const parameters = [ - QueryParameter.text( - "start_time", - range.startDate.toFormat("yyyy-MM-dd 00:00:00") + " UTC", - ), - QueryParameter.text( - "end_time", - range.endDate.toFormat("yyyy-MM-dd 00:00:00") + " UTC", - ), - QueryParameter.text("contracts_table", contractSha1), - ]; - - logger.debug("loading from dune"); - const response = await this.client.refresh( - this.options.queryId, - parameters, - ); - //const response = JSON.parse(await readFile('tester-123456.json', { encoding: 'utf-8' })) as Awaited>; - //await writeFile('tester-123456.json', JSON.stringify(response)); - const rawRows = response.result?.rows as unknown[] as DuneRawRow[]; - - // Split raw rows by date - const rowsByDay = rawRows.reduce>( - (acc, curr) => { - // Update the date value to be ISO - const rowsForDate = acc[curr.date] || []; - rowsForDate.push(curr); - acc[curr.date] = rowsForDate; - return acc; - }, - {}, - ); - - const sortedDates = Object.keys(rowsByDay).sort(); - const rows: DailyContractUsageRow[] = []; - for (const date of sortedDates) { - const rawRows = rowsByDay[date]; - rows.push(...this.expandRowsForDate(contractsMap, rawRows)); - } - - // Aggregate safes. Not sure why but sometimes the data isn't being - // aggregated on the Dune side. - return rows; - } - - expandRowsForDate( - contractsMap: Record, - rawRows: DuneRawRow[], - ): DailyContractUsageRow[] { - const usageRows: DailyContractUsageRow[] = []; - - const safes: Record = {}; - for (const rawRow of rawRows) { - let expandedRows: DailyContractUsageRow[] = []; - try { - expandedRows = transformDuneRawRowToUsageRows(rawRow, contractsMap); - } catch (err) { - logger.error("Error collecting dune rows", err); - throw err; - } - for (const usageRow of expandedRows) { - if (usageRow.safeAddress) { - const address = usageRow.safeAddress.toLowerCase(); - if (safes[address]) { - safes[address].add(usageRow); - } else { - safes[address] = new SafeAggregate(usageRow); - } - } else { - usageRows.push(usageRow); - } - } - } - for (const address in safes) { - const safe = safes[address]; - usageRows.push(safe.aggregate()); - } - return usageRows; - } - - // At this time the `_contractSha1` is not needed because the available csvs already resolve the contract addresses - async getDailyContractUsageFromCsv( - date: DateTime, - ): Promise { - return this.loadCsvPath( - `${this.options.csvDirPath}/${date.toISODate()}.csv`, - ); - } - - async loadContractsTable(sha: string): Promise> { - const contracts = await loadContractsTable( - path.join(this.options.tablesDirectoryPath, `contracts-${sha}.csv`), - ); - - return contracts.reduce>((acc, curr) => { - const id = parseInt(curr.id); - acc[id] = curr.address; - return acc; - }, {}); - } - - async loadCsvPath(path: string) { - return new Promise((resolve, reject) => { - setImmediate(() => { - const rows: DailyContractUsageRow[] = []; - const stream = fs.createReadStream(path); - return stream - .pipe(parse({ delimiter: ",", fromLine: 1 })) - .on("data", (chunk) => { - const row: DailyContractUsageRow = { - date: chunk[0], - contractAddress: chunk[1], - userAddress: chunk[2] === "" ? null : chunk[2], - safeAddress: chunk[3] === "" ? null : chunk[3], - l2GasUsed: chunk[4], - l1GasUsed: chunk[5], - txCount: stringToInt(chunk[6]), - }; - rows.push(row); - }) - .on("error", (err) => { - return reject(err); - }) - .on("finish", () => { - resolve(rows); - }); - }); - }); - } -} diff --git a/indexer/src/collectors/dune/funding-events/client.ts b/indexer/src/collectors/dune/funding-events/client.ts deleted file mode 100644 index 2b2c47c88..000000000 --- a/indexer/src/collectors/dune/funding-events/client.ts +++ /dev/null @@ -1,148 +0,0 @@ -/** - * A client for our daily contract usage query on Dune - * - * The client will resolve everything in the request to raw addresses as we - * currently use a method for querying dune that involves passing in - * known-user-addresses + ids and known-contract-addresses + ids. This reduces - * the burden of the api calls on our quota by about 20% in testing. - */ -import { DateTime } from "luxon"; -import { QueryParameter } from "@cowprotocol/ts-dune-client"; - -import { IDuneClient } from "../../../utils/dune/type.js"; -import { Cacheable } from "../../../cacher/time-series.js"; -import { logger } from "../../../utils/logger.js"; - -export type FundingEventRawRow = { - block_time: string; - from: string; - to: string; - blockchain: string; - token: string; - value: string; - tx_hash: string; - - // Catch all for other raw values returned in the result - [key: string]: any; -}; - -export type FundingEventRow = { - blockTime: string; - from: string; - to: string; - blockchain: string; - token: string; - value: string; - txHash: string; - - // Catch all for other raw values returned in the result - [key: string]: any; -}; - -export type FundingPoolAddress = { - id: number; - groupId: number; - address: string; -}; - -export type ProjectAddress = { - id: number; - projectId: number; - address: string; -}; - -export interface IFundingEventsClient { - getFundingEvents( - start: DateTime, - end: DateTime, - fundingPoolAddresses: FundingPoolAddress[], - projectAddresses: ProjectAddress[], - ): Promise>; -} - -export interface FundingEventsClientOptions { - queryId: number; -} - -export const DefaultFundingEventsClientOptions: FundingEventsClientOptions = { - // This default is based on this: https://dune.com/queries/3020253 - queryId: 3020253, -}; - -export class FundingEventsClient implements IFundingEventsClient { - private client: IDuneClient; - private options: FundingEventsClientOptions; - - constructor( - client: IDuneClient, - options: FundingEventsClientOptions = DefaultFundingEventsClientOptions, - ) { - this.client = client; - this.options = options; - } - - /** - * Refreshes the dune [query](https://dune.com/queries/3020253) - * - * @param start - * @param end - * @param fundingPoolAddresses - * @param projectAddresses - */ - async getFundingEvents( - start: DateTime, - end: DateTime, - fundingPoolAddresses: FundingPoolAddress[], - projectAddresses: ProjectAddress[], - ): Promise> { - logger.debug("Calling dune for funding events"); - const projectAddressesInput = projectAddresses.map((a) => { - return `(${a.id}, ${a.projectId}, ${a.address})`; - }); - const fundingPoolAddressesInput = fundingPoolAddresses.map((a) => { - return `(${a.id}, ${a.groupId}, ${a.address})`; - }); - - const parameters = [ - QueryParameter.text("project_addresses", projectAddressesInput.join(",")), - QueryParameter.text( - "funding_pool_addresses", - fundingPoolAddressesInput.join(","), - ), - QueryParameter.text( - "start_time", - start.toFormat("yyyy-MM-dd 00:00:00") + " UTC", - ), - QueryParameter.text( - "end_time", - end.toFormat("yyyy-MM-dd 00:00:00") + " UTC", - ), - ]; - - const response = await this.client.refresh( - this.options.queryId, - parameters, - ); - const rawRows = response.result?.rows || []; - const rows: FundingEventRow[] = rawRows.map((r) => { - const raw = r as FundingEventRawRow; - return { - blockTime: DateTime.fromSQL(raw.block_time).toISO() || "", - from: raw.from, - to: raw.to, - blockchain: raw.blockchain, - token: raw.token, - value: raw.value, - txHash: raw.tx_hash, - }; - }); - return { - raw: rows, - cacheRange: { - startDate: start, - endDate: end, - }, - hasNextPage: false, - }; - } -} diff --git a/indexer/src/collectors/dune/utils/csv-uploader.ts b/indexer/src/collectors/dune/utils/csv-uploader.ts deleted file mode 100644 index a38b9024f..000000000 --- a/indexer/src/collectors/dune/utils/csv-uploader.ts +++ /dev/null @@ -1,22 +0,0 @@ -export class DuneCSVUploader { - private apiKey: string; - - constructor(apiKey: string) { - this.apiKey = apiKey; - } - - async upload(tableName: string, description: string, rows: string[]) { - return await fetch("https://api.dune.com/api/v1/table/upload/csv", { - method: "POST", - body: JSON.stringify({ - table_name: tableName, - description: description, - data: rows.join("\n"), - }), - headers: { - "Content-Type": "application/json", - "X-Dune-Api-Key": this.apiKey, - }, - }); - } -} diff --git a/indexer/src/collectors/github-commits.ts b/indexer/src/collectors/github-commits.ts deleted file mode 100644 index 11d300f7d..000000000 --- a/indexer/src/collectors/github-commits.ts +++ /dev/null @@ -1,596 +0,0 @@ -import { DateTime } from "luxon"; -import _ from "lodash"; -import { - IEventRecorderClient, - IncompleteArtifact, - IncompleteEvent, - RecordHandle, -} from "../recorder/types.js"; -import { Range, rangeToString } from "../utils/ranges.js"; -import { gql } from "graphql-request"; -import { - Artifact, - ArtifactNamespace, - ArtifactType, - Project, -} from "../db/orm-entities.js"; -import { logger } from "../utils/logger.js"; -import { Octokit, RequestError } from "octokit"; -import { GetResponseDataTypeFromEndpointMethod } from "@octokit/types"; -import { Repository } from "typeorm"; -import { - IArtifactGroup, - IArtifactGroupCommitmentProducer, -} from "../scheduler/types.js"; -import { - TimeSeriesCacheLookup, - TimeSeriesCacheWrapper, -} from "../cacher/time-series.js"; -import { asyncBatch } from "../utils/array.js"; -import { - GithubBaseCollectorOptions, - GithubBatchedProjectArtifactsBaseCollector, - GithubGraphQLResponse, -} from "./github/common.js"; -import { Batch } from "../scheduler/common.js"; - -const validateEmail = (email: string) => { - return String(email) - .toLowerCase() - .match( - /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, - ); -}; - -const GET_COMMITS_FOR_MANY_REPOSITORIES = gql` - query GetCommitsForManyRepositories( - $searchStr: String! - $first: Int - $since: GitTimestamp - $until: GitTimestamp - $cursor: String - ) { - rateLimit { - resetAt - remaining - nodeCount - cost - } - search(query: $searchStr, type: REPOSITORY, first: $first, after: $cursor) { - pageInfo { - hasNextPage - endCursor - } - nodes { - ... on Repository { - nameWithOwner - isFork - defaultBranchRef { - name - target { - ... on Commit { - history(first: $first, since: $since, until: $until) { - totalCount - nodes { - ... on Commit { - committedDate - authoredDate - oid - committer { - user { - login - } - name - email - } - author { - user { - login - } - name - email - } - } - } - } - } - } - } - } - } - } - } -`; - -type RespositorySummariesResponse = GithubGraphQLResponse; - -type RepositorySummaries = { - search: { - pageInfo: { - hasNextPage: boolean; - endCursor: string; - }; - nodes: RepositorySummary[]; - }; -}; - -type RepositorySummary = { - nameWithOwner: string; - isFork: boolean; - defaultBranchRef: { - name: string; - target: { - history: { - totalCount: number; - nodes: SummaryCommitInfo[]; - }; - }; - } | null; -}; - -type CommitUser = { - login: string; -}; - -type GenericCommit = { - committer: { - login: string; - name?: string | null | undefined; - email?: string | null | undefined; - } | null; - author: { - login: string; - name?: string | null | undefined; - email?: string | null | undefined; - } | null; -}; - -type SummaryCommitInfo = { - committedDate: string | undefined; - authoredDate: string | undefined; - oid: string; - committer: { - user: CommitUser | null; - name: string; - email: string; - }; - author: { - user: CommitUser | null; - name: string; - email: string; - }; -}; - -type Commit = GetResponseDataTypeFromEndpointMethod< - Octokit["rest"]["repos"]["getCommit"] ->; - -interface EmptyRepository { - isEmptyRepository: boolean; -} - -type CommitsResponse = Commit[] | EmptyRepository; - -const DefaultGithubCommitCollectorOptions: GithubBaseCollectorOptions = { - cacheOptions: { - bucket: "github-commits", - }, -}; - -type RepoSummaryProcessedResult = { - repo: Artifact; - count: number; - events: IncompleteEvent[]; -}; -type RepoSummaryProcessedResults = { - unchanged: Artifact[]; - changed: RepoSummaryProcessedResult[]; -}; - -export class GithubCommitCollector extends GithubBatchedProjectArtifactsBaseCollector { - private gh: Octokit; - - constructor( - projectRepository: Repository, - gh: Octokit, - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - batchSize: number, - options?: Partial, - ) { - const opts = _.merge(DefaultGithubCommitCollectorOptions, options); - super(projectRepository, recorder, cache, batchSize, opts); - this.gh = gh; - } - - async gatherSummary( - missingRepos: Artifact[], - range: Range, - ): Promise { - const results: RepoSummaryProcessedResults = { - changed: [], - unchanged: [], - }; - - const repoMap = missingRepos.reduce>( - (acc, curr) => { - acc[curr.name.toLowerCase()] = curr; - return acc; - }, - {}, - ); - - const repoSearchStr = Object.keys(repoMap).map((n) => `repo:${n}`); - - let resultCount = 0; - - const searchStr = `${repoSearchStr.join(" ")} fork:true sort:updated-desc`; - - // Do the summary search - const pages = this.cache.loadCachedOrRetrieve( - TimeSeriesCacheLookup.new( - `${this.options.cacheOptions.bucket}/summaries`, - repoSearchStr, - range, - ), - async (missing, lastPage) => { - const response = (await this.rateLimitedGraphQLRequest( - GET_COMMITS_FOR_MANY_REPOSITORIES, - { - searchStr: searchStr, - since: missing.range.startDate.toUTC().toISO(), - until: missing.range.endDate.toUTC().toISO(), - first: 100, - cursor: - lastPage?.cursor !== undefined ? lastPage.cursor : undefined, - }, - )) as RespositorySummariesResponse; - const hasNextPage = response.search.pageInfo.hasNextPage; - const cursor = response.search.pageInfo.endCursor; - return { - raw: response, - cacheRange: missing.range, - cursor: cursor, - hasNextPage: hasNextPage, - }; - }, - ); - - for await (const page of pages) { - const response = page.raw; - for (const summary of response.search.nodes) { - const repoName = summary.nameWithOwner.toLowerCase(); - const repo = repoMap[repoName]; - if (!repo) { - // skip things are aren't monitoring (at the moment) - continue; - } - delete repoMap[repoName]; - if (!summary.defaultBranchRef) { - // This means the repository is empty (at least that's how it will be interpreted) - summary.defaultBranchRef = { - name: "n/a", - target: { - history: { - totalCount: 0, - nodes: [], - }, - }, - }; - } - const totalCount = summary.defaultBranchRef!.target.history.totalCount; - resultCount += totalCount; - // Ignore things that are forks - if (totalCount === 0 || summary.isFork) { - results.unchanged.push(repo); - } else { - // Create store the events for later - results.changed.push({ - repo: repo, - count: totalCount, - events: this.createEventsFromSummaryResults(repo, summary), - }); - } - } - } - logger.debug(`Results in github commits summary ${resultCount}`); - const repoMapLen = Object.keys(repoMap).length; - if (repoMapLen !== 0) { - logger.debug( - `${repoMapLen} repos were missed in the summary, force retrieval`, - ); - for (const repoName in repoMap) { - results.changed.push({ - repo: repoMap[repoName], - count: 1, - events: [], - }); - } - } - return results; - } - - private createEventsFromSummaryResults( - repo: Artifact, - summary: RepositorySummary, - ): IncompleteEvent[] { - return summary - .defaultBranchRef!.target.history.nodes.map((e) => { - const contributor = this.contributorFromCommit({ - committer: - e.committer.user === null - ? null - : { - email: e.committer.email, - name: e.committer.name, - login: e.committer.user.login || "", - }, - author: { - email: e.author.email, - name: e.author.name, - login: e.author.user?.login || "", - }, - }); - - // Committed date is the date it was pushed to the repo. As opposed to - // the day it was written. This is likely more important however we'll - // make some decisions on this later. Pragmatically this is good enough - // and is more consistent with the `updatedAt` filtering that this - // collector utilizes. - let time = DateTime.fromISO(e.committedDate || ""); - if (!time.isValid) { - time = DateTime.fromISO(e.authoredDate || ""); - } - if (!time.isValid) { - logger.debug("invalid date for commit. skipping"); - return undefined; - } - return { - time: time, - to: repo, - from: contributor, - sourceId: e.oid, - type: { - name: "COMMIT_CODE", - version: 1, - }, - amount: 1, - }; - }) - .filter((s) => s !== undefined) as IncompleteEvent[]; - } - - async collect( - group: IArtifactGroup, - range: Range, - committer: IArtifactGroupCommitmentProducer, - ) { - const artifacts = await group.artifacts(); - - // Check if the preprocessing has everything cached - const summaryResults = await this.gatherSummary(artifacts, range); - - // Commit the unchanged artifacts - for (const repo of summaryResults.unchanged) { - committer.commit(repo).withNoChanges(); - } - - // Load commits for each artifact - await asyncBatch(summaryResults.changed, 10, async (batch) => { - for (const summary of batch) { - const artifact = summary.repo; - - try { - const handles = await asyncBatch(summary.events, 1, async (e) => { - return this.recorder.record(e[0]); - }); - if (summary.count !== summary.events.length) { - const existingMap = summary.events.reduce>( - (a, c) => { - a[c.sourceId] = true; - return a; - }, - {}, - ); - const newHandles = await this.recordEventsForRepo( - artifact, - range, - existingMap, - ); - newHandles.forEach((h) => handles.push(h)); - } - committer.commit(artifact).withHandles(handles); - } catch (err) { - committer.commit(artifact).withResults({ - success: [], - errors: [err], - }); - } - } - }); - } - - private async recordEventsForRepo( - repoArtifact: Artifact, - range: Range, - skipExistingMap?: Record, - ) { - skipExistingMap = skipExistingMap || {}; - logger.debug( - `Recording commits for ${repoArtifact.name} in ${rangeToString(range)}`, - ); - const locator = this.splitGithubRepoIntoLocator(repoArtifact); - const responses = this.cache.loadCachedOrRetrieve( - TimeSeriesCacheLookup.new( - `${this.options.cacheOptions.bucket}/${locator.owner}/${locator.repo}`, - [`${repoArtifact.namespace}:${repoArtifact.name}`], - range, - ), - async (missing, lastPage) => { - const currentPage = (lastPage?.cursor || 1) as number; - try { - logger.debug("loading page from github"); - const commits = await this.gh.rest.repos.listCommits({ - ...locator, - ...{ - since: range.startDate - .toUTC() - .startOf("second") - .toISO({ suppressMilliseconds: true })!, - until: range.endDate - .toUTC() - .startOf("second") - .toISO({ suppressMilliseconds: true })!, - per_page: 500, - page: lastPage?.cursor || 1, - }, - }); - let hasNextPage = false; - if (commits.headers.link) { - if (commits.headers.link.indexOf('rel="next"') !== -1) { - logger.debug(`found next page after ${currentPage}`); - hasNextPage = true; - } - } - - return { - raw: commits.data as Commit[], - cacheRange: missing.range, - hasNextPage: hasNextPage, - cursor: currentPage + 1, - }; - } catch (err) { - const reqErr = err as RequestError; - - if (reqErr.status) { - if (reqErr.status === 404) { - logger.debug("repo not found"); - return { - raw: { isEmptyRepository: true }, - cacheRange: missing.range, - hasNextPage: false, - cursor: 1, - }; - } - const reqData: { message?: string } = reqErr.response?.data || { - message: "", - }; - const reqDataMsg = reqData.message || ""; - if (reqDataMsg.indexOf("Git Repository is empty") !== -1) { - logger.debug("found empty repo"); - return { - raw: { isEmptyRepository: true }, - cacheRange: missing.range, - hasNextPage: false, - cursor: 1, - }; - } - } - throw err; - } - }, - ); - - const recordHandles: RecordHandle[] = []; - - for await (const page of responses) { - const commits: Commit[] = (page.raw as EmptyRepository).isEmptyRepository - ? [] - : (page.raw as Commit[]); - for (const commit of commits) { - if (skipExistingMap[commit.sha]) { - logger.debug(`skipping commit ${commit.sha}. already recorded`); - continue; - } - const rawCommitTime = - commit.commit.author?.date || commit.commit.committer?.date; - if (!rawCommitTime) { - logger.warn( - `encountered a commit without a date. skipping for now. repo=${locator.owner}/${locator.repo}@${commit.sha}`, - { - owner: locator.owner, - repo: locator.repo, - sha: commit.sha, - }, - ); - continue; - } - const commitTime = DateTime.fromISO(rawCommitTime); - const event: IncompleteEvent = { - time: commitTime, - type: { - name: "COMMIT_CODE", - version: 1, - }, - to: repoArtifact, - amount: 1, - sourceId: commit.sha, - }; - - const contributor = this.contributorFromCommit(commit); - if (!contributor) { - logger.warn( - `encountered a commit without a login, email, or a name. recording commit without a contributor. repo=${locator.owner}/${locator.repo}@${commit.sha}`, - { - owner: locator.owner, - repo: locator.repo, - sha: commit.sha, - }, - ); - } else { - event.from = contributor; - } - - recordHandles.push(await this.recorder.record(event)); - } - } - - // Wait for all of the events for this repo to be recorded - return recordHandles; - } - - private contributorFromCommit( - commit: GenericCommit, - ): IncompleteArtifact | undefined { - const contributor: IncompleteArtifact = { - name: "", - namespace: ArtifactNamespace.GITHUB, - type: ArtifactType.GITHUB_USER, - }; - if (commit.author) { - contributor.name = commit.author.login; - } else if (commit.committer) { - contributor.name = commit.committer.login; - } - - if (!contributor.name) { - // We will need to resort to use the email of the user if we cannot find a - // name - contributor.type = ArtifactType.GIT_EMAIL; - if (commit.author?.email) { - contributor.name = commit.author.email; - } else if (commit.committer?.email) { - contributor.name = commit.committer.email; - } - if (!validateEmail(contributor.name || "")) { - contributor.name = `unverified:git:data:email:${contributor.name}`; - } - if (!contributor.name) { - contributor.type = ArtifactType.GIT_NAME; - // If there's still nothing we will attempt to use a name - if (commit.author?.name) { - contributor.name = `unverified:git:data:name:${commit.author.name}`; - } else if (commit.committer?.name) { - contributor.name = `unverified:git:data:name:${commit.committer.name}`; - } - if (!contributor.name) { - return undefined; - } - } - } - - // Lowercase the name being stored - contributor.name = contributor.name.toLowerCase(); - return contributor; - } -} diff --git a/indexer/src/collectors/github-followers.ts b/indexer/src/collectors/github-followers.ts deleted file mode 100644 index 973dc2033..000000000 --- a/indexer/src/collectors/github-followers.ts +++ /dev/null @@ -1,682 +0,0 @@ -import { DateTime } from "luxon"; -import { - IEventGroupRecorder, - IEventRecorderClient, - IncompleteArtifact, - IncompleteEvent, - RecordHandle, -} from "../recorder/types.js"; -import { logger } from "../utils/logger.js"; -import { gql, ClientError } from "graphql-request"; -import { - GithubGraphQLResponse, - Actor, - PaginatableEdges, - GraphQLNode, - GithubBaseCollectorOptions, - GithubRepoLocator, - GithubBatchedProjectArtifactsBaseCollector, -} from "./github/common.js"; -import { unpaginateIterator } from "./github/unpaginate.js"; -import { - Project, - ArtifactNamespace, - ArtifactType, - Artifact, -} from "../db/orm-entities.js"; -import { Repository } from "typeorm"; -import { TimeSeriesCacheWrapper } from "../cacher/time-series.js"; -import _ from "lodash"; -import { - IArtifactGroup, - IArtifactGroupCommitmentProducer, -} from "../scheduler/types.js"; -import { Range, doRangesIntersect, rangeFromISO } from "../utils/ranges.js"; -import { sha1FromArray } from "../utils/source-ids.js"; -import { GraphQLError } from "graphql"; -import { Batch } from "../scheduler/common.js"; -import { VariableType } from "json-to-graphql-query"; -import { - MultiplexGithubGraphQLRequester, - MultiplexObjectRetreiver, -} from "./github/multiplex-graphql.js"; -import { ArtifactGroupRecorder } from "../recorder/group.js"; -import { EnumType } from "json-to-graphql-query"; - -const GET_ALL_PUBLIC_FORKS = gql` - query getAllPublicForks($owner: String!, $name: String!, $cursor: String) { - repository(owner: $owner, name: $name) { - forks( - first: 100 - privacy: PUBLIC - orderBy: { field: CREATED_AT, direction: DESC } - after: $cursor - ) { - totalCount - pageInfo { - hasNextPage - endCursor - } - edges { - node { - id - url - createdAt - owner { - __typename - login - } - } - } - } - } - rateLimit { - limit - cost - remaining - resetAt - } - } -`; - -const REPOSITORY_FOLLOWING_SUMMARY = gql` - query repositoryFollowingSummary( - $owner: String! - $name: String! - $cursor: String - ) { - repository(owner: $owner, name: $name) { - createdAt - forkCount - forks( - first: 100 - privacy: PUBLIC - orderBy: { field: CREATED_AT, direction: DESC } - ) { - totalCount - pageInfo { - hasNextPage - endCursor - } - edges { - node { - id - url - createdAt - owner { - __typename - login - } - } - } - } - watchers(first: 1) { - totalCount - } - stargazers( - first: 100 - after: $cursor - orderBy: { field: STARRED_AT, direction: DESC } - ) { - pageInfo { - hasNextPage - endCursor - } - totalCount - edges { - node { - login - } - starredAt - } - } - } - rateLimit { - limit - cost - remaining - resetAt - } - } -`; - -const repositorySummaryRetriever: MultiplexObjectRetreiver< - GithubRepoLocator -> = (vars) => { - return { - type: "repository", - definition: { - __args: { - owner: vars.owner, - name: vars.repo, - }, - ...repositoryFollowersAsJson(), - }, - }; -}; - -function repositoryFollowersAsJson(includeCursor?: boolean) { - const optional = includeCursor - ? { - cursor: new VariableType("cursor"), - } - : {}; - - return { - createdAt: true, - forkCount: true, - forks: { - __args: { - first: 100, - privacy: new EnumType("PUBLIC"), - orderBy: { - field: new EnumType("CREATED_AT"), - direction: new EnumType("DESC"), - }, - }, - totalCount: true, - pageInfo: { - hasNextPage: true, - endCursor: true, - }, - edges: { - node: { - id: true, - url: true, - createdAt: true, - owner: { - __typename: true, - login: true, - }, - }, - }, - }, - watchers: { - totalCount: true, - }, - stargazers: { - __args: _.merge( - { - first: 100, - orderBy: { - field: new EnumType("STARRED_AT"), - direction: new EnumType("DESC"), - }, - }, - optional, - ), - pageInfo: { - hasNextPage: true, - endCursor: true, - }, - totalCount: true, - edges: { - node: { - login: true, - }, - starredAt: true, - }, - }, - }; -} - -type StarringWrapper = { - starring: Starring; - summary: RepoFollowingSummarySingleQueryResponse; -}; - -type Starring = { - node: Actor; - starredAt: string; -}; - -type Fork = { - id: string; - url: string; - createdAt: string; - owner: Actor & { __typename: string }; -}; - -type RepoFollowingSummaryResponse = { - createdAt: string; - - // This gives us total forks (the assumption is that this is different than - // forks.totalCount because of private forking) - forkCount: number; - - forks: PaginatableEdges> & { - totalCount: number; - }; - - watchers: { - totalCount: number; - }; - - stargazers: PaginatableEdges & { - totalCount: number; - }; -}; - -type RepoFollowingSummarySingleQueryResponse = GithubGraphQLResponse<{ - repository: RepoFollowingSummaryResponse; -}>; - -type RepoFollowingSummariesResponse = GithubGraphQLResponse<{ - [key: string]: RepoFollowingSummaryResponse; -}>; - -type GetAllPublicForks = GithubGraphQLResponse<{ - repository: { - createdAt: string; - - // This gives us total forks (the assumption is that this is different than - // forks.totalCount because of private forking) - forkCount: number; - - forks: PaginatableEdges> & { - totalCount: number; - }; - }; -}>; - -const DefaultGithubFollowingCollectorOptions: GithubBaseCollectorOptions = { - cacheOptions: { - bucket: "github-followers", - }, -}; - -export class GithubFollowingCollector extends GithubBatchedProjectArtifactsBaseCollector { - constructor( - projectRepository: Repository, - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - batchSize: number, - options?: Partial, - ) { - super( - projectRepository, - recorder, - cache, - batchSize, - _.merge(DefaultGithubFollowingCollectorOptions, options), - ); - } - - async collect( - group: IArtifactGroup, - range: Range, - committer: IArtifactGroupCommitmentProducer, - ): Promise { - const batch = await group.meta(); - const artifacts = await group.artifacts(); - logger.debug(`collecting followers for repos of ${batch.name}`); - - const groupRecorder = new ArtifactGroupRecorder(this.recorder); - // load the summaries for each - //logger.debug(`loading events for ${repo.name}`); - try { - await this.collectEventsForRepo(groupRecorder, artifacts, range); - committer.commitGroup(groupRecorder); - } catch (err) { - committer.failAll(err); - } - logger.debug(`follower collection complete`); - } - - private async collectEventsForRepo( - groupRecorder: IEventGroupRecorder, - repos: Artifact[], - range: Range, - ) { - const locators = repos.map((r) => this.splitGithubRepoIntoLocator(r)); - logger.debug("loading summary of many repos"); - const summaries = await this.loadSummaryForRepos(locators); - - for (let i = 0; i < summaries.length; i++) { - const repo = repos[i]; - const summary = summaries[i]; - const locator = locators[i]; - if (!summary) { - logger.debug( - `skipping artifact[name=${repo.name}, namespace=${repo.namespace}] - no summary found`, - ); - continue; - } - const ranges = this.rangeOfRepoFollowingSummaryResponse(summary); - - if ( - ranges.forksRange && - doRangesIntersect(ranges.forksRange, range, true) - ) { - // Gather fork events within this range - await this.recordForkEvents( - groupRecorder, - repo, - locator, - summary.forkCount, - range, - ); - } - if ( - ranges.stargazersRange && - doRangesIntersect(ranges.stargazersRange, range, true) - ) { - // Gather starring events within this range - await this.recordStarHistoryForRepo( - groupRecorder, - repo, - locator, - range, - ); - } - } - } - - private async loadSummaryForRepos( - locators: GithubRepoLocator[], - ): Promise<(RepoFollowingSummaryResponse | null)[]> { - const multiplex = new MultiplexGithubGraphQLRequester< - GithubRepoLocator, - RepoFollowingSummaryResponse - >( - "getRepoFollowingSummaries", - { - owner: "String!", - repo: "String!", - }, - repositorySummaryRetriever, - ); - try { - const response = await this.rateLimitedGraphQLGeneratedRequest< - GithubRepoLocator, - RepoFollowingSummaryResponse, - RepoFollowingSummariesResponse - >(multiplex, locators); - return response.items; - } catch (err) { - if (err instanceof ClientError) { - const errors = err.response.errors || []; - const notFoundError = errors.filter((e) => { - return ( - (e as GraphQLError & { [key: string]: string }).type === "NOT_FOUND" - ); - }); - - // This repo doesn't exist. Just skip. - if (notFoundError.length > 0) { - throw new Error("something is broken"); - } - } - throw err; - } - } - - private async recordStarHistoryForRepo( - groupRecorder: IEventGroupRecorder, - artifact: Artifact, - locator: GithubRepoLocator, - range: Range, - ) { - let aggregateStatsRecorded = false; - - for await (const { summary, starring } of this.loadStarHistoryForRepo( - locator, - range, - )) { - if (!aggregateStatsRecorded) { - // Hack to make this work. we need to change how this works - await this.recordStarAggregateStats(groupRecorder, locator, summary); - logger.debug("record watchers"); - - await this.recordWatcherEvents(groupRecorder, locator, summary); - aggregateStatsRecorded = true; - } - const commitTime = DateTime.fromISO(starring.starredAt); - - const contributor = - starring.node && starring.node.login !== "" - ? { - name: starring.node.login.toLowerCase(), - namespace: ArtifactNamespace.GITHUB, - type: ArtifactType.GITHUB_USER, - } - : undefined; - - const event: IncompleteEvent = { - time: commitTime, - type: { - name: "STARRED", - version: 1, - }, - to: artifact, - from: contributor, - amount: 1, - sourceId: sha1FromArray([ - "STARRED", - commitTime.toISO()!, - locator.owner, - locator.repo, - contributor?.name || "", - ]), - }; - - await groupRecorder.record(event); - } - } - - private async *loadStarHistoryForRepo( - locator: GithubRepoLocator, - range: Range, - ): AsyncGenerator { - const iterator = - unpaginateIterator()( - REPOSITORY_FOLLOWING_SUMMARY, - "repository.stargazers.edges", - "repository.stargazers.pageInfo", - { - owner: locator.owner, - name: locator.repo, - }, - ); - for await (const data of iterator) { - const response = data.raw as RepoFollowingSummarySingleQueryResponse; - - for (const starring of data.results) { - const commitTime = DateTime.fromISO(starring.starredAt); - - if (commitTime.toUnixInteger() < range.startDate.toUnixInteger()) { - // Once we've reached a commitTime _before_ the range we're searching - // for, we can stop collecting stars for this artifact. - return; - } - yield { - starring: starring, - summary: response, - }; - } - } - } - - private rangeOfRepoFollowingSummaryResponse( - res: RepoFollowingSummaryResponse, - ) { - const stargazersCount = res.stargazers.edges.length; - const forksCount = res.forks.edges.length; - if (stargazersCount === 0 && forksCount === 0) { - return { - stargazersRange: undefined, - forksRange: undefined, - }; - } - let stargazersRange: Range | undefined = undefined; - let forksRange: Range | undefined = undefined; - if (stargazersCount > 0) { - const first = res.stargazers.edges[0]; - const last = res.stargazers.edges.slice(-1)[0]; - - stargazersRange = rangeFromISO(last.starredAt, first.starredAt); - } - if (forksCount > 0) { - const first = res.forks.edges[0]; - const last = res.forks.edges.slice(-1)[0]; - forksRange = rangeFromISO(last.node.createdAt, first.node.createdAt); - } - return { - stargazersRange, - forksRange, - }; - } - - private async *loadAllForksHistory( - repo: GithubRepoLocator, - range: Range, - ): AsyncGenerator { - const iterator = unpaginateIterator()( - GET_ALL_PUBLIC_FORKS, - "repository.forks.edges", - "repository.forks.pageInfo", - { - owner: repo.owner, - name: repo.repo, - }, - ); - for await (const data of iterator) { - for (const fork of data.results) { - const createdAt = DateTime.fromISO(fork.node.createdAt); - - if (createdAt.toUnixInteger() < range.startDate.toUnixInteger()) { - return; - } - yield fork.node; - } - } - } - - private recordStarAggregateStats( - groupRecorder: IEventGroupRecorder, - repo: GithubRepoLocator, - response: RepoFollowingSummarySingleQueryResponse, - ) { - const artifact: IncompleteArtifact = { - name: `${repo.owner}/${repo.repo}`, - type: ArtifactType.GIT_REPOSITORY, - namespace: ArtifactNamespace.GITHUB, - }; - const startOfDay = DateTime.now().startOf("day"); - const starCount = response.repository.stargazers.totalCount; - - return groupRecorder.record({ - time: startOfDay, - type: { - name: "STAR_AGGREGATE_STATS", - version: 1, - }, - to: artifact, - amount: starCount, - sourceId: sha1FromArray([ - "STARS", - startOfDay.toISO()!, - repo.owner, - repo.repo, - ]), - }); - } - - private async recordWatcherEvents( - groupRecorder: IEventGroupRecorder, - repo: GithubRepoLocator, - response: RepoFollowingSummarySingleQueryResponse, - ) { - const artifact: IncompleteArtifact = { - name: `${repo.owner}/${repo.repo}`, - type: ArtifactType.GIT_REPOSITORY, - namespace: ArtifactNamespace.GITHUB, - }; - const startOfDay = DateTime.now().toUTC().startOf("day"); - const watchersCount = response.repository.watchers.totalCount; - - logger.debug("recording watcher stats for today"); - - // Get the aggregate stats for forking - await groupRecorder.record({ - time: startOfDay, - type: { - name: "WATCHER_AGGREGATE_STATS", - version: 1, - }, - to: artifact, - amount: watchersCount, - sourceId: sha1FromArray([ - "WATCHERS", - startOfDay.toISO()!, - repo.owner, - repo.repo, - ]), - }); - } - - private async recordForkEvents( - groupRecorder: IEventGroupRecorder, - artifact: Artifact, - repo: GithubRepoLocator, - forkCount: number, - range: Range, - ) { - const startOfDay = DateTime.now().toUTC().startOf("day"); - - const recordHandles: RecordHandle[] = []; - - // Get the aggregate stats for forking - recordHandles.push( - await this.recorder.record({ - time: startOfDay, - type: { - name: "FORK_AGGREGATE_STATS", - version: 1, - }, - to: artifact, - amount: forkCount, - sourceId: sha1FromArray([ - "FORKS", - startOfDay.toISO()!, - repo.owner, - repo.repo, - ]), - }), - ); - - const recordForkedEvent = (f: Fork) => { - const createdAt = DateTime.fromISO(f.createdAt); - const contributor = { - name: f.owner.login, - namespace: ArtifactNamespace.GITHUB, - type: - f.owner.__typename == "Organization" - ? ArtifactType.GITHUB_ORG - : ArtifactType.GITHUB_USER, - }; - return this.recorder.record({ - time: createdAt, - type: { - name: "FORKED", - version: 1, - }, - to: artifact, - from: contributor, - amount: 1, - sourceId: f.id, - }); - }; - - // If we have more forks than 100 we need to make some additional queries to gather information - logger.debug("loading fork history"); - for await (const fork of this.loadAllForksHistory(repo, range)) { - if (DateTime.fromISO(fork.createdAt) < range.startDate) { - break; - } - recordHandles.push(await recordForkedEvent(fork)); - } - - return recordHandles; - } -} diff --git a/indexer/src/collectors/github-issues.ts b/indexer/src/collectors/github-issues.ts deleted file mode 100644 index 3f3351c84..000000000 --- a/indexer/src/collectors/github-issues.ts +++ /dev/null @@ -1,806 +0,0 @@ -import { DateTime } from "luxon"; -import { - IEventGroupRecorder, - IEventRecorderClient, - IncompleteArtifact, - IncompleteEvent, -} from "../recorder/types.js"; -import { - Artifact, - ArtifactNamespace, - ArtifactType, - Project, -} from "../db/orm-entities.js"; -import { logger } from "../utils/logger.js"; -import _ from "lodash"; -import { unpaginateIterator } from "./github/unpaginate.js"; -import { gql } from "graphql-request"; -import { - GithubGraphQLResponse, - GraphQLNode, - Actor, - GithubBaseCollectorOptions, - GithubGraphQLCursor, - GithubRepoLocator, - GithubBatchedProjectArtifactsBaseCollector, -} from "./github/common.js"; -import { Repository } from "typeorm"; -import { - TimeSeriesCacheLookup, - TimeSeriesCacheWrapper, -} from "../cacher/time-series.js"; -import { - IArtifactGroup, - IArtifactGroupCommitmentProducer, -} from "../scheduler/types.js"; -import { Range } from "../utils/ranges.js"; -import { ArtifactGroupRecorder } from "../recorder/group.js"; -import { Batch } from "../scheduler/common.js"; -import { sha1FromArray } from "../utils/source-ids.js"; - -const GET_ISSUE_TIMELINE = gql` - query GetIssueTimeline($id: ID!, $cursor: String) { - node(id: $id) { - ... on Issue { - timelineItems( - first: 100 - itemTypes: [REOPENED_EVENT, CLOSED_EVENT, REMOVED_FROM_PROJECT_EVENT] - after: $cursor - ) { - edges { - node { - __typename - ... on Node { - id - } - ... on ReopenedEvent { - createdAt - actor { - login - } - } - ... on ClosedEvent { - createdAt - actor { - login - } - } - ... on RemovedFromProjectEvent { - createdAt - actor { - login - } - } - } - } - pageInfo { - hasNextPage - endCursor - } - } - } - ... on PullRequest { - timelineItems( - first: 100 - itemTypes: [REOPENED_EVENT, CLOSED_EVENT, REMOVED_FROM_PROJECT_EVENT] - after: $cursor - ) { - edges { - node { - __typename - ... on Node { - id - } - ... on ReopenedEvent { - createdAt - actor { - login - } - } - ... on ClosedEvent { - createdAt - actor { - login - } - } - ... on RemovedFromProjectEvent { - createdAt - actor { - login - } - } - } - } - pageInfo { - hasNextPage - endCursor - } - } - } - } - rateLimit { - limit - cost - remaining - resetAt - } - } -`; - -const GET_PULL_REQUEST_REVIEWS = gql` - query GetPullRequestReviews($id: ID!, $cursor: String) { - node(id: $id) { - ... on PullRequest { - reviews(first: 100, states: [APPROVED], after: $cursor) { - edges { - node { - id - createdAt - author { - login - } - } - } - pageInfo { - hasNextPage - endCursor - } - } - } - } - rateLimit { - limit - cost - remaining - resetAt - } - } -`; - -const GET_ALL_ISSUES_AND_PRS = gql` - query GetAllIssues($first: Int!, $searchStr: String!, $cursor: String) { - search(first: $first, type: ISSUE, query: $searchStr, after: $cursor) { - count: issueCount - edges { - node { - __typename - ... on Issue { - id - repository { - nameWithOwner - name - } - number - title - url - createdAt - updatedAt - closedAt - state - author { - login - } - - openCloseEvents: timelineItems( - first: 100 - itemTypes: [ - CLOSED_EVENT - REMOVED_FROM_PROJECT_EVENT - REOPENED_EVENT - ] - ) { - edges { - node { - __typename - ... on ReopenedEvent { - id - createdAt - actor { - login - } - } - ... on ClosedEvent { - id - createdAt - actor { - login - } - } - ... on RemovedFromProjectEvent { - id - createdAt - actor { - login - } - } - } - } - pageInfo { - hasNextPage - endCursor - } - } - } - ... on PullRequest { - id - repository { - nameWithOwner - name - } - number - title - url - createdAt - updatedAt - closedAt - state - author { - login - } - openCloseEvents: timelineItems( - first: 100 - itemTypes: [ - CLOSED_EVENT - REMOVED_FROM_PROJECT_EVENT - REOPENED_EVENT - ] - ) { - edges { - node { - __typename - ... on ReopenedEvent { - id - createdAt - actor { - login - } - } - ... on ClosedEvent { - id - createdAt - actor { - login - } - } - ... on RemovedFromProjectEvent { - id - createdAt - actor { - login - } - } - } - } - pageInfo { - hasNextPage - endCursor - } - } - - mergedAt - merged - mergedBy { - login - } - reviews(first: 100, states: [APPROVED, CHANGES_REQUESTED]) { - edges { - node { - __typename - id - createdAt - author { - login - } - state - } - } - pageInfo { - hasNextPage - endCursor - } - } - reviewCount: reviews(first: 1) { - totalCount - } - } - } - } - pageInfo { - hasNextPage - endCursor - } - } - rateLimit { - limit - cost - remaining - resetAt - } - } -`; - -// Replace this with something generated eventually. -// Too much to setup for now. -export type IssueOrPullRequest = { - __typename: string; - id: string; - repository: { - nameWithOwner: string; - name: string; - }; - title: string; - url: string; - createdAt: string; - updatedAt: string; - closedAt: string | null; - state: string; - author: Actor | null; - mergedAt: string | null | undefined; - merged: boolean; - mergedBy: Actor | null; - reviews?: Query; - openCloseEvents: Query; -}; - -export type Query = { - edges: GraphQLNode[]; - pageInfo: { - hasNextPage: boolean; - endCursor: string; - }; -}; - -export type IssueEvent = { - __typename: string; - id: string; - createdAt: string; - actor: Actor | null; -}; - -export type Review = { - id: string; - createdAt: string; - state: string; - author: Actor | null; -}; - -export type GetIssueTimelineResponse = GithubGraphQLResponse<{ - node: { - timelineItems: Query; - }; -}>; - -export type GetPullRequestReviewsResponse = GithubGraphQLResponse<{ - node: { - reviews: Query; - }; -}>; - -export type GetLatestUpdatedIssuesResponse = GithubGraphQLResponse<{ - search: Query & { count: number }; -}>; - -const DefaultGithubIssueCollectorOptions: GithubBaseCollectorOptions = { - cacheOptions: { - bucket: "github-issues", - }, -}; - -export class GithubIssueCollector extends GithubBatchedProjectArtifactsBaseCollector { - // Some of these event names are arbitrary - private eventTypeMapping: Record = { - CreatedEvent: { - Issue: "ISSUE_CREATED", - PullRequest: "PULL_REQUEST_CREATED", - }, - ClosedEvent: { - Issue: "ISSUE_CLOSED", - PullRequest: "PULL_REQUEST_CLOSED", - }, - ReopenedEvent: { - Issue: "ISSUE_REOPENED", - PullRequest: "PULL_REQUEST_REOPENED", - }, - RemovedFromProjectEvent: { - Issue: "ISSUE_REMOVED_FROM_PROJECT", - PullRequest: "PULL_REQUEST_REMOVED_FROM_PROJECT", - }, - MergedEvent: { - PullRequest: "PULL_REQUEST_MERGED", - }, - PullRequestApprovedEvent: { - PullRequest: "PULL_REQUEST_APPROVED", - }, - }; - - private eventVersion = 1; - - constructor( - projectRepository: Repository, - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - batchSize: number, - options?: Partial, - ) { - const opts = _.merge(DefaultGithubIssueCollectorOptions, options); - super(projectRepository, recorder, cache, batchSize, opts); - } - - async collect( - group: IArtifactGroup, - range: Range, - committer: IArtifactGroupCommitmentProducer, - ) { - const batch = await group.meta(); - const artifacts = await group.artifacts(); - - const groupRecorder = new ArtifactGroupRecorder(this.recorder); - try { - await this.collectEventsForRepos( - groupRecorder, - batch, - artifacts, - range, - committer, - ); - committer.commitGroup(groupRecorder); - } catch (err) { - committer.failAll(err); - } - - logger.debug(`completed issue collection for repos of ${batch.name}`); - } - - private async collectEventsForRepos( - groupRecorder: IEventGroupRecorder, - batch: Batch, - artifacts: Artifact[], - range: Range, - committer: IArtifactGroupCommitmentProducer, - ) { - const locators = artifacts - .map((a) => { - try { - return this.splitGithubRepoIntoLocator(a); - } catch (err) { - // Errored for the current artifact. End now. - committer.commit(a).withResults({ - errors: [err], - success: [], - }); - return undefined; - } - }) - .filter((a): a is GithubRepoLocator => { - return a !== undefined; - }); - - // FIXME. Quick fix. caching is broken... going to use a sha of all the locators - const locatorStrs = locators.map((l) => `${l.owner}/${l.repo}`); - const cacheKey = sha1FromArray(locators.map((l) => `${l.owner}/${l.repo}`)); - - const pages = this.cache.loadCachedOrRetrieve< - GraphQLNode[], - GithubGraphQLCursor - >( - TimeSeriesCacheLookup.new( - `${this.options.cacheOptions.bucket}/${cacheKey}`, - locatorStrs, - range, - ), - async (missing, lastPage) => { - logger.debug("loading more from github"); - const searchStrSuffix = - lastPage?.cursor?.searchSuffix || - `updated:<${missing.range.endDate.toISO()}`; - const searchStr = - missing.keys.map((a) => `repo:${a}`).join(" ") + - " sort:updated-desc " + - searchStrSuffix; - logger.debug(`running issues query with ${searchStr}`); - - const cursor = lastPage?.cursor?.githubCursor; - - // Get current page of results - const response = - await this.rateLimitedGraphQLRequest( - GET_ALL_ISSUES_AND_PRS, - { - first: 100, - searchStr: searchStr, - cursor: cursor, - }, - ); - - let nextCursor: string | undefined = response.search.pageInfo.endCursor; - let nextSearchSuffix = searchStrSuffix; - let hasNextPage = response.search.pageInfo.hasNextPage; - - let count = - (lastPage?.cursor?.count || 0) + response.search.edges.length; - const totalResults = response.search.count; - - // If we've reaached the end of the available pages and the totalResults - // is still greater than the number of results we've processed we need to - // keep going. This is a bit janky - if (!hasNextPage && totalResults > count) { - count = 0; - const last = response.search.edges.slice(-1)[0]; - const lastUpdatedAtDt = DateTime.fromISO(last.node.updatedAt); - // Some overlap is expected but we will try to keep it minimal. - nextSearchSuffix = ` updated:<${lastUpdatedAtDt - .plus({ hours: 6 }) - .toISO()} `; - nextCursor = undefined; - hasNextPage = true; - } - - return { - raw: response.search.edges, - hasNextPage: hasNextPage, - cursor: { - searchSuffix: nextSearchSuffix, - githubCursor: nextCursor, - count: count, - }, - cacheRange: missing.range, - }; - }, - ); - - const errors: unknown[] = []; - - for await (const page of pages) { - const edges = page.raw; - for (const edge of edges) { - // Stop processing if we've reached the end - if (DateTime.fromISO(edge.node.updatedAt) < range.startDate) { - return; - } - try { - await this.collectEventsForIssue( - groupRecorder, - range, - artifacts, - edge.node, - ); - } catch (err) { - errors.push(err); - } - } - } - } - - private async collectEventsForIssue( - groupRecorder: IEventGroupRecorder, - range: Range, - artifacts: Artifact[], - issue: IssueOrPullRequest, - ) { - const artifactMap = _.keyBy(artifacts, (a: Artifact) => { - return a.name.toLowerCase(); - }); - - const repoLocatorStr = issue.repository.nameWithOwner.toLowerCase(); - - const artifact = artifactMap[repoLocatorStr]; - if (!artifact) { - // Try parsing the URL - throw new Error( - `unexpected repository ${issue.repository.nameWithOwner}`, - ); - } - const creationTime = DateTime.fromISO(issue.createdAt); - - // Github replaces author with null if the user has been deleted from github. - let contributor: IncompleteArtifact | undefined = undefined; - if (issue.author !== null && issue.author !== undefined) { - if (issue.author.login !== "") { - contributor = { - name: issue.author.login.toLowerCase(), - namespace: ArtifactNamespace.GITHUB, - type: ArtifactType.GITHUB_USER, - }; - } - } - const githubId = issue.id; - const eventType = this.getEventType("CreatedEvent", issue.__typename); - - const creationEvent: IncompleteEvent = { - time: creationTime, - type: { - name: eventType, - version: this.eventVersion, - }, - to: artifact, - amount: 1, - from: contributor, - sourceId: githubId, - }; - - // Record creation - await groupRecorder.record(creationEvent); - - // Record merging of a pull request - if (issue.mergedAt) { - const mergedTime = DateTime.fromISO(issue.mergedAt); - - const mergedBy = issue.mergedBy !== null ? issue.mergedBy.login : ""; - - await groupRecorder.record({ - time: mergedTime, - type: { - name: this.getEventType("MergedEvent", issue.__typename), - version: this.eventVersion, - }, - to: artifact, - amount: 1, - from: creationEvent.from, - sourceId: githubId, - details: { - mergedBy: mergedBy, - }, - }); - } - - // Find any reviews - await this.recordReviews(groupRecorder, range, artifact, issue); - - // Find and record any close/open events - await this.recordOpenCloseEvents(groupRecorder, range, artifact, issue); - } - - private async *loadIssueTimeline(id: string): AsyncGenerator { - logger.debug(`loading issue timeline for ${id}`); - const iterator = unpaginateIterator()( - GET_ISSUE_TIMELINE, - "node.timelineItems.edges", - "node.timelineItems.pageInfo", - { - first: 100, - id: id, - }, - ); - - for await (const edges of iterator) { - for (const edge of edges.results) { - yield edge.node; - } - } - } - - private async *loadReviews(id: string): AsyncGenerator { - logger.debug(`loading reviews timeline for ${id}`); - const iterator = unpaginateIterator()( - GET_PULL_REQUEST_REVIEWS, - "node.reviews.edges", - "node.reviews.pageInfo", - { - id: id, - }, - ); - for await (const edges of iterator) { - for (const edge of edges.results) { - yield edge.node; - } - } - } - - private getEventType(eventTypeStr: string, issueType: string) { - const eventTypeMap = this.eventTypeMapping[eventTypeStr]; - if (!eventTypeMap) { - logger.debug(`no map for ${eventTypeStr}`); - } - const eventType = this.eventTypeMapping[eventTypeStr][issueType]; - if (!eventType) { - throw new Error(`invalid event ${eventTypeStr} type for ${issueType}`); - } - return eventType; - } - - private async recordReviews( - groupRecorder: IEventGroupRecorder, - range: Range, - artifact: IncompleteArtifact, - issue: IssueOrPullRequest, - ) { - if (!issue.reviews) { - return; - } - const recordReview = (review: Review) => { - const createdAt = DateTime.fromISO(review.createdAt); - const contributor: IncompleteArtifact | undefined = - review.author && review.author.login !== "" - ? { - name: review.author.login.toLowerCase(), - namespace: ArtifactNamespace.GITHUB, - type: ArtifactType.GITHUB_USER, - } - : undefined; - - return groupRecorder.record({ - time: createdAt, - type: { - name: this.getEventType("PullRequestApprovedEvent", issue.__typename), - version: 1, - }, - to: artifact, - amount: 1, - from: contributor, - sourceId: review.id, - }); - }; - - if (issue.reviews.pageInfo.hasNextPage) { - logger.debug("need to load more reviews"); - for await (const review of this.loadReviews(issue.id)) { - if (DateTime.fromISO(review.createdAt) < range.startDate) { - break; - } - await recordReview(review); - } - } else { - for (const edge of issue.reviews.edges) { - if (DateTime.fromISO(edge.node.createdAt) < range.startDate) { - break; - } - await recordReview(edge.node); - } - } - } - - private async recordOpenCloseEvents( - groupRecorder: IEventGroupRecorder, - range: Range, - artifact: IncompleteArtifact, - issue: IssueOrPullRequest, - ) { - if (!issue.openCloseEvents.edges) { - return; - } - const recordOpenCloseEvent = (event: IssueEvent) => { - const createdAt = DateTime.fromISO(event.createdAt); - const contributor: IncompleteArtifact | undefined = - event.actor && event.actor.login !== "" - ? { - name: event.actor.login.toLowerCase(), - namespace: ArtifactNamespace.GITHUB, - type: ArtifactType.GITHUB_USER, - } - : undefined; - - return groupRecorder.record({ - time: createdAt, - type: { - name: this.getEventType(event.__typename, issue.__typename), - version: 1, - }, - to: artifact, - amount: 1, - from: contributor, - sourceId: event.id, - details: { - // Grab the original author's login if it's there - originalAuthorLogin: issue.author?.login || undefined, - }, - }); - }; - - if (issue.openCloseEvents.pageInfo.hasNextPage) { - logger.debug("need to load more open/close events"); - for await (const event of this.loadIssueTimeline(issue.id)) { - if (DateTime.fromISO(event.createdAt) < range.startDate) { - break; - } - await recordOpenCloseEvent(event); - } - } else { - for (const edge of issue.openCloseEvents.edges) { - if (DateTime.fromISO(edge.node.createdAt) < range.startDate) { - break; - } - await recordOpenCloseEvent(edge.node); - } - } - } -} diff --git a/indexer/src/collectors/github/common.ts b/indexer/src/collectors/github/common.ts deleted file mode 100644 index 498f6768e..000000000 --- a/indexer/src/collectors/github/common.ts +++ /dev/null @@ -1,292 +0,0 @@ -import { Repository, In } from "typeorm"; -import { PageInfo } from "./unpaginate.js"; -import { - Artifact, - Project, - ArtifactType, - ArtifactNamespace, -} from "../../db/orm-entities.js"; -import { GenericError } from "../../common/errors.js"; -import { IEventRecorderClient } from "../../recorder/types.js"; -import { TimeSeriesCacheWrapper } from "../../cacher/time-series.js"; -import { ClientError, RequestDocument, Variables } from "graphql-request"; -import { graphQLClient } from "./graphql-client.js"; -import { DateTime } from "luxon"; -import { logger } from "../../utils/logger.js"; -import { - BatchedProjectArtifactsCollector, - ProjectArtifactsCollector, -} from "../../scheduler/common.js"; -import { Mutex } from "async-mutex"; -import { - MultiplexGithubGraphQLRequester, - MultiplexRequestResponse, -} from "./multiplex-graphql.js"; -import { asyncBatchFlattened } from "../../utils/array.js"; - -export class IncompleteRepoName extends GenericError {} -export type GithubRepoLocator = { owner: string; repo: string }; - -function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -export type GithubGraphQLResponse = T & { - rateLimit: { - remaining: number; - limit: number; - cost: number; - resetAt: string; - }; -}; - -export type GraphQLNode = { - node: T; -}; - -export type Actor = { - login: string; -}; - -export type PaginatableEdges = { - edges: T[]; - pageInfo: PageInfo; -}; - -export interface GithubBaseCollectorOptions { - cacheOptions: { - bucket: string; - }; -} - -export const DefaultGithubBaseCollectorOptions: GithubBaseCollectorOptions = { - cacheOptions: { - bucket: "github-commits", - }, -}; - -export type GithubGraphQLCursor = { - searchSuffix: string; - githubCursor?: string; - count: number; -}; - -// Create a github mixin -type Constructor = new (...args: any[]) => object; - -export function GithubCollectorMixins(Base: TBase) { - return class extends Base { - _requestMutex: Mutex; - _resetTime: DateTime; - - constructor(...args: any[]) { - super(...args); - this._requestMutex = new Mutex(); - this._resetTime = DateTime.fromISO("1970-01-01T00:00:00Z"); - } - - splitGithubRepoIntoLocator(artifact: Artifact): GithubRepoLocator { - const rawURL = artifact.url; - if (!rawURL) { - throw new IncompleteRepoName(`no url for artifact[${artifact.id}]`); - } - // Parse without any trailing slashes - const repoURL = new URL(rawURL.replace(/\/+$/, "")); - if (repoURL.host !== "github.com") { - throw new IncompleteRepoName(`unexpected url ${rawURL}`); - } - const splitName = repoURL.pathname.slice(1).split("/"); - if (splitName.length !== 2) { - throw new IncompleteRepoName(`unexpected url ${rawURL}`); - } - return { - owner: splitName[0].toLowerCase(), - repo: splitName[1].toLowerCase(), - }; - } - - debugLogAnError(message: string, err: unknown) { - logger.debug(message); - try { - logger.debug(JSON.stringify(err)); - } catch (_e) { - logger.debug("could not json stringify the error"); - logger.debug(err); - } - } - - async rateLimitedGraphQLRequest>( - query: RequestDocument, - variables: Variables, - allowErrors: boolean = false, - retries: number = 10, - ): Promise { - for (let i = 0; i < retries; i++) { - if (this._resetTime) { - const now = DateTime.now(); - const diffMs = this._resetTime.toMillis() - now.toMillis(); - if (diffMs > 0) { - if (diffMs > 200) { - logger.debug( - `encountered rate limit on github. waiting for ${diffMs}ms`, - ); - } - await sleep(diffMs); - } - } - - const release = await this._requestMutex.acquire(); - // Hacky retry loop for 5XX errors - try { - const response = await graphQLClient.request(query, variables, { - Accept: "application/vnd.github.hawkgirl-preview+json", - }); - const rateLimit = response.rateLimit; - if ( - rateLimit.remaining == 0 || - rateLimit.remaining - rateLimit.cost <= 0 - ) { - this._resetTime = DateTime.fromISO(rateLimit.resetAt); - } else { - // Artificially rate limit to 5reqs/second - this._resetTime = DateTime.now().plus(100); - } - release(); - return response; - } catch (err) { - release(); - if (err instanceof ClientError) { - // Retry up - if (err.response.status >= 500 && err.response.status < 600) { - logger.error("hit a github 500 error. waiting for some period"); - this.debugLogAnError("github 500 error:", err); - this._resetTime = DateTime.now().plus({ - milliseconds: 2500 * (i + 1), - }); - continue; - } - - if (err.response.status === 403) { - logger.error("likely hit a secondary rate limit. pausing 5 mins"); - this.debugLogAnError("github 403 error:", err); - logger.debug(JSON.stringify(err)); - this._resetTime = DateTime.now().plus({ milliseconds: 300000 }); - continue; - } - if (err.response.status === 200) { - if (allowErrors) { - return err.response.data as GithubGraphQLResponse; - } - logger.debug("very interesting! we only have some errors"); - throw new Error("no errors expected in response"); - } - } - const errAsString = `${err}`; - if (errAsString.indexOf("Premature close") !== -1) { - logger.error( - "received a premature close from github. retrying request after a pause", - ); - logger.error(err); - this.debugLogAnError("github premature close error:", err); - this._resetTime = DateTime.now().plus({ - milliseconds: 2500 * (i + 1), - }); - continue; - } - throw err; - } - } - throw new Error("too many retries for graphql request"); - } - - async rateLimitedGraphQLGeneratedRequest< - Input extends object, - Response, - R extends GithubGraphQLResponse<{ [key: string]: Response }>, - >( - multiplex: MultiplexGithubGraphQLRequester, - inputs: Input[], - ): Promise> { - try { - const resp = await multiplex.request(inputs, (r, v) => { - return this.rateLimitedGraphQLRequest(r, v, true, 2); - }); - return resp; - } catch (err) { - logger.debug("attempt to use smaller batches"); - const responses = await asyncBatchFlattened( - inputs, - 1, - async (batch) => { - const resp = await multiplex.request(batch, (r, v) => { - return this.rateLimitedGraphQLRequest(r, v, true, 3); - }); - return [resp]; - }, - ); - return { - raw: responses.slice(-1)[0].raw, - items: responses.flatMap((r) => r.items), - }; - } - } - }; -} - -class _GithubByProjectBaseCollector extends ProjectArtifactsCollector { - protected recorder: IEventRecorderClient; - protected cache: TimeSeriesCacheWrapper; - protected options: GithubBaseCollectorOptions; - protected resetTime: DateTime | null; - private requestMutex: Mutex; - - constructor( - projectRepository: Repository, - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - options: GithubBaseCollectorOptions, - ) { - super(projectRepository, recorder, cache, { - type: In([ArtifactType.GIT_REPOSITORY]), - namespace: ArtifactNamespace.GITHUB, - }); - - this.options = options; - this.resetTime = null; - this.requestMutex = new Mutex(); - } -} - -class _GithubBatchedProjectsBaseCollector extends BatchedProjectArtifactsCollector { - protected recorder: IEventRecorderClient; - protected cache: TimeSeriesCacheWrapper; - protected options: GithubBaseCollectorOptions; - protected resetTime: DateTime | null; - - constructor( - projectRepository: Repository, - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - batchSize: number, - options: GithubBaseCollectorOptions, - ) { - super(projectRepository, recorder, cache, batchSize, { - type: In([ArtifactType.GIT_REPOSITORY]), - namespace: ArtifactNamespace.GITHUB, - }); - - this.options = options; - this.resetTime = null; - } -} - -class _Base {} - -export const GithubBaseMixin = GithubCollectorMixins(_Base); - -export const GithubByProjectBaseCollector = GithubCollectorMixins( - _GithubByProjectBaseCollector, -); -export const GithubBatchedProjectArtifactsBaseCollector = GithubCollectorMixins( - _GithubBatchedProjectsBaseCollector, -); diff --git a/indexer/src/collectors/github/get-org-repos.ts b/indexer/src/collectors/github/get-org-repos.ts deleted file mode 100644 index e9a09e3fc..000000000 --- a/indexer/src/collectors/github/get-org-repos.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { gql } from "graphql-request"; -import { unpaginate } from "./unpaginate.js"; - -const getUserRepos = gql` - query getUserRepos($name: String!, $cursor: String) { - rateLimit { - limit - cost - remaining - resetAt - } - user(login: $name) { - id - createdAt - repositories(first: 100, after: $cursor) { - pageInfo { - hasNextPage - endCursor - } - edges { - node { - nameWithOwner - url - name - } - } - } - } - } -`; - -const getOrgRepos = gql` - query getOrgRepos($name: String!, $cursor: String) { - rateLimit { - limit - cost - remaining - resetAt - } - organization(login: $name) { - id - createdAt - repositories(first: 100, after: $cursor) { - pageInfo { - hasNextPage - endCursor - } - edges { - node { - nameWithOwner - isFork - url - name - } - } - } - } - } -`; - -interface OrgData { - rateLimit: { - limit: number; - cost: number; - remaining: number; - resetAt: string; - }; - organization: { - id: string; - createdAt: string; - repositories: { - pageInfo: { - hasNextPage: boolean; - endCursor: string; - }; - edges: [ - { - node: Repository; - }, - ]; - }; - }; -} - -interface UserData { - rateLimit: { - limit: number; - cost: number; - remaining: number; - resetAt: string; - }; - user: { - id: string; - createdAt: string; - repositories: { - pageInfo: { - hasNextPage: boolean; - endCursor: string; - }; - edges: [ - { - node: Repository; - }, - ]; - }; - }; -} - -export interface Repository { - nameWithOwner: string; - url: string; - name: string; - isFork: boolean; -} - -export async function getOwnerRepos(ownerName: string): Promise { - const variables = { - name: ownerName, - }; - - try { - const nodes = await unpaginate()( - getOrgRepos, - "organization.repositories.edges", - "organization.repositories.pageInfo", - variables, - ); - return nodes.map((node: any) => node.node); - } catch (err) { - // Try getting the information as a user - const nodes = await unpaginate()( - getUserRepos, - "user.repositories.edges", - "user.repositories.pageInfo", - variables, - ); - return nodes.map((node: any) => node.node); - } - //return nodes.map((node: any) => node.node); -} diff --git a/indexer/src/collectors/github/graphql-client.ts b/indexer/src/collectors/github/graphql-client.ts deleted file mode 100644 index 2f66045ff..000000000 --- a/indexer/src/collectors/github/graphql-client.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { GraphQLClient } from "graphql-request"; -import { GITHUB_GRAPHQL_API, GITHUB_TOKEN } from "../../config.js"; - -export const graphQLClient = new GraphQLClient(GITHUB_GRAPHQL_API, { - headers: { - authorization: `Bearer ${GITHUB_TOKEN}`, - }, -}); diff --git a/indexer/src/collectors/github/multiplex-graphql.ts b/indexer/src/collectors/github/multiplex-graphql.ts deleted file mode 100644 index be56ef090..000000000 --- a/indexer/src/collectors/github/multiplex-graphql.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { RequestDocument, Variables } from "graphql-request"; -import { jsonToGraphQLQuery, VariableType } from "json-to-graphql-query"; -import _ from "lodash"; - -const varTypeMap = { - String: (i: any) => { - return i as string; - }, - "String!": (i: any) => { - return i as string; - }, - Int: (i: any) => { - return i as string; - }, - "Int!": (i: any) => { - return i as string; - }, - PreciseDateTime: (i: any) => { - const d = i as Date; - return d.toUTCString(); - }, - "PreciseDateTime!": (i: any) => { - const d = i as Date; - return d.toUTCString(); - }, - GitTimestamp: (i: any) => { - const d = i as Date; - return d.toUTCString(); - }, - "GitTimestamp!": (i: any) => { - const d = i as Date; - return d.toUTCString(); - }, -}; - -export type MultiplexVarType = keyof typeof varTypeMap; - -export type MultiplexInputMapping = Record< - keyof Input, - VariableType ->; -export type MultiplexObjectRetreiverResponse = { - type: string; - definition: object; -}; -export type MultiplexObjectRetreiver = ( - vars: MultiplexInputMapping, -) => MultiplexObjectRetreiverResponse; - -export type MultiplexRequestResponse< - R extends { [key: string]: Response }, - Response, -> = { - raw: R; - items: (Response | null)[]; -}; - -/* - * This class allows us to make requests for multiple object lookups items given a - * specific template. - */ -export class MultiplexGithubGraphQLRequester { - private retreiver: MultiplexObjectRetreiver; - private variableDef: Record; - private name: string; - - constructor( - name: string, - variableDef: Record, - objectRetreiver: MultiplexObjectRetreiver, - ) { - this.name = name; - this.variableDef = variableDef; - this.retreiver = objectRetreiver; - } - - async request( - inputs: Input[], - requester: (r: RequestDocument, v: Variables) => Promise, - ): Promise> { - const req = this.makeRequest(inputs); - const res = await requester(req.graphql, req.variables); - - const items: (Response | null)[] = inputs.map((_n, index) => { - return res[`response${index}`]; - }); - - return { - raw: res, - items: items, - }; - } - - makeRequest(inputs: Input[]): { - graphql: string; - variables: Record; - } { - if (inputs.length === 0) { - throw new Error("need inputs"); - } - const keys = Object.keys(inputs[0]) as (keyof Input)[]; - let index = 0; - - const query = { - query: { - __name: this.name, - __variables: {}, - rateLimit: { - limit: true, - cost: true, - remaining: true, - resetAt: true, - }, - }, - }; - - let varDefs = {}; - let inputVars: Record = {}; - - for (const input of inputs) { - const currentVarName = (c: keyof Input) => { - return `${c as string}${index}`; - }; - const currentVarDefs = keys.reduce>((a, c) => { - a[currentVarName(c)] = this.variableDef[c]; - return a; - }, {}); - - // Add variable definitions - varDefs = _.merge(varDefs, currentVarDefs); - - const mapping = keys.reduce>((a, c) => { - a[c] = new VariableType(currentVarName(c)); - return a; - }, {} as MultiplexInputMapping); - - const currentInputVars = keys.reduce>((a, c) => { - a[currentVarName(c)] = input[c]; - return a; - }, {}); - inputVars = _.merge(inputVars, currentInputVars); - - const resp = this.retreiver(mapping); - const definition = _.merge(resp.definition, { __aliasFor: resp.type }); - - const queryAddition: Record = {}; - queryAddition[`response${index}`] = definition; - query.query = _.merge(query.query, queryAddition); - - index += 1; - } - - query.query.__variables = _.merge(query.query.__variables, varDefs); - const gqlQuery = jsonToGraphQLQuery(query, { pretty: true }); - return { - graphql: gqlQuery, - variables: inputVars, - }; - } -} diff --git a/indexer/src/collectors/github/unpaginate.ts b/indexer/src/collectors/github/unpaginate.ts deleted file mode 100644 index e2c8a91ce..000000000 --- a/indexer/src/collectors/github/unpaginate.ts +++ /dev/null @@ -1,105 +0,0 @@ -import ora from "ora"; -import { graphQLClient } from "./graphql-client.js"; -import { Path, Choose, getPath } from "../../utils/getPath.js"; - -export type PageInfo = { - hasNextPage: boolean; - endCursor: string; -}; - -// TODO: return type should error if not array -// TODO: pageInfoPath should locate a PageInfo object -// type HasPageInfo, K extends Path> = Choose extends PageInfo ? K : never; - -// typescript can't infer T because it's not passed in (like in getPath), and K can't be inferred precisely without writing -// out the entire path again. So we curry the function. Sorry for the mess... -export function unpaginate>() { - return async function , A extends Path>( - query: string, - dataPath: K, - pageInfoPath: A, - variables: any = {}, - ): Promise extends Array ? Choose : never> { - const items: any[] = []; - for await (const page of unpaginateIterator()( - query, - dataPath, - pageInfoPath, - variables, - )) { - items.push(...(page.results as any[])); - } - return items as any; - }; -} - -export type UnpaginateResult< - T extends Record, - K extends Path, -> = { - results: Choose extends Array ? Choose : never; - raw: unknown; -}; - -export function unpaginateIterator>() { - return async function* , A extends Path>( - query: string, - dataPath: K, - pageInfoPath: A, - variables: any = {}, - ): AsyncGenerator> { - let cursor = null; - - const spinner = ora("GitHub API").start(); - /* eslint-disable-next-line no-constant-condition */ - while (true) { - const data = await graphQLClient.request(query, { - ...variables, - cursor: cursor, - }); - - yield { - results: await getPath(data, dataPath), - raw: data, - }; - - // hacky... slow things down right now - await sleep(250); - - const rateLimit: RateLimit = data.rateLimit; - spinner.suffixText = `: ${rateLimit.remaining}/${rateLimit.limit} credits remaining `; - - if ( - rateLimit.remaining == 0 || - rateLimit.remaining - rateLimit.cost <= 0 - ) { - const timeToReset = Date.parse(rateLimit.resetAt) - Date.now() + 1000; - console.log(`sleeping until rate limit reset for ${timeToReset}ms`); - await sleep(timeToReset); - } - - const pageInfo: PageInfo = getPath(data, pageInfoPath); - if (!pageInfo.hasNextPage) { - break; - } - - if (cursor === pageInfo.endCursor) { - throw new Error("the cursor is not changing. please fix your query"); - } - - cursor = pageInfo.endCursor; - } - spinner.stop(); - }; -} - -function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -export interface RateLimit { - limit: number; - cost: number; - remaining: number; - resetAt: string; -} diff --git a/indexer/src/collectors/import-oss-directory.ts b/indexer/src/collectors/import-oss-directory.ts deleted file mode 100644 index 89f2864ad..000000000 --- a/indexer/src/collectors/import-oss-directory.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { fetchData } from "oss-directory"; -import { - getCollectionBySlug, - getProjectBySlug, - ossUpsertCollection, - ossUpsertProject, -} from "../db/entities.js"; -import { CommonArgs } from "../utils/api.js"; -import { logger } from "../utils/logger.js"; -import { CollectionRepository } from "../db/collection.js"; -import { In, IsNull } from "typeorm"; -import _ from "lodash"; - -/** - * Entrypoint arguments - */ -export type ImportOssDirectoryArgs = CommonArgs & { - skipExisting?: boolean; -}; - -export async function importOssDirectory( - args: ImportOssDirectoryArgs, -): Promise { - const { skipExisting } = args; - logger.info("Importing from 'oss-directory'"); - const { projects, collections } = await fetchData(); - logger.info( - `Found ${projects.length} projects and ${collections.length} collections`, - ); - - logger.info("Upserting projects..."); - for (let i = 0; i < projects.length; i++) { - const p = projects[i]; - // This is not ideal but for now this will ensure the slug is normalized - p.slug = p.slug.toLowerCase(); - if (skipExisting && (await getProjectBySlug(p.slug))) { - logger.info( - `projects[${i}]: Skipping ${p.slug} because it already exists`, - ); - continue; - } - logger.info(`projects[${i}]: Upserting ${p.slug}`); - try { - const result = await ossUpsertProject(p); - logger.info(`projects[${i}]: Added ${result.added} artifacts`); - logger.info(`projects[${i}]: Removed ${result.removed} artifacts`); - } catch (e) { - logger.error( - `projects[${i}]: error occured processing project ${p.slug}. Skipping with error: ${e}`, - ); - } - } - - const currentCollections = await CollectionRepository.find({ - where: { - type: { name: "OSS_DIRECTORY" }, - deletedAt: IsNull(), - }, - }); - - // Calculate the removed collections - const removedCollections = _.differenceWith( - currentCollections, - collections, - (a, b) => { - return a.slug === b.slug; - }, - ); - - if (removedCollections.length > 0) { - logger.info(`removing ${removedCollections.length} deleted collections`); - - // Mark the collections as deleted - await CollectionRepository.update( - { - id: In(removedCollections.map((c) => c.id)), - }, - { - deletedAt: new Date(), - }, - ); - } - - logger.info("Upserting collections..."); - for (let i = 0; i < collections.length; i++) { - const c = collections[i]; - c.slug = c.slug.toLowerCase(); - if (skipExisting && (await getCollectionBySlug(c.slug))) { - logger.info( - `collections[${i}]: Skipping ${c.slug} because it already exists`, - ); - continue; - } - logger.info(`collections[${i}]: Upserting ${c.slug}`); - try { - const result = await ossUpsertCollection(c); - logger.info(`collections[${i}]: Added ${result.added} artifacts`); - logger.info(`collections[${i}]: Removed ${result.removed} artifacts`); - } catch (e) { - logger.error( - `collections[${i}]: error occured processing colelction ${c.slug}. Skipping with error: ${e}`, - ); - } - } - - logger.info("Done"); -} diff --git a/indexer/src/collectors/npm-downloads.ts b/indexer/src/collectors/npm-downloads.ts deleted file mode 100644 index 58e7563c9..000000000 --- a/indexer/src/collectors/npm-downloads.ts +++ /dev/null @@ -1,218 +0,0 @@ -import _ from "lodash"; -import npmFetch from "npm-registry-fetch"; -import { ensureString, ensureArray, ensureNumber } from "../utils/common.js"; -import { MalformedDataError } from "../utils/error.js"; -import { logger } from "../utils/logger.js"; -import { - Batch, - BatchedProjectArtifactsCollector, -} from "../scheduler/common.js"; -import { - Project, - Artifact, - ArtifactType, - ArtifactNamespace, -} from "../index.js"; -import { - IArtifactGroup, - CollectResponse, - IArtifactGroupCommitmentProducer, -} from "../scheduler/types.js"; -import { Range } from "../utils/ranges.js"; -import { - TimeSeriesCacheLookup, - TimeSeriesCacheWrapper, -} from "../cacher/time-series.js"; -import { In, Repository } from "typeorm"; -import { IEventRecorderClient, RecordHandle } from "../recorder/types.js"; -import { DateTime } from "luxon"; -import { sha1FromArray } from "../utils/source-ids.js"; - -// API endpoint to query -const NPM_HOST = "https://api.npmjs.org/"; -// npm was initially released 2010-01-12 -// Only get data up to 2 days ago, accounting for incomplete days and time zones -// const TODAY_MINUS = 2; - -export interface NPMCollectorOptions { - cacheOptions: { - bucket: string; - }; -} - -export const DefaultNPMCollectorOptions: NPMCollectorOptions = { - cacheOptions: { - bucket: "npm-downloads", - }, -}; - -export class NpmDownloadCollector extends BatchedProjectArtifactsCollector { - private options: NPMCollectorOptions; - - constructor( - projectRepository: Repository, - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - batchSize: number, - options?: Partial, - ) { - super(projectRepository, recorder, cache, batchSize, { - type: In([ArtifactType.NPM_PACKAGE]), - namespace: ArtifactNamespace.NPM_REGISTRY, - }); - - this.options = _.merge(DefaultNPMCollectorOptions, options); - } - - async collect( - group: IArtifactGroup, - range: Range, - committer: IArtifactGroupCommitmentProducer, - ): Promise { - const artifacts = await group.artifacts(); - - for (const npmPackage of artifacts) { - await this.getEventsForPackage(npmPackage, range, committer); - } - } - - async getEventsForPackage( - npmPackage: Artifact, - range: Range, - committer: IArtifactGroupCommitmentProducer, - ) { - const response = this.cache.loadCachedOrRetrieve( - TimeSeriesCacheLookup.new( - `${this.options.cacheOptions.bucket}/${npmPackage.name}`, - [npmPackage.name], - range, - ), - async (missing) => { - const downloads = await getDailyDownloads( - npmPackage.name, - missing.range.startDate, - missing.range.endDate, - ); - return { - raw: downloads, - cacheRange: missing.range, - hasNextPage: false, - }; - }, - ); - const handles: RecordHandle[] = []; - - for await (const page of response) { - const days = page.raw; - for (const download of days) { - const sourceId = sha1FromArray([ - "NPM_DOWNLOADS", - npmPackage.name, - download.day, - ]); - - handles.push( - await this.recorder.record({ - time: DateTime.fromISO(download.day), - type: { - name: "DOWNLOADS", - version: 1, - }, - to: npmPackage, - sourceId: sourceId, - amount: download.downloads, - }), - ); - } - } - committer.commit(npmPackage).withHandles(handles); - } -} - -interface DayDownloads { - downloads: number; - day: string; -} - -function createDayDownloads(x: any): DayDownloads { - return { - downloads: ensureNumber(x.downloads), - day: ensureString(x.day), - }; -} - -/** - * When you query the NPM API with a range, it may only return a subset of dates you want - * This function lets us recurse until we get everything - * @param name npm package name - * @param start date inclusive - * @param end date inclusive - */ -async function getDailyDownloads( - name: string, - start: DateTime, - end: DateTime, -): Promise { - const dateRange = `${start.toISODate()}:${end.toISODate()} `; - const endpoint = `/downloads/range/${dateRange}/${name}`; - logger.debug(`Fetching ${endpoint}`); - let results: Record; - try { - results = await npmFetch.json(endpoint, { registry: NPM_HOST }); - } catch (e) { - const err = e as { statusCode?: number; body?: { error?: string } }; - logger.warn("Error fetching from NPM API: ", err); - if (err.statusCode == 404) { - logger.error(`${name} project cannot be found. skipping`); - return []; - } - if (err.statusCode && err.body) { - // suppress 404's for now but we should like collect some kinds of warnings for data that is missing - if ( - err.statusCode == 400 && - err.body.error?.indexOf("end date > start date") !== -1 - ) { - logger.debug( - `npm is at the limit of all available history for project ${name}`, - ); - return []; - } - } - throw err; - } - //logger.info(JSON.stringify(fetchResults, null, 2)); - const resultStart = DateTime.fromISO(ensureString(results.start), { - zone: "utc", - }); - const resultEnd = DateTime.fromISO(ensureString(results.end), { - zone: "utc", - }); - const resultDownloads = ensureArray(results.downloads).map( - createDayDownloads, - ); - logger.info( - `Got ${ - resultDownloads.length - } results from ${resultStart.toISODate()} to ${resultEnd.toISODate()}`, - ); - - // If we got all the data, we're good - if ( - start.startOf("day").toMillis() === resultStart.startOf("day").toMillis() && - end.startOf("day").toMillis() === resultEnd.startOf("day").toMillis() - ) { - return [...resultDownloads]; - } else if ( - end.startOf("day").toMillis() !== resultEnd.startOf("day").toMillis() - ) { - // Assume that NPM will always give us the newest data first - throw new MalformedDataError( - `Expected end date ${end.toISO()} but got ${resultEnd.toISO()}`, - ); - } else { - // If we didn't get all the data, recurse - const missingEnd = resultStart.minus({ day: 1 }); - const missingResults = await getDailyDownloads(name, start, missingEnd); - return [...missingResults, ...resultDownloads]; - } -} diff --git a/indexer/src/common/errors.ts b/indexer/src/common/errors.ts deleted file mode 100644 index fd34b3c18..000000000 --- a/indexer/src/common/errors.ts +++ /dev/null @@ -1,12 +0,0 @@ -export class GenericError extends Error { - details: Record; - - constructor(message: string, details?: Record) { - super(message); - this.details = details ?? {}; - } - - toJSON(): string { - return JSON.stringify({ message: this.message, details: this.details }); - } -} diff --git a/indexer/src/config.ts b/indexer/src/config.ts deleted file mode 100644 index 5ee932ae2..000000000 --- a/indexer/src/config.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as dotenv from "dotenv"; -dotenv.config(); - -export function requireEnv(identifier: string) { - const value = process.env[identifier]; - - if (!value) { - throw new Error(`Required env var ${identifier} does not exist`); - } - return value; -} - -export function envWithDefault(identifier: string, defaultValue: any) { - const value = process.env[identifier]; - if (!value) { - return defaultValue; - } - return value; -} - -export function envBoolean( - identifier: string, - defaultValue: boolean = false, -): boolean { - if (defaultValue) { - return process.env[identifier] === "false" ? false : true; - } - return process.env[identifier] === "true" ? true : false; -} - -export const DB_HOST = requireEnv("DB_HOST"); -export const DB_PORT = requireEnv("DB_PORT"); -export const DB_USER = requireEnv("DB_USER"); -export const DB_PASSWORD = requireEnv("DB_PASSWORD"); -export const DB_DATABASE = requireEnv("DB_DATABASE"); -export const GITHUB_GRAPHQL_API = requireEnv("X_GITHUB_GRAPHQL_API"); -export const GITHUB_TOKEN = requireEnv("X_GITHUB_TOKEN"); -export const DUNE_API_KEY = requireEnv("DUNE_API_KEY"); -export const DUNE_CSV_DIR_PATH = process.env.DUNE_CSVS_DIR_PATH || ""; -export const TEST_ONLY_ALLOW_CLEAR_DB = envBoolean("TEST_ONLY_ALLOW_CLEAR_DB"); -export const NO_DYNAMIC_LOADS = envBoolean("NO_DYNAMIC_LOADS"); -export const GITHUB_WORKERS_OWNER = envWithDefault( - "GITHUB_WORKERS_OWNER", - "opensource-observer", -); -export const GITHUB_WORKERS_REPO = envWithDefault("GITHUB_WORKERS_REPO", "oso"); -export const GITHUB_WORKERS_REF = envWithDefault("GITHUB_WORKERS_REF", "main"); -export const GITHUB_WORKERS_WORKFLOW_ID = envWithDefault( - "GITHUB_WORKERS_WORKFLOW_ID", - "indexer-worker.yml", -); -export const ENABLE_DB_TESTS = envBoolean("ENABLE_DB_TESTS"); -export const REDIS_URL = envWithDefault("REDIS_URL", "redis://localhost:6379"); -export const INDEXER_SPAWN = envBoolean("INDEXER_SPAWN"); -export const DEBUG_DB = envBoolean("DEBUG_DB"); -export const DB_APPLICATION_NAME = envWithDefault( - "DB_APPLICATION_NAME", - "indexer-default", -); - -// This should never be set to true for production. This might cause data loss. -export const DB_SYNCHRONIZE = envBoolean("DB_SYNCHRONIZE"); diff --git a/indexer/src/database.py b/indexer/src/database.py deleted file mode 100644 index e4ad278bd..000000000 --- a/indexer/src/database.py +++ /dev/null @@ -1,271 +0,0 @@ -from dotenv import load_dotenv -import json -import logging -import os -import pandas as pd -from supabase import create_client, Client -import sys - -from validate_github_org import validate_github_org -from validate_eth_address import get_address_data - -from events.github_events import execute_org_query -from events.zerion_scraper import convert_csvs_to_records -from events.funding_rounds import get_transfers - - -QUERIES = ["merged PR", "issue", "created PR"] - - -PROJECTS_TABLE = 'projects' -WALLETS_TABLE = 'wallets' -EVENTS_TABLE = 'events' -FUNDING_TABLE = 'funding' - -# Configure logging -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(levelname)s - %(message)s', - filename='logging.log' -) - - -# -------------- DATABASE SETUP -------------- # - -def supabase_client() -> Client: - load_dotenv() - url = os.environ.get("SUPABASE_URL") - key = os.environ.get("SUPABASE_KEY") - return create_client(url, key) - -supabase = supabase_client() - -# -------------- DATABASE OPS ---------------- # - - -def select_all(table): - response = (supabase - .table(table) - .select('*') - .execute()) - return response.data - - -def select_all_project_events(project_id, event_type): - response = (supabase - .table(EVENTS_TABLE) - .select('*') - .eq('project_id', project_id) - .eq('event_type', event_type) - .execute()) - return response.data - - -def select_col(table, col): - response = (supabase - .table(table) - .select(col) - .execute()) - lst = [x[col] for x in response.data] - return lst - - -def select_row(table, row_id): - response = (supabase - .table(table) - .select('*') - .eq('id', row_id) - .execute()) - if response.data: - return response.data[0] - - -def insert(table, records): - response = (supabase - .table(table) - .insert(records) - .execute()) - return response.data - - -def bulk_insert(table, records, lim=2000): - for i in range(0, len(records), lim): - insert(table, records[i:i + lim]) - - -# -------------- DB INSERT SCRIPTS -------------- # - - -def insert_project(project): - - name = project['name'] - github_org = project['github_org'] - description = project.get('description') - - record = dict( - name=name, - github_org=github_org, - description=description - ) - - response = (supabase - .table(PROJECTS_TABLE) - .select('id, name, github_org') - .ilike('name', f'%{name}%') - .execute()) - - if response.data: - logging.info(f"DUPLICATE PROJECT: {record} -> {response.data}") - return response.data - - response = (supabase - .table(PROJECTS_TABLE) - .select('id, name, github_org') - .ilike('github_org', f'%{github_org}%') - .execute()) - - if response.data: - logging.info(f"DUPLICATE PROJECT: {record} -> {response.data}") - return response.data - - return insert(PROJECTS_TABLE, record) - - -def insert_wallet(wallet_data): - - address = wallet_data['address'] - - response = (supabase - .table(WALLETS_TABLE) - .select('id, project_id') - .ilike('address', f'%{address}%') - .execute()) - - if response.data: - logging.info(f"DUPLICATE WALLET: {address} -> {response.data}") - return response.data - - return insert(WALLETS_TABLE, wallet_data) - - -def insert_grant_funding(funding_id): - - funding_data = select_row(FUNDING_TABLE, funding_id) - if not funding_data: - return - - grant = { - "chain": funding_data['chain'], - "address": funding_data['address'], - "token": [funding_data['token']], - "action": "tokentx" - } - transfers_data = get_transfers(grant) - - if not transfers_data: - return - - wallet_mapping = { - w['address'].lower():w['id'] - for w in select_all(WALLETS_TABLE) - } - - events_data = [] - for event in transfers_data: - addr = event['details'].get('to') - if addr: - project_id = wallet_mapping.get(addr.lower()) - if project_id: - event.update({'project_id': project_id}) - event['details'].update({'funding_id': funding_id}) - events_data.append(event) - - return insert(EVENTS_TABLE, events_data) - - -# -------------- POPULATE DB SCRIPTS------------- # - - -def populate_from_json(json_path): - - with open(json_path, 'r') as f: - projects_data = json.load(f) - - for project in projects_data: - if not validate_github_org(project['github_org']): - logging.info(f"INVALID GITHUB: {project['github_org']}") - continue - results = insert_project(project) - project_id = results[0]['id'] - for address in project['wallets']: - address_data = get_address_data(address) - if not address_data: - logging.info(f"INVALID WALLET: {address}") - continue - address_data.update({'project_id': project_id}) - insert_wallet(address_data) - - -def insert_project_github_events(query_num, project_id, start_date, end_date): - - project_data = select_row(PROJECTS_TABLE, project_id) - github_org = project_data['github_org'] - - events = execute_org_query(query_num, github_org, start_date, end_date) - for event in events: - event.update({"project_id": project_id}) - - bulk_insert(EVENTS_TABLE, events) - logging.info(f"Successfully added {len(events)} events for project {github_org}") - - -def insert_zerion_transactions(): - - records = convert_csvs_to_records() - - batch_size = 1000 - batches = [ - records[i:i + batch_size] - for i in range(0, len(records), batch_size) - ] - - for batch in batches: - response = (supabase - .table("events") - .insert(batch) - .execute()) - - -# -------------- MAIN SCRIPT -------------------- # - -def populate_db(): - - # populate projects and wallet addresses - populate_from_json("data/op-rpgf2/projects.json") - populate_from_json("data/gitcoin-allo/allo.json") - - # populate github events - start, end = '2018-01-01T00:00:00Z', '2023-05-25T00:00:00Z' - project_ids = select_col(PROJECTS_TABLE, 'id') - for query_num, query_name in enumerate(QUERIES): - for pid in project_ids: - existing_entries = select_all_project_events(pid, query_name) - if len(existing_entries): - logging.info(f"\nSkipping `{pid}` for project id: {pid}") - else: - logging.info(f"\nAdding `{pid}` for project id: {pid}") - insert_project_github_events(query_num, pid, start, end) - - # insert all zerion data (saved locally as csvs) - insert_zerion_transactions() - - - -if __name__ == "__main__": - - #populate_db() - insert_zerion_transactions() - - # testing - #start, end = '2022-01-01T00:00:00Z', '2023-05-24T00:00:00Z' - #insert_project_github_events(1, 1, start, end) \ No newline at end of file diff --git a/indexer/src/db/artifacts.ts b/indexer/src/db/artifacts.ts deleted file mode 100644 index e149498d4..000000000 --- a/indexer/src/db/artifacts.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { AppDataSource } from "./data-source.js"; -import { Artifact, EventType, Event, ArtifactType } from "./orm-entities.js"; -import { DeepPartial } from "typeorm"; -import { Range } from "../utils/ranges.js"; - -export const ArtifactRepository = AppDataSource.getRepository(Artifact).extend({ - async createMany(artifacts: DeepPartial[]) { - const newArtifacts = Artifact.create(artifacts); - return await this.insert(newArtifacts); - }, - async upsertMany(artifacts: DeepPartial[]) { - const newArtifacts = Artifact.create(artifacts); - return await this.upsert(newArtifacts, { - conflictPaths: ["namespace", "name"], - upsertType: "on-conflict-do-update", - }); - }, - async nonCanonical(types: ArtifactType[]) { - const sumString = - 'SUM(CASE WHEN LOWER(a."name") = a."name" THEN 0 ELSE 1 END)'; - return (await this.manager - .createQueryBuilder() - .select() - .addSelect('a."name"', "name") - .addSelect('a."namespace"', "namespace") - .addSelect('a."type"', "type") - .addSelect(sumString, "nonCanonicalCount") - .from(Artifact, "a") - .where("a.type IN (:...types)", { types: types }) - .groupBy("1,2,3") - .having(`${sumString} > 0`) - .getRawMany()) as { - name: string; - namespace: string; - type: string; - nonCanonicalCount: number; - }[]; - }, - async duplicates(types: ArtifactType[]) { - return (await this.manager - .createQueryBuilder() - .select() - .addSelect('lower(a."name")', "name") - .addSelect('a."namespace"', "namespace") - .addSelect('a."type"', "type") - .addSelect('count(a."id")', "count") - .addSelect('array_agg(a."id")', "ids") - .addSelect('array_agg(a."name")', "names") - .from(Artifact, "a") - .where("a.type IN (:...types)", { types: types }) - .groupBy("1,2,3") - .having('count(a."id") > 1') - .getRawMany()) as { - name: string; - namespace: string; - type: string; - count: number; - ids: number[]; - names: string[]; - }[]; - }, - async mostFrequentContributors(range: Range, eventTypes: EventType[]) { - const response = (await this.manager - .createQueryBuilder() - .select() - .addSelect("event.from", "contributorId") - .addSelect("COUNT(event.from)", "count") - .addSelect("artifact.name", "contributorName") - .addSelect("artifact.namespace", "contributorNamespace") - .from(Event, "event") - .innerJoin("event.from", "artifact") - .where("event.type = (:...types)", { types: eventTypes }) - .andWhere("event.time >= :startDate", { - startDate: range.startDate.toUTC().toSQL(), - }) - .andWhere("event.time < :endDate", { - endDate: range.endDate.toUTC().toSQL(), - }) - .addGroupBy("event.from") - .addGroupBy("artifact.name") - .addGroupBy("artifact.namespace") - .having("COUNT(event.from) > 3") - .orderBy("count", "DESC") - .getRawMany()) as Array<{ - contributorId: number; - count: number; - contributorName: string; - contributorNamespace: string; - }>; - return response; - }, -}); - -/* -export function streamFindAll( - prisma: PrismaClient, - batchSize: number, - where: Prisma.ArtifactWhereInput, -): Readable { - let cursorId: number | undefined = undefined; - - // Lazily stolen from: https://github.com/prisma/prisma/issues/5055 - return new Readable({ - objectMode: true, - highWaterMark: batchSize, - async read() { - try { - const items = await prisma.artifact.findMany({ - where: where, - take: batchSize, - skip: cursorId ? 1 : 0, - cursor: cursorId ? { id: cursorId } : undefined, - }); - for (const item of items) { - this.push(item); - } - if (items.length < batchSize) { - this.push(null); - return; - } - const item = items[items.length - 1]; - cursorId = item.id; - } catch (err) { - this.destroy(err as any); - } - }, - }); -} -*/ diff --git a/indexer/src/db/collection.ts b/indexer/src/db/collection.ts deleted file mode 100644 index 827401ddc..000000000 --- a/indexer/src/db/collection.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { AppDataSource } from "./data-source.js"; -import { Collection } from "./orm-entities.js"; - -export const CollectionRepository = AppDataSource.getRepository( - Collection, -).extend({ - // Find any duplicates - async duplicates() { - return (await this.manager - .createQueryBuilder() - .select() - .addSelect('lower(c."name")', "name") - .addSelect('lower(c."slug")', "slug") - .addSelect('count(c."id")', "count") - .addSelect('array_agg(c."id")', "ids") - .addSelect('array_agg(c."name")', "names") - .from(Collection, "c") - .groupBy("1,2") - .having('count(c."id") > 1') - .getRawMany()) as { - name: string; - slug: string; - count: number; - ids: number[]; - names: string[]; - }[]; - }, - async nonCanonical() { - const sumString = - 'SUM(CASE WHEN LOWER(c."slug") = c."slug" THEN 0 ELSE 1 END)'; - return (await this.manager - .createQueryBuilder() - .select() - .addSelect('c."name"', "name") - .addSelect('c."slug"', "slug") - .addSelect(sumString, "nonCanonicalCount") - .from(Collection, "c") - .groupBy("1,2") - .having(`${sumString} > 0`) - .getRawMany()) as { - name: string; - slug: string; - nonCanonicalCount: number; - }[]; - }, -}); diff --git a/indexer/src/db/data-source.ts b/indexer/src/db/data-source.ts deleted file mode 100644 index c8ab7b169..000000000 --- a/indexer/src/db/data-source.ts +++ /dev/null @@ -1,189 +0,0 @@ -import "reflect-metadata"; -import path from "path"; -import { fileURLToPath } from "url"; -import { DataSource, DataSourceOptions, LoggerOptions } from "typeorm"; -import _ from "lodash"; - -import { - DB_DATABASE, - DB_HOST, - DB_PASSWORD, - DB_PORT, - DB_USER, - DEBUG_DB, - NO_DYNAMIC_LOADS, - DB_APPLICATION_NAME, - DB_SYNCHRONIZE, -} from "../config.js"; -import { - Artifact, - CollectionType, - Collection, - Project, - Event, - EventPointer, - EventType, - Recording, - RecorderTempDuplicateEvent, - RecorderTempEvent, - RecorderTempEventArtifact, - Job, - JobGroupLock, - JobExecution, - Log, - RepoDependency, - ProjectPackageDependency, - FirstContributionToProject, - LastContributionToProject, - EventsDailyToArtifact, - EventsWeeklyToArtifact, - EventsMonthlyToArtifact, - EventsDailyToProject, - EventsWeeklyToProject, - EventsMonthlyToProject, - EventsDailyFromArtifact, - EventsWeeklyFromArtifact, - EventsMonthlyFromArtifact, - EventsDailyFromProject, - EventsWeeklyFromProject, - EventsMonthlyFromProject, - EventsDailyToCollection, - EventsWeeklyToCollection, - EventsMonthlyToCollection, -} from "./orm-entities.js"; - -const loggingOption: LoggerOptions = DEBUG_DB ? "all" : false; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -export interface OSODataSourceOptions { - connectionSuffix: string; -} - -const defaultOSODataSourceOptions: OSODataSourceOptions = { - connectionSuffix: "", -}; - -export function dynamicallyLoadedDataSource( - options?: Partial, -): DataSourceOptions { - const opts = _.merge(defaultOSODataSourceOptions, options); - return { - type: "postgres", - host: DB_HOST, - port: parseInt(DB_PORT), - username: DB_USER, - password: DB_PASSWORD, - database: DB_DATABASE, - synchronize: DB_SYNCHRONIZE, - logging: loggingOption, - applicationName: `${DB_APPLICATION_NAME}${opts.connectionSuffix}`, - - entities: [path.resolve(__dirname, "./orm-entities.ts")], - migrations: [path.resolve(__dirname, "./migration/**/*.ts")], - subscribers: [], - }; -} - -/** - * This is wrapped in a function so we can easily use this for testing. - * - * @param databaseName Name of the database to use - * @returns - */ -export function staticDataSourceOptions( - databaseName: string, - options?: Partial, -): DataSourceOptions { - const opts = _.merge(defaultOSODataSourceOptions, options); - return { - type: "postgres", - host: DB_HOST, - port: parseInt(DB_PORT), - username: DB_USER, - password: DB_PASSWORD, - database: databaseName, - synchronize: DB_SYNCHRONIZE, - logging: loggingOption, - applicationName: `${DB_APPLICATION_NAME}${opts.connectionSuffix}`, - - entities: [ - Artifact, - CollectionType, - Collection, - Project, - Event, - EventPointer, - Recording, - RecorderTempDuplicateEvent, - RecorderTempEvent, - RecorderTempEventArtifact, - Job, - JobGroupLock, - JobExecution, - Log, - RepoDependency, - ProjectPackageDependency, - FirstContributionToProject, - LastContributionToProject, - EventsDailyToArtifact, - EventsWeeklyToArtifact, - EventsMonthlyToArtifact, - EventsDailyFromArtifact, - EventsWeeklyFromArtifact, - EventsMonthlyFromArtifact, - EventsDailyToProject, - EventsWeeklyToProject, - EventsMonthlyToProject, - EventsDailyFromProject, - EventsWeeklyFromProject, - EventsMonthlyFromProject, - EventsDailyToCollection, - EventsWeeklyToCollection, - EventsMonthlyToCollection, - EventType, - ], - migrations: [], - subscribers: [], - }; -} - -function testingDataSource(databaseName: string): DataSource { - return new DataSource(staticDataSourceOptions(databaseName)); -} - -/** - * Creates a new app data source and connection - * @param id string used to name the connection - */ -export function createNewAppDataSource(id: string): DataSource { - // Unfortunately, jest seems to error when using this. We cannot use dynamic - // imports. It's possible that this can be fixed in the future but for now this - // is something that is unavoidable. See: - // https://github.com/typeorm/typeorm/issues/10212. This following is a hacky - // solution so that we use dynamic normally and non-dynamic for tests which is - // particularly important for migrations - return new DataSource( - NO_DYNAMIC_LOADS - ? staticDataSourceOptions(DB_DATABASE, { connectionSuffix: `-${id}` }) - : dynamicallyLoadedDataSource({ connectionSuffix: `-${id}` }), - ); -} - -export async function createAndConnectDataSource( - id: string, -): Promise { - const ds = createNewAppDataSource(id); - await ds.initialize(); - return ds; -} - -export const AppDataSource = createNewAppDataSource("default"); - -async function initializeDataSource() { - if (!AppDataSource.isInitialized) { - await AppDataSource.initialize(); - } -} - -export { initializeDataSource, testingDataSource }; diff --git a/indexer/src/db/entities.ts b/indexer/src/db/entities.ts deleted file mode 100644 index 7e42baf18..000000000 --- a/indexer/src/db/entities.ts +++ /dev/null @@ -1,473 +0,0 @@ -import _ from "lodash"; -import { - Artifact, - ArtifactType, - ArtifactNamespace, - Project as DBProject, - Collection as DBCollection, - CollectionType, -} from "./orm-entities.js"; -import { Project, Collection, URL, BlockchainAddress } from "oss-directory"; -import { logger } from "../utils/logger.js"; -import { - isGitHubOrg, - isGitHubRepo, - parseGitHubUrl, - parseNpmUrl, -} from "../utils/parsing.js"; -import { getNpmUrl } from "../utils/format.js"; -import { safeCast, ensure, filterFalsy } from "../utils/common.js"; -import { getOwnerRepos } from "../collectors/github/get-org-repos.js"; -import { ProjectRepository } from "./project.js"; -import { CollectionRepository } from "./collection.js"; -import { In } from "typeorm"; -import { ArtifactRepository } from "./artifacts.js"; -import { AppDataSource } from "./data-source.js"; - -/** - * Upsert a Collection from oss-directory - * Pre-condition: all of the project slugs need to already be in the database - * @param ossCollection - */ -async function ossUpsertCollection(ossCollection: Collection) { - const { slug, name, projects: projectSlugs } = ossCollection; - - // Get all of the projects - const projects = await ProjectRepository.find({ - where: { - slug: In(projectSlugs), - }, - }); - - // Check that all Projects are already in the database - if (projects.length != projectSlugs.length) { - logger.warn( - `Not all of the projects for collection ${slug} are in the database. Please add all projects first`, - ); - } - // Get collection type OSS_DIRECTORY - const collectionType = await AppDataSource.getRepository( - CollectionType, - ).findOneOrFail({ - where: { - name: "OSS_DIRECTORY", - }, - }); - - // Upsert into the database - return await AppDataSource.transaction(async (manager) => { - const collectionResult = await manager - .withRepository(CollectionRepository) - .upsert( - { - name: name, - slug: slug, - type: collectionType, - }, - ["slug"], - ); - - // Manage all the relations for each of the projects - const collection = await manager.getRepository(DBCollection).findOneOrFail({ - relations: { - projects: true, - }, - where: { - id: collectionResult.identifiers[0].id, - }, - }); - - const currentProjectIds = collection.projects.map((p) => p.id); - const intendedProjectIds = projects.map((p) => p.id); - - // intended - current = new - const newProjectIds = _.difference(intendedProjectIds, currentProjectIds); - - // current - intended = removed - const removedProjectIds = _.difference( - currentProjectIds, - intendedProjectIds, - ); - - // Add new - await manager - .createQueryBuilder() - .relation(DBCollection, "projects") - .of(collection.id) - .add(newProjectIds); - - // Delete removed - await manager - .createQueryBuilder() - .relation(DBCollection, "projects") - .of(collection.id) - .remove(removedProjectIds); - - return { - added: newProjectIds.length, - removed: removedProjectIds.length, - new: intendedProjectIds.length, - old: currentProjectIds.length, - ids: intendedProjectIds, - collection: collection, - }; - }); -} - -/** - * Upsert a Project from oss-directory - * @param ossProj - * @returns - */ -async function ossUpsertProject(ossProj: Project) { - const { slug, name, github, npm, blockchain } = ossProj; - - // Create all of the missing artifacts first - // Note: this will only create missing artifacts, not update existing ones - const artifacts = [ - // Create GitHub artifacts - ...(await ossCreateGitHubArtifacts(github)), - // Create npm artifacts - ...(await ossCreateNpmArtifacts(npm)), - // Create Optimism artifacts - ...(await ossCreateBlockchainArtifacts(blockchain)), - ]; - - // Then upsert the project with the relations - return await AppDataSource.transaction(async (manager) => { - const repo = manager.withRepository(ProjectRepository); - const projectResult = await repo.upsert( - [ - { - slug: slug, - name: name, - }, - ], - ["slug"], - ); - - const project = await repo.findOneOrFail({ - relations: { - artifacts: true, - }, - where: { - id: projectResult.identifiers[0].id, - }, - }); - - const currentArtifactIds = project.artifacts.map((a) => a.id); - const intendedArtifactIds = artifacts.map((a) => a.id); - - const newArtifactIds = _.difference( - intendedArtifactIds, - currentArtifactIds, - ); - const removedArtifactIds = _.difference( - currentArtifactIds, - intendedArtifactIds, - ); - - // Update all artifact relations - await manager - .createQueryBuilder() - .relation(DBProject, "artifacts") - .of(project.id) - .add(newArtifactIds); - - // Delete removed artifacts - await manager - .createQueryBuilder() - .relation(DBProject, "artifacts") - .of(project.id) - .remove(removedArtifactIds); - - return { - old: intendedArtifactIds.length, - new: currentArtifactIds.length, - added: newArtifactIds.length, - removed: removedArtifactIds.length, - ids: intendedArtifactIds, - project: project, - }; - }); - - // FIXME this needs to be added back in. - // Remove any artifact relations that are no longer valid - /* - await prisma.projectsOnArtifacts.deleteMany({ - where: { - projectId: project.id, - artifactId: { - notIn: artifacts.map((a) => a.id), - }, - }, - }); - */ - - //return project; -} - -/** - * Upsert a GitHub resource from oss-directory - */ -async function ossCreateGitHubArtifacts( - urlObjects?: URL[], -): Promise { - if (!urlObjects) { - return safeCast([]); - } - - const urls = urlObjects.map((o) => o.url); - const repoUrls = urls.filter(isGitHubRepo); - const orgUrls = urls.filter(isGitHubOrg); - - // Check for invalid URLs - if (orgUrls.length + repoUrls.length !== urls.length) { - const nonConfirmingUrls = urls.filter( - (u) => !isGitHubOrg(u) && !isGitHubRepo(u), - ); - const sep = "\n\t"; - logger.warn(`Invalid GitHub URLs:${sep}${nonConfirmingUrls.join(sep)}`); - } - - // Flatten all GitHub orgs - const orgNames = filterFalsy( - orgUrls.map(parseGitHubUrl).map((p) => p?.owner), - ); - const orgRepos = _.flatten(await Promise.all(orgNames.map(getOwnerRepos))); - const orgRepoUrls = orgRepos.filter((r) => !r.isFork).map((r) => r.url); - const allRepos = [...repoUrls, ...orgRepoUrls]; - const parsedRepos = allRepos.map(parseGitHubUrl); - const data = parsedRepos.map((p) => ({ - type: ArtifactType.GIT_REPOSITORY, - namespace: ArtifactNamespace.GITHUB, - name: ensure( - p?.slug.toLowerCase(), - `Invalid parsed GitHub URL: ${p}`, - ), - url: ensure( - p?.url.toLowerCase(), - `Invalid parsed GitHub URL: ${p}`, - ), - })); - const slugs = filterFalsy(parsedRepos.map((p) => p?.slug.toLowerCase())); - - const newArtifacts = Artifact.create(data); - logger.debug("Upserting artifacts"); - // Create records - const result = await ArtifactRepository.upsertMany(newArtifacts); - const createCount = result.identifiers.length; - - logger.debug( - `... created ${createCount}/${data.length} GitHub artifacts (skip duplicates)`, - ); - - // Now get all of the artifacts - const artifacts = ArtifactRepository.find({ - where: { - type: ArtifactType.GIT_REPOSITORY, - namespace: ArtifactNamespace.GITHUB, - name: In(slugs), - }, - }); - return artifacts; -} - -/** - * Upsert an npm artifact from oss-directory - * @param ossUrl - * @returns - */ -async function ossCreateNpmArtifacts(urlObjects?: URL[]) { - if (!urlObjects) { - return safeCast([]); - } - - const urls = urlObjects.map((o) => o.url); - const parsed = urls.map(parseNpmUrl); - const data = parsed.map((p) => ({ - type: ArtifactType.NPM_PACKAGE, - namespace: ArtifactNamespace.NPM_REGISTRY, - name: ensure(p?.slug.toLowerCase(), `Invalid parsed npm URL: ${p}`), - url: ensure(p?.url, `Invalid parsed npm URL: ${p}`), - })); - const slugs = filterFalsy(parsed.map((p) => p?.slug.toLowerCase())); - - // Create records - const result = await ArtifactRepository.upsertMany(data); - const createCount = result.identifiers.length; - logger.debug( - `... inserted ${createCount}/${data.length} npm artifacts (skip duplicates)`, - ); - - // Now get all of the artifacts - const artifacts = await ArtifactRepository.find({ - where: { - type: ArtifactType.NPM_PACKAGE, - namespace: ArtifactNamespace.NPM_REGISTRY, - name: In(slugs), - }, - }); - return artifacts; -} - -/** - * Upsert a blockchain address artifact from oss-directory - * @param ossAddr - * @param artifactNamespace - * @returns - */ -async function ossCreateBlockchainArtifacts(addrObjects?: BlockchainAddress[]) { - if (!addrObjects) { - return safeCast([]); - } - - const typeMap: { [key: string]: ArtifactType } = { - eoa: ArtifactType.EOA_ADDRESS, - safe: ArtifactType.SAFE_ADDRESS, - factory: ArtifactType.FACTORY_ADDRESS, - contract: ArtifactType.CONTRACT_ADDRESS, - }; - const typeResolutionOrder = ["eoa", "safe", "factory", "contract"]; - const typeResolver = (tags: string[]) => { - for (const type of typeResolutionOrder) { - if (tags.indexOf(type) !== -1) { - return typeMap[type]; - } - } - return typeMap["eoa"]; - }; - - const data = addrObjects.flatMap((o) => { - const address = o.address.toLowerCase(); - return o.networks.map((network) => { - return { - type: typeResolver(o.tags), - // Hacky solution for now. We should we address after the typeorm migration - namespace: - network === "optimism" - ? ArtifactNamespace.OPTIMISM - : ArtifactNamespace.ETHEREUM, - // Normalize the addresses to lowercase - name: address, - // etherscan url - url: - network === "optimism" - ? `https://optimistic.etherscan.io/address/${address}` - : `https://etherscan.io/address/${address}`, - }; - }); - }); - const addresses = data.map((d) => d.name); - - // Create records - const result = await ArtifactRepository.upsertMany(data); - const createCount = result.identifiers.length; - logger.debug( - `... inserted ${createCount}/${data.length} blockchain artifacts (skip duplicates)`, - ); - - // Now get all of the artifacts - const artifacts = await ArtifactRepository.find({ - where: { - name: In(addresses), - }, - }); - return artifacts; -} - -/** - * Gets a collection by a slug - * @param slug - * @returns - */ -async function getCollectionBySlug(slug: string) { - return await CollectionRepository.findOne({ where: { slug } }); -} - -/** - * Gets a project by a slug - * @param slug - * @returns - */ -async function getProjectBySlug(slug: string) { - return await ProjectRepository.findOne({ where: { slug } }); -} - -/** - * Gets an artifact by name - * @param fields - * @returns - */ -async function getArtifactByName(fields: { - namespace: ArtifactNamespace; - name: string; -}): Promise { - const { namespace, name } = fields; - const result = await ArtifactRepository.findOne({ - where: { - namespace: namespace, - name: name, - }, - }); - return result; -} - -/** - * Generic upsert for an artifact - * @param address - * @param type - * @param ns - * @returns - */ -async function upsertArtifact(fields: { - namespace: ArtifactNamespace; - type: ArtifactType; - name: string; - url?: string; - details?: any; -}) { - return await ArtifactRepository.upsert( - [{ ...fields }], - ["name", "namespace"], - ); -} - -/** - * Upsert a GitHub repo artifact - * @param slug - * @param url - * @returns - */ -async function upsertGitHubRepo(slug: string, url: string) { - return await upsertArtifact({ - type: ArtifactType.GIT_REPOSITORY, - namespace: ArtifactNamespace.GITHUB, - name: slug.toLowerCase(), - url, - }); -} - -/** - * Upsert an npm package artifact - * @param packageName - * @returns - */ -async function upsertNpmPackage(packageName: string) { - return await upsertArtifact({ - type: ArtifactType.NPM_PACKAGE, - namespace: ArtifactNamespace.NPM_REGISTRY, - name: packageName, - url: getNpmUrl(packageName), - }); -} - -export { - getCollectionBySlug, - getProjectBySlug, - getArtifactByName, - ossUpsertProject, - ossUpsertCollection, - upsertGitHubRepo, - upsertNpmPackage, -}; diff --git a/indexer/src/db/events.ts b/indexer/src/db/events.ts deleted file mode 100644 index 7fe489950..000000000 --- a/indexer/src/db/events.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { - And, - DeepPartial, - FindOperator, - FindOptionsWhere, - In, - LessThanOrEqual, - MoreThanOrEqual, -} from "typeorm"; -import { normalizeToObject } from "../utils/common.js"; -import { AppDataSource } from "./data-source.js"; -import type { Brand } from "utility-types"; -import { EventPointer, Event, EventType } from "./orm-entities.js"; -import { Range } from "../utils/ranges.js"; -import { DateTime } from "luxon"; -import { logger } from "../utils/logger.js"; -import { GenericError } from "../common/errors.js"; - -export const EventPointerRepository = AppDataSource.getRepository( - EventPointer, -).extend({ - async getPointer( - artifactId: Brand, - collector: string, - ) { - const record = await this.findOne({ - relations: { - artifact: true, - }, - where: { - artifact: { - id: artifactId, - }, - collector: collector, - }, - }); - if (!record) { - return null; - } - // Safe because `Partial` means they're all optional props anyway - return normalizeToObject(record); - }, -}); - -export type EventTypeRef = Pick; - -export type EventRef = Pick; - -export type BulkUpdateBySourceIDEvent = DeepPartial & - Pick; - -export class BulkUpdateError extends GenericError {} -class BulkUpdateDeletionRecordsMismatch extends GenericError {} - -export const EventRepository = AppDataSource.getRepository(Event).extend({ - // Mostly used for testing - // async findByTypeWithArtifacts(type: EventTypeEnum, addWhere?: FindOptionsWhere) { - // const where: FindOptionsWhere = { - // } - - // return this.find({ - // where: where - // }) - // }, - async bulkUpdateBySourceIDAndType(events: BulkUpdateBySourceIDEvent[]) { - // Ensure all have the same event type - if (events.length === 0) { - return []; - } - - const summary = events.reduce<{ - types: Record; - sourceIds: string[]; - range: Range; - }>( - (summ, event) => { - const time = DateTime.fromJSDate(event.time); - if (time < summ.range.startDate) { - summ.range.startDate = time; - } - if (time > summ.range.endDate) { - summ.range.endDate = time; - } - const eventTypeKey = `${event.typeId}:${event.typeId}`; - if (!summ.types[eventTypeKey]) { - summ.types[eventTypeKey] = true; - } - summ.sourceIds.push(event.sourceId); - return summ; - }, - { - types: {}, - sourceIds: [], - range: { - startDate: DateTime.fromJSDate(events[0].time), - endDate: DateTime.fromJSDate(events[0].time), - }, - }, - ); - if (Object.keys(summary.types).length !== 1) { - throw new BulkUpdateError( - "bulk update requires event types have the same type", - ); - } - - const updateTransaction = async ( - queryRange: FindOperator | undefined, - ) => { - return await this.manager.transaction(async (manager) => { - const repo = manager.withRepository(this); - - // Delete all of the events within the specific time range and with the specific sourceIds - logger.debug(`deleting ${events.length} event(s) in transaction`); - const whereOptions: FindOptionsWhere = { - sourceId: In(summary.sourceIds), - typeId: events[0].typeId, - }; - if (queryRange) { - whereOptions.time = queryRange; - } - const deleteResult = await repo.delete(whereOptions); - if (!deleteResult.affected) { - logger.debug(`deletion affected no rows. that is not expected`); - throw new BulkUpdateError( - "the deletion should have effected the expected number of rows. no deletions recorded", - ); - } - if (deleteResult.affected < summary.sourceIds.length) { - logger.debug( - `deletion affected ${deleteResult.affected} rows. expected to delete ${summary.sourceIds.length}`, - ); - logger.debug(`deleted with ${whereOptions}`); - throw new BulkUpdateDeletionRecordsMismatch( - `the deletion should have effected ${summary.sourceIds.length}. Only deleted ${deleteResult.affected}`, - ); - } - - logger.debug(`reinserting ${events.length} event(s) in transaction`); - return await repo.insert(events); - }); - }; - - // All a retry with the range Give a larger range in case event dates - // changed (either we got it wrong our our definitions or data changed). - // There's retry logic to allow for more but this should hopefully cover 95% - // of cases - let rangeToUse: FindOperator | undefined = And( - MoreThanOrEqual(summary.range.startDate.minus({ months: 3 }).toJSDate()), - LessThanOrEqual(summary.range.endDate.plus({ months: 3 }).toJSDate()), - ); - for (let i = 0; i < 2; i++) { - try { - return await updateTransaction(rangeToUse); - } catch (err) { - if (err instanceof BulkUpdateDeletionRecordsMismatch) { - logger.debug( - "failed to do bulk update due to unexpected affected records. retrying without time range constraint", - ); - rangeToUse = undefined; - } else { - throw err; - } - } - } - throw new BulkUpdateError( - `failed to update ${events.length} existing event(s)`, - ); - }, -}); diff --git a/indexer/src/db/jobs.test.ts b/indexer/src/db/jobs.test.ts deleted file mode 100644 index 3ce8ef757..000000000 --- a/indexer/src/db/jobs.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { DateTime } from "luxon"; -import { - GroupAlreadyActive, - JobAlreadyActive, - JobExecutionRepository, - JobsRepository, -} from "./jobs.js"; -import { withDbDescribe } from "./testing.js"; - -withDbDescribe("Jobs", () => { - it("should create non-backfill jobs", async () => { - const job0 = await JobsRepository.queueJob( - "collector0", - null, - DateTime.now().startOf("day"), - false, - ); - const job1 = await JobsRepository.queueJob( - "collector1", - "group0", - DateTime.now().startOf("day"), - false, - ); - const job2 = await JobsRepository.queueJob( - "collector2", - "group1", - DateTime.now().startOf("day"), - false, - ); - const job3 = await JobsRepository.queueJob( - "collector3", - "group1", - DateTime.now().startOf("day"), - false, - ); - await JobsRepository.queueJob( - "collector4", - null, - DateTime.now().startOf("day"), - false, - ); - - let availableJobs = await JobsRepository.availableJobGroups(); - expect(availableJobs.length).toBe(4); - - await JobExecutionRepository.createExecutionForJob(job0); - - availableJobs = await JobsRepository.availableJobGroups(); - expect(availableJobs.length).toBe(3); - - await expect(() => { - return JobExecutionRepository.createExecutionForJob(job0); - }).rejects.toThrow(JobAlreadyActive); - - await JobExecutionRepository.createExecutionForJob(job1); - - await JobExecutionRepository.createExecutionForJob(job2); - - await expect(() => { - return JobExecutionRepository.createExecutionForJob(job3); - }).rejects.toThrow(GroupAlreadyActive); - }); -}); diff --git a/indexer/src/db/jobs.ts b/indexer/src/db/jobs.ts deleted file mode 100644 index 671ac1c22..000000000 --- a/indexer/src/db/jobs.ts +++ /dev/null @@ -1,292 +0,0 @@ -import { randomUUID } from "crypto"; -import { GenericError } from "../common/errors.js"; -import { AppDataSource } from "./data-source.js"; -import { - Job, - JobStatus, - JobExecutionStatus, - JobExecution, - JobGroupLock, -} from "./orm-entities.js"; -import { DateTime } from "luxon"; -import { QueryFailedError } from "typeorm"; -import type { Brand } from "utility-types"; - -export class JobAlreadyActive extends GenericError {} -export class JobAlreadyQueued extends GenericError {} -export class GroupAlreadyActive extends GenericError {} - -export type JobGrouping = { - name: string; - jobs: T[]; -}; - -export type JobRef = { - id: number; - group: { - id?: number; - name?: string; - }; - collector: string; -}; - -export function jobHasActiveExecution(job: Job) { - if (job.executions.length == 0) { - return false; - } - return job.executions.reduce((hasActiveExec, exec) => { - if (hasActiveExec) { - return true; - } - return exec.status == JobExecutionStatus.ACTIVE; - }, false); -} - -export function jobsHaveActiveExecution(jobs: Job[]) { - return jobs.reduce((hasActive, current) => { - if (hasActive) { - return true; - } - return jobHasActiveExecution(current); - }, false); -} - -function queryFailedErrorWithDuplicate(err: unknown): boolean { - if (err instanceof QueryFailedError) { - if ((err as QueryFailedError).message.indexOf("duplicate") !== -1) { - return true; - } - } - return false; -} - -/** - * Jobs repository - */ -export const JobsRepository = AppDataSource.getRepository(Job).extend({ - async queueJob( - collector: string, - group: string | null, - scheduledTime: DateTime, - backfill: boolean, - options?: Record, - ): Promise { - let scheduleType = "main"; - if (backfill) { - scheduleType = `backfill-${randomUUID()}`; - } - const job = this.create({ - group: group, - scheduledTime: scheduledTime.toJSDate(), - scheduleType: scheduleType, - collector: collector, - status: JobStatus.PENDING, - options: options, - }); - try { - const result = await this.insert(job); - return await this.findOneOrFail({ - relations: { - executions: true, - }, - where: { - id: result.identifiers[0].id, - }, - }); - } catch (err) { - if (queryFailedErrorWithDuplicate(err)) { - throw new JobAlreadyQueued( - `Job already queued for this collector at this time`, - ); - } - throw err; - } - }, - async availableJobGroups(): Promise[]> { - const allIncompleteJobs = await this.find({ - relations: { - executions: true, - }, - where: { - status: JobStatus.PENDING, - }, - }); - const groupedJobsByName = allIncompleteJobs.reduce< - Record> - >((groups, job) => { - const group = job.group || "none"; - const groupStorage = groups[group] || []; - groupStorage.push(job); - groups[group] = groupStorage; - return groups; - }, {}); - const jobGroups: JobGrouping[] = []; - - for (const name in groupedJobsByName) { - const jobs = groupedJobsByName[name]; - - if (name == "none") { - jobs.forEach((j) => { - if (!jobHasActiveExecution(j)) { - jobGroups.push({ - name: "none", - jobs: [j], - }); - } - }); - } else { - if (!jobsHaveActiveExecution(jobs)) { - jobGroups.push({ - name: name, - jobs: jobs, - }); - } - } - } - return jobGroups; - }, -}); - -export const JobExecutionRepository = AppDataSource.getRepository( - JobExecution, -).extend({ - /** - * Call this to establish a lock on a job's execution - * - * @param ref JobRef for the job to create an execution for - * @returns - */ - async createExecutionForJob(job: Job) { - const hasActive = job.executions.reduce((hasActive, current) => { - if (hasActive) { - return true; - } - return current.status === JobExecutionStatus.ACTIVE; - }, false); - - if (hasActive) { - throw new JobAlreadyActive( - `Job[${job.id}] running "${job.collector}" already has an active execution`, - ); - } - - const attempt = job.executions.length; - - return this.manager.transaction(async (manager) => { - const repo = manager.withRepository(this); - const groupLockRepo = manager.getRepository(JobGroupLock); - - if (job.group) { - const group = await groupLockRepo.create({ - name: job.group, - }); - try { - // This will fail if there's already a lock for the group - await groupLockRepo.insert(group); - } catch (err) { - if (err instanceof QueryFailedError) { - if ((err as QueryFailedError).message.indexOf("duplicate") === -1) { - throw err; - } - throw new GroupAlreadyActive(`group "${group}" already active`); - } - } - } - - try { - const created = await repo.insert( - repo.create({ - status: JobExecutionStatus.ACTIVE, - job: job, - attempt: attempt, - }), - ); - return repo.findOneOrFail({ - relations: { - job: true, - }, - where: { - id: created.identifiers[0].id, - }, - }); - } catch (err) { - if (err instanceof QueryFailedError) { - if ((err as QueryFailedError).message.indexOf("duplicate") === -1) { - throw err; - } - throw new JobAlreadyActive( - `Job[${job.id}] running "${job.collector}" already has an active execution`, - ); - } - } - }); - }, - - async updateExecutionStatusById(id: number, status: JobExecutionStatus) { - const execution = await this.findOneOrFail({ - relations: { - job: true, - }, - where: { - id: id as Brand, - }, - }); - return this.updateExecutionStatus(execution, status); - }, - - async updateExecutionStatus(exec: JobExecution, status: JobExecutionStatus) { - return this.manager.transaction(async (manager) => { - const jobRepo = manager.getRepository(Job); - const repo = manager.withRepository(this); - const groupLockRepo = manager.getRepository(JobGroupLock); - - const job = await jobRepo.findOneOrFail({ - where: { - id: exec.job.id, - }, - }); - - if (status === JobExecutionStatus.COMPLETE) { - // Also mark the job as complete if we've completed this job successfully. - await jobRepo.update( - { - id: job.id, - version: job.version, - }, - { - status: JobStatus.COMPLETE, - }, - ); - } - - if (job.group) { - if ( - status === JobExecutionStatus.FAILED || - status === JobExecutionStatus.COMPLETE - ) { - // Remove any group lock that might exist - const lock = await groupLockRepo.findOne({ - where: { - name: job.group, - }, - }); - if (lock) { - await groupLockRepo.delete({ id: lock.id }); - } - } - } - - // Update the status - return repo.update( - { - id: exec.id, - version: exec.version, - }, - { - status: status, - version: exec.version + 1, - }, - ); - }); - }, -}); diff --git a/indexer/src/db/migration/1695752959358-create.ts b/indexer/src/db/migration/1695752959358-create.ts deleted file mode 100644 index a0b89d8d3..000000000 --- a/indexer/src/db/migration/1695752959358-create.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class Create1695752959358 implements MigrationInterface { - name = "Create1695752959358"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TYPE "public"."artifact_type_enum" AS ENUM('EOA_ADDRESS', 'SAFE_ADDRESS', 'CONTRACT_ADDRESS', 'FACTORY_ADDRESS', 'GIT_REPOSITORY', 'GIT_EMAIL', 'GIT_NAME', 'GITHUB_ORG', 'GITHUB_USER', 'NPM_PACKAGE')`, - ); - await queryRunner.query( - `CREATE TYPE "public"."artifact_namespace_enum" AS ENUM('ETHEREUM', 'OPTIMISM', 'GOERLI', 'GITHUB', 'GITLAB', 'NPM_REGISTRY')`, - ); - await queryRunner.query( - `CREATE TYPE "public"."event_type_enum" AS ENUM('FUNDING', 'PULL_REQUEST_CREATED', 'PULL_REQUEST_MERGED', 'COMMIT_CODE', 'ISSUE_FILED', 'ISSUE_CLOSED', 'DOWNSTREAM_DEPENDENCY_COUNT', 'UPSTREAM_DEPENDENCY_COUNT', 'DOWNLOADS', 'CONTRACT_INVOKED', 'USERS_INTERACTED', 'CONTRACT_INVOKED_AGGREGATE_STATS', 'PULL_REQUEST_CLOSED', 'STAR_AGGREGATE_STATS', 'PULL_REQUEST_REOPENED', 'PULL_REQUEST_REMOVED_FROM_PROJECT', 'PULL_REQUEST_APPROVED', 'ISSUE_CREATED', 'ISSUE_REOPENED', 'ISSUE_REMOVED_FROM_PROJECT', 'STARRED', 'FORK_AGGREGATE_STATS', 'FORKED', 'WATCHER_AGGREGATE_STATS')`, - ); - await queryRunner.query( - `CREATE TYPE "public"."job_status_enum" AS ENUM('PENDING', 'COMPLETE')`, - ); - await queryRunner.query( - `CREATE TYPE "public"."job_execution_status_enum" AS ENUM('ACTIVE', 'COMPLETE', 'FAILED')`, - ); - - await queryRunner.query( - `CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE`, - ); - await queryRunner.query( - `CREATE TABLE "collection" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "name" text NOT NULL, "description" text, "verified" boolean NOT NULL DEFAULT false, "slug" text NOT NULL, CONSTRAINT "UQ_75a6fd6eedd7fa7378de400b0aa" UNIQUE ("slug"), CONSTRAINT "PK_ad3f485bbc99d875491f44d7c85" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE TABLE "project" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "name" text NOT NULL, "description" text, "verified" boolean NOT NULL DEFAULT false, "slug" text NOT NULL, CONSTRAINT "UQ_6fce32ddd71197807027be6ad38" UNIQUE ("slug"), CONSTRAINT "PK_4d68b1358bb5b766d3e78f32f57" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE TABLE "artifact" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "type" "public"."artifact_type_enum" NOT NULL, "namespace" "public"."artifact_namespace_enum" NOT NULL, "name" text NOT NULL, "url" text, CONSTRAINT "PK_1f238d1d4ef8f85d0c0b8616fa3" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_1b59a64c744d72143d2b96b2c7" ON "artifact" ("namespace", "name") `, - ); - await queryRunner.query( - `CREATE TABLE "event" ("id" SERIAL NOT NULL, "sourceId" text NOT NULL, "type" "public"."event_type_enum" NOT NULL, "time" TIMESTAMP WITH TIME ZONE NOT NULL, "amount" double precision NOT NULL, "toId" integer, "fromId" integer, CONSTRAINT "PK_82ff22c1314984ac7b8cade26a6" PRIMARY KEY ("id", "time"))`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_8eb34e6d33033e17e91776c1c2" ON "event" ("sourceId", "time") `, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_82ff22c1314984ac7b8cade26a" ON "event" ("id", "time") `, - ); - await queryRunner.query( - `CREATE INDEX "IDX_46f304d2b1e02a2ce46d43403e" ON "event" ("time") `, - ); - await queryRunner.query( - `CREATE TABLE "event_pointer" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "collector" text NOT NULL, "startDate" TIMESTAMP WITH TIME ZONE NOT NULL, "endDate" TIMESTAMP WITH TIME ZONE NOT NULL, "version" integer NOT NULL, "artifactId" integer, CONSTRAINT "PK_602eaa0f5966ca040d2835d8cd9" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_3d92caafdc685cc487ce201b49" ON "event_pointer" ("artifactId", "collector", "startDate", "endDate") `, - ); - await queryRunner.query( - `CREATE TABLE "job" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "execGroup" text NOT NULL, "scheduledTime" TIMESTAMP WITH TIME ZONE NOT NULL, "collector" text NOT NULL, "status" "public"."job_status_enum" NOT NULL, CONSTRAINT "PK_98ab1c14ff8d1cf80d18703b92f" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE TABLE "job_execution" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "status" "public"."job_execution_status_enum" NOT NULL, "jobId" integer, CONSTRAINT "PK_81e54343e6d62f09a166d105792" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE TABLE "log" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "level" text NOT NULL, "body" jsonb NOT NULL, "executionId" integer, CONSTRAINT "PK_350604cbdf991d5930d9e618fbd" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE TABLE "collection_projects_project" ("collectionId" integer NOT NULL, "projectId" integer NOT NULL, CONSTRAINT "PK_4d933f2f3a834897250ca67fb20" PRIMARY KEY ("collectionId", "projectId"))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_44350998a09c6306a61f6854d2" ON "collection_projects_project" ("collectionId") `, - ); - await queryRunner.query( - `CREATE INDEX "IDX_d38703155092b0b8b0779b43dd" ON "collection_projects_project" ("projectId") `, - ); - await queryRunner.query( - `CREATE TABLE "project_artifacts_artifact" ("projectId" integer NOT NULL, "artifactId" integer NOT NULL, CONSTRAINT "PK_c0174962d1f020db7458cf5810a" PRIMARY KEY ("projectId", "artifactId"))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_baaddcf9097758446307cd9825" ON "project_artifacts_artifact" ("projectId") `, - ); - await queryRunner.query( - `CREATE INDEX "IDX_fad8b476b66052b4e2a5653cf5" ON "project_artifacts_artifact" ("artifactId") `, - ); - await queryRunner.query( - `ALTER TABLE "event" ADD CONSTRAINT "FK_404b3d263eafc41aae2044e9b85" FOREIGN KEY ("toId") REFERENCES "artifact"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "event" ADD CONSTRAINT "FK_b36ab188856dd8cf3d6c7ec4f48" FOREIGN KEY ("fromId") REFERENCES "artifact"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "event_pointer" ADD CONSTRAINT "FK_afa2cb33377e4c90a7c6d6a3a66" FOREIGN KEY ("artifactId") REFERENCES "artifact"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "job_execution" ADD CONSTRAINT "FK_6f342ebe2b66a1c613516efa7db" FOREIGN KEY ("jobId") REFERENCES "job"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "log" ADD CONSTRAINT "FK_d4bef9bfb811b441cb8ecb115eb" FOREIGN KEY ("executionId") REFERENCES "job_execution"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "collection_projects_project" ADD CONSTRAINT "FK_44350998a09c6306a61f6854d23" FOREIGN KEY ("collectionId") REFERENCES "collection"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - await queryRunner.query( - `ALTER TABLE "collection_projects_project" ADD CONSTRAINT "FK_d38703155092b0b8b0779b43dd3" FOREIGN KEY ("projectId") REFERENCES "project"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "project_artifacts_artifact" ADD CONSTRAINT "FK_baaddcf9097758446307cd9825d" FOREIGN KEY ("projectId") REFERENCES "project"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - await queryRunner.query( - `ALTER TABLE "project_artifacts_artifact" ADD CONSTRAINT "FK_fad8b476b66052b4e2a5653cf5f" FOREIGN KEY ("artifactId") REFERENCES "artifact"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query(`SELECT create_hypertable('event', 'time')`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "project_artifacts_artifact" DROP CONSTRAINT "FK_fad8b476b66052b4e2a5653cf5f"`, - ); - await queryRunner.query( - `ALTER TABLE "project_artifacts_artifact" DROP CONSTRAINT "FK_baaddcf9097758446307cd9825d"`, - ); - await queryRunner.query( - `ALTER TABLE "collection_projects_project" DROP CONSTRAINT "FK_d38703155092b0b8b0779b43dd3"`, - ); - await queryRunner.query( - `ALTER TABLE "collection_projects_project" DROP CONSTRAINT "FK_44350998a09c6306a61f6854d23"`, - ); - await queryRunner.query( - `ALTER TABLE "log" DROP CONSTRAINT "FK_d4bef9bfb811b441cb8ecb115eb"`, - ); - await queryRunner.query( - `ALTER TABLE "job_execution" DROP CONSTRAINT "FK_6f342ebe2b66a1c613516efa7db"`, - ); - await queryRunner.query( - `ALTER TABLE "event_pointer" DROP CONSTRAINT "FK_afa2cb33377e4c90a7c6d6a3a66"`, - ); - await queryRunner.query( - `ALTER TABLE "event" DROP CONSTRAINT "FK_b36ab188856dd8cf3d6c7ec4f48"`, - ); - await queryRunner.query( - `ALTER TABLE "event" DROP CONSTRAINT "FK_404b3d263eafc41aae2044e9b85"`, - ); - await queryRunner.query( - `DROP INDEX "public"."IDX_fad8b476b66052b4e2a5653cf5"`, - ); - await queryRunner.query( - `DROP INDEX "public"."IDX_baaddcf9097758446307cd9825"`, - ); - await queryRunner.query(`DROP TABLE "project_artifacts_artifact"`); - await queryRunner.query( - `DROP INDEX "public"."IDX_d38703155092b0b8b0779b43dd"`, - ); - await queryRunner.query( - `DROP INDEX "public"."IDX_44350998a09c6306a61f6854d2"`, - ); - await queryRunner.query(`DROP TABLE "collection_projects_project"`); - await queryRunner.query(`DROP TABLE "log"`); - await queryRunner.query(`DROP TABLE "job_execution"`); - await queryRunner.query(`DROP TABLE "job"`); - await queryRunner.query( - `DROP INDEX "public"."IDX_3d92caafdc685cc487ce201b49"`, - ); - await queryRunner.query(`DROP TABLE "event_pointer"`); - await queryRunner.query( - `DROP INDEX "public"."IDX_46f304d2b1e02a2ce46d43403e"`, - ); - await queryRunner.query( - `DROP INDEX "public"."IDX_82ff22c1314984ac7b8cade26a"`, - ); - await queryRunner.query( - `DROP INDEX "public"."IDX_8eb34e6d33033e17e91776c1c2"`, - ); - await queryRunner.query(`DROP TABLE "event"`); - await queryRunner.query( - `DROP INDEX "public"."IDX_1b59a64c744d72143d2b96b2c7"`, - ); - await queryRunner.query(`DROP TABLE "artifact"`); - await queryRunner.query(`DROP TABLE "project"`); - await queryRunner.query(`DROP TABLE "collection"`); - } -} diff --git a/indexer/src/db/migration/1695756174303-view_daily.ts b/indexer/src/db/migration/1695756174303-view_daily.ts deleted file mode 100644 index 767d080b0..000000000 --- a/indexer/src/db/migration/1695756174303-view_daily.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class ViewDaily1695756174303 implements MigrationInterface { - name = "ViewDaily1695756174303"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE MATERIALIZED VIEW "events_daily_by_artifact" WITH (timescaledb.continuous) AS - SELECT "toId", - "type", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - GROUP BY "toId", "type", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query(` - SELECT add_continuous_aggregate_policy('events_daily_by_artifact', - start_offset => INTERVAL '1 month', - end_offset => INTERVAL '1 day', - schedule_interval => INTERVAL '1 hour'); - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_by_artifact", - 'SELECT "toId",\n "type",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event" \n GROUP BY "toId", "type", "bucketDaily"\n WITH NO DATA;', - ], - ); - await queryRunner.query(` - CREATE MATERIALIZED VIEW "events_daily_by_project" WITH (timescaledb.continuous) AS - SELECT "projectId", - "type", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."toId" - GROUP BY "projectId", "type", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query(` - SELECT add_continuous_aggregate_policy('events_daily_by_project', - start_offset => INTERVAL '1 month', - end_offset => INTERVAL '1 day', - schedule_interval => INTERVAL '1 hour'); - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_by_project", - 'SELECT "projectId",\n "type",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event"\n INNER JOIN "project_artifacts_artifact"\n on "project_artifacts_artifact"."artifactId" = "event"."toId"\n GROUP BY "projectId", "type", "bucketDaily"\n WITH NO DATA;', - ], - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_by_project", "public"], - ); - await queryRunner.query(` - SELECT remove_continuous_aggregate_policy('events_daily_by_project'); - `); - await queryRunner.query(`DROP MATERIALIZED VIEW "events_daily_by_project"`); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_by_artifact", "public"], - ); - await queryRunner.query(` - SELECT remove_continuous_aggregate_policy('events_daily_by_artifact'); - `); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_daily_by_artifact"`, - ); - } -} diff --git a/indexer/src/db/migration/1695760450566-tweaks.ts b/indexer/src/db/migration/1695760450566-tweaks.ts deleted file mode 100644 index c23e7fb70..000000000 --- a/indexer/src/db/migration/1695760450566-tweaks.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class Tweaks1695760450566 implements MigrationInterface { - name = "Tweaks1695760450566"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX "public"."IDX_1b59a64c744d72143d2b96b2c7"`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_1b59a64c744d72143d2b96b2c7" ON "artifact" ("namespace", "name") `, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX "public"."IDX_1b59a64c744d72143d2b96b2c7"`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_1b59a64c744d72143d2b96b2c7" ON "artifact" ("namespace", "name") `, - ); - } -} diff --git a/indexer/src/db/migration/1696000344339-update-job.ts b/indexer/src/db/migration/1696000344339-update-job.ts deleted file mode 100644 index 970158994..000000000 --- a/indexer/src/db/migration/1696000344339-update-job.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateJob1696000344339 implements MigrationInterface { - name = "UpdateJob1696000344339"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "job_group_lock" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "name" text NOT NULL, CONSTRAINT "PK_7ac2c36fc088312e6aa48757bea" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_e2e4d62585b211a9ce3e53a940" ON "job_group_lock" ("name") `, - ); - await queryRunner.query(`ALTER TABLE "job" DROP COLUMN "execGroup"`); - await queryRunner.query(`ALTER TABLE "job" ADD "group" text`); - await queryRunner.query( - `ALTER TABLE "job" ADD "options" jsonb NOT NULL DEFAULT '{}'`, - ); - await queryRunner.query( - `ALTER TABLE "job_execution" ADD "attempt" integer NOT NULL`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_93b37df8fba4d12188e1a2fa60" ON "job" ("scheduledTime", "collector") `, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_e11d88cacc0910d13d258ebe5a" ON "job_execution" ("jobId", "attempt") `, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX "public"."IDX_e11d88cacc0910d13d258ebe5a"`, - ); - await queryRunner.query( - `DROP INDEX "public"."IDX_93b37df8fba4d12188e1a2fa60"`, - ); - await queryRunner.query( - `ALTER TABLE "job_execution" DROP COLUMN "attempt"`, - ); - await queryRunner.query(`ALTER TABLE "job" DROP COLUMN "options"`); - await queryRunner.query(`ALTER TABLE "job" DROP COLUMN "group"`); - await queryRunner.query(`ALTER TABLE "job" ADD "execGroup" text NOT NULL`); - await queryRunner.query( - `DROP INDEX "public"."IDX_e2e4d62585b211a9ce3e53a940"`, - ); - await queryRunner.query(`DROP TABLE "job_group_lock"`); - } -} diff --git a/indexer/src/db/migration/1696246784819-job-execution-version.ts b/indexer/src/db/migration/1696246784819-job-execution-version.ts deleted file mode 100644 index 4735b05d9..000000000 --- a/indexer/src/db/migration/1696246784819-job-execution-version.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class JobExecutionVersion1696246784819 implements MigrationInterface { - name = "JobExecutionVersion1696246784819"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "job_execution" ADD "version" integer NOT NULL DEFAULT '0'`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "job_execution" DROP COLUMN "version"`, - ); - } -} diff --git a/indexer/src/db/migration/1696332077457-allow-manual-lock.ts b/indexer/src/db/migration/1696332077457-allow-manual-lock.ts deleted file mode 100644 index e859672e2..000000000 --- a/indexer/src/db/migration/1696332077457-allow-manual-lock.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AllowManualLock1696332077457 implements MigrationInterface { - name = "AllowManualLock1696332077457"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TYPE "public"."job_status_enum" RENAME TO "job_status_enum_old"`, - ); - await queryRunner.query( - `CREATE TYPE "public"."job_status_enum" AS ENUM('PENDING', 'COMPLETE', 'MANUALLY_LOCKED')`, - ); - await queryRunner.query( - `ALTER TABLE "job" ALTER COLUMN "status" TYPE "public"."job_status_enum" USING "status"::"text"::"public"."job_status_enum"`, - ); - await queryRunner.query(`DROP TYPE "public"."job_status_enum_old"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TYPE "public"."job_status_enum_old" AS ENUM('PENDING', 'COMPLETE')`, - ); - await queryRunner.query( - `ALTER TABLE "job" ALTER COLUMN "status" TYPE "public"."job_status_enum_old" USING "status"::"text"::"public"."job_status_enum_old"`, - ); - await queryRunner.query(`DROP TYPE "public"."job_status_enum"`); - await queryRunner.query( - `ALTER TYPE "public"."job_status_enum_old" RENAME TO "job_status_enum"`, - ); - } -} diff --git a/indexer/src/db/migration/1696495364452-job-add-version.ts b/indexer/src/db/migration/1696495364452-job-add-version.ts deleted file mode 100644 index a6631a274..000000000 --- a/indexer/src/db/migration/1696495364452-job-add-version.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class JobAddVersion1696495364452 implements MigrationInterface { - name = "JobAddVersion1696495364452"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "job" ADD "version" integer NOT NULL DEFAULT '0'`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "job" DROP COLUMN "version"`); - } -} diff --git a/indexer/src/db/migration/1696805280733-fix-event-source-id-index.ts b/indexer/src/db/migration/1696805280733-fix-event-source-id-index.ts deleted file mode 100644 index e82bd7b58..000000000 --- a/indexer/src/db/migration/1696805280733-fix-event-source-id-index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixEventSourceIdIndex1696805280733 implements MigrationInterface { - name = "FixEventSourceIdIndex1696805280733"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX "public"."IDX_8eb34e6d33033e17e91776c1c2"`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_7ea8a55b1ae8ec8d4a5e0af3e5" ON "event" ("type", "sourceId", "time") `, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX "public"."IDX_7ea8a55b1ae8ec8d4a5e0af3e5"`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_8eb34e6d33033e17e91776c1c2" ON "event" ("sourceId", "time") `, - ); - } -} diff --git a/indexer/src/db/migration/1697236370397-event-add-size-and-details.ts b/indexer/src/db/migration/1697236370397-event-add-size-and-details.ts deleted file mode 100644 index e99bd5e49..000000000 --- a/indexer/src/db/migration/1697236370397-event-add-size-and-details.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class EventAddSizeAndDetails1697236370397 implements MigrationInterface { - name = "EventAddSizeAndDetails1697236370397"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "event" ADD "size" bigint NOT NULL DEFAULT '0'`, - ); - await queryRunner.query( - `ALTER TABLE "event" ADD "details" jsonb NOT NULL DEFAULT '{}'`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "event" DROP COLUMN "details"`); - await queryRunner.query(`ALTER TABLE "event" DROP COLUMN "size"`); - } -} diff --git a/indexer/src/db/migration/1697488360700-allow-move-to-event-type-table.ts b/indexer/src/db/migration/1697488360700-allow-move-to-event-type-table.ts deleted file mode 100644 index 764808292..000000000 --- a/indexer/src/db/migration/1697488360700-allow-move-to-event-type-table.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -// The original EventType enum used before this migration -enum OriginalEventType { - FUNDING = "FUNDING", - PULL_REQUEST_CREATED = "PULL_REQUEST_CREATED", - PULL_REQUEST_MERGED = "PULL_REQUEST_MERGED", - COMMIT_CODE = "COMMIT_CODE", - ISSUE_FILED = "ISSUE_FILED", - ISSUE_CLOSED = "ISSUE_CLOSED", - DOWNSTREAM_DEPENDENCY_COUNT = "DOWNSTREAM_DEPENDENCY_COUNT", - UPSTREAM_DEPENDENCY_COUNT = "UPSTREAM_DEPENDENCY_COUNT", - DOWNLOADS = "DOWNLOADS", - CONTRACT_INVOKED = "CONTRACT_INVOKED", - USERS_INTERACTED = "USERS_INTERACTED", - CONTRACT_INVOKED_AGGREGATE_STATS = "CONTRACT_INVOKED_AGGREGATE_STATS", - PULL_REQUEST_CLOSED = "PULL_REQUEST_CLOSED", - STAR_AGGREGATE_STATS = "STAR_AGGREGATE_STATS", - PULL_REQUEST_REOPENED = "PULL_REQUEST_REOPENED", - PULL_REQUEST_REMOVED_FROM_PROJECT = "PULL_REQUEST_REMOVED_FROM_PROJECT", - PULL_REQUEST_APPROVED = "PULL_REQUEST_APPROVED", - ISSUE_CREATED = "ISSUE_CREATED", - ISSUE_REOPENED = "ISSUE_REOPENED", - ISSUE_REMOVED_FROM_PROJECT = "ISSUE_REMOVED_FROM_PROJECT", - STARRED = "STARRED", - FORK_AGGREGATE_STATS = "FORK_AGGREGATE_STATS", - FORKED = "FORKED", - WATCHER_AGGREGATE_STATS = "WATCHER_AGGREGATE_STATS", -} - -export class AllowMoveToEventTypeTable1697488360700 - implements MigrationInterface -{ - name = "AllowMoveToEventTypeTable1697488360700"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "event_type" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(50) NOT NULL, "version" smallint NOT NULL, CONSTRAINT "PK_d968f34984d7d85d96f782872fe" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_512d8bcc602d7561a096a46395" ON "event_type" ("name", "version") `, - ); - await queryRunner.query(`ALTER TABLE "event" ADD "typeId" integer`); - await queryRunner.query( - `ALTER TABLE "event" ADD CONSTRAINT "FK_255cc0faa667931c91431716165" FOREIGN KEY ("typeId") REFERENCES "event_type"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - - const enumValues = Object.keys(OriginalEventType); - const keys = Object.values(enumValues); - - // Initialize the event type table. Not worrying about sql injection - // here. We're the ones providing input. - await queryRunner.query( - ` - insert into event_type(name, version) - select unnest($1::text[]), 1 - `, - [keys], - ); - - // Fill the fields with the correct values from the new event type table - await queryRunner.query(` - update event e - set "typeId" = et.id - from event_type et where et.name = e.type::TEXT - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "event" DROP CONSTRAINT "FK_255cc0faa667931c91431716165"`, - ); - await queryRunner.query(`ALTER TABLE "event" DROP COLUMN "typeId"`); - await queryRunner.query( - `DROP INDEX "public"."IDX_512d8bcc602d7561a096a46395"`, - ); - await queryRunner.query(`DROP TABLE "event_type"`); - } -} diff --git a/indexer/src/db/migration/1697488360710-rebuild-views.ts b/indexer/src/db/migration/1697488360710-rebuild-views.ts deleted file mode 100644 index fc7d1cf85..000000000 --- a/indexer/src/db/migration/1697488360710-rebuild-views.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RebuildViews1697488360710 implements MigrationInterface { - name = "RebuildViews1697488360710"; - - public async up(queryRunner: QueryRunner): Promise { - // Delete the original views - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_by_project", "public"], - ); - await queryRunner.query(` - SELECT remove_continuous_aggregate_policy('events_daily_by_project'); - `); - await queryRunner.query(`DROP MATERIALIZED VIEW "events_daily_by_project"`); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_by_artifact", "public"], - ); - await queryRunner.query(` - SELECT remove_continuous_aggregate_policy('events_daily_by_artifact'); - `); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_daily_by_artifact"`, - ); - - // Create using typeId - await queryRunner.query(` - CREATE MATERIALIZED VIEW "events_daily_by_artifact" WITH (timescaledb.continuous) AS - SELECT "toId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - GROUP BY "toId", "typeId", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query(` - SELECT add_continuous_aggregate_policy('events_daily_by_artifact', - start_offset => INTERVAL '1 month', - end_offset => INTERVAL '1 day', - schedule_interval => INTERVAL '1 hour'); - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_by_artifact", - 'SELECT "toId",\n "typeId",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event" \n GROUP BY "toId", "typeId", "bucketDaily"\n WITH NO DATA;', - ], - ); - await queryRunner.query(` - CREATE MATERIALIZED VIEW "events_daily_by_project" WITH (timescaledb.continuous) AS - SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."toId" - GROUP BY "projectId", "typeId", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query(` - SELECT add_continuous_aggregate_policy('events_daily_by_project', - start_offset => INTERVAL '1 month', - end_offset => INTERVAL '1 day', - schedule_interval => INTERVAL '1 hour'); - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_by_project", - 'SELECT "projectId",\n "typeId",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event"\n INNER JOIN "project_artifacts_artifact"\n on "project_artifacts_artifact"."artifactId" = "event"."toId"\n GROUP BY "projectId", "typeId", "bucketDaily"\n WITH NO DATA;', - ], - ); - } - - public async down(queryRunner: QueryRunner): Promise { - // Delete the new views - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_by_project", "public"], - ); - await queryRunner.query(` - SELECT remove_continuous_aggregate_policy('events_daily_by_project'); - `); - await queryRunner.query(`DROP MATERIALIZED VIEW "events_daily_by_project"`); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_by_artifact", "public"], - ); - await queryRunner.query(` - SELECT remove_continuous_aggregate_policy('events_daily_by_artifact'); - `); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_daily_by_artifact"`, - ); - - // Recreate the old views - await queryRunner.query(` - CREATE MATERIALIZED VIEW "events_daily_by_artifact" WITH (timescaledb.continuous) AS - SELECT "toId", - "type", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - GROUP BY "toId", "type", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query(` - SELECT add_continuous_aggregate_policy('events_daily_by_artifact', - start_offset => INTERVAL '1 month', - end_offset => INTERVAL '1 day', - schedule_interval => INTERVAL '1 hour'); - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_by_artifact", - 'SELECT "toId",\n "type",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event" \n GROUP BY "toId", "type", "bucketDaily"\n WITH NO DATA;', - ], - ); - await queryRunner.query(` - CREATE MATERIALIZED VIEW "events_daily_by_project" WITH (timescaledb.continuous) AS - SELECT "projectId", - "type", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."toId" - GROUP BY "projectId", "type", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query(` - SELECT add_continuous_aggregate_policy('events_daily_by_project', - start_offset => INTERVAL '1 month', - end_offset => INTERVAL '1 day', - schedule_interval => INTERVAL '1 hour'); - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_by_project", - 'SELECT "projectId",\n "type",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event"\n INNER JOIN "project_artifacts_artifact"\n on "project_artifacts_artifact"."artifactId" = "event"."toId"\n GROUP BY "projectId", "type", "bucketDaily"\n WITH NO DATA;', - ], - ); - } -} diff --git a/indexer/src/db/migration/1697508748403-remove-type-enum-from-events.ts b/indexer/src/db/migration/1697508748403-remove-type-enum-from-events.ts deleted file mode 100644 index f407a98b3..000000000 --- a/indexer/src/db/migration/1697508748403-remove-type-enum-from-events.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveTypeEnumFromEvents1697508748403 - implements MigrationInterface -{ - name = "RemoveTypeEnumFromEvents1697508748403"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX "public"."IDX_7ea8a55b1ae8ec8d4a5e0af3e5"`, - ); - await queryRunner.query(`ALTER TABLE "event" DROP COLUMN "type"`); - await queryRunner.query(`DROP TYPE "public"."event_type_enum"`); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_1bbe6d5332074e612550af9f35" ON "event" ("typeId", "sourceId", "time") `, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX "public"."IDX_1bbe6d5332074e612550af9f35"`, - ); - await queryRunner.query( - `CREATE TYPE "public"."event_type_enum" AS ENUM('COMMIT_CODE', 'CONTRACT_INVOKED', 'CONTRACT_INVOKED_AGGREGATE_STATS', 'DOWNLOADS', 'DOWNSTREAM_DEPENDENCY_COUNT', 'FORKED', 'FORK_AGGREGATE_STATS', 'FUNDING', 'ISSUE_CLOSED', 'ISSUE_CREATED', 'ISSUE_FILED', 'ISSUE_REMOVED_FROM_PROJECT', 'ISSUE_REOPENED', 'PULL_REQUEST_APPROVED', 'PULL_REQUEST_CLOSED', 'PULL_REQUEST_CREATED', 'PULL_REQUEST_MERGED', 'PULL_REQUEST_REMOVED_FROM_PROJECT', 'PULL_REQUEST_REOPENED', 'STARRED', 'STAR_AGGREGATE_STATS', 'UPSTREAM_DEPENDENCY_COUNT', 'USERS_INTERACTED', 'WATCHER_AGGREGATE_STATS')`, - ); - // Set a default so the rollback can succeed. We will need to do a custom - // query to fix it if necessary. - await queryRunner.query( - `ALTER TABLE "event" ADD "type" "public"."event_type_enum" NOT NULL DEFAULT 'COMMIT_CODE'`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_7ea8a55b1ae8ec8d4a5e0af3e5" ON "event" ("sourceId", "time", "type") `, - ); - } -} diff --git a/indexer/src/db/migration/1697508826281-split-contract-invoked-events.ts b/indexer/src/db/migration/1697508826281-split-contract-invoked-events.ts deleted file mode 100644 index 809478119..000000000 --- a/indexer/src/db/migration/1697508826281-split-contract-invoked-events.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SplitContractInvokedEvents1697508826281 - implements MigrationInterface -{ - public async up(queryRunner: QueryRunner): Promise { - // Create the new event types - const events = [ - "CONTRACT_INVOCATION_DAILY_COUNT", - "CONTRACT_INVOCATION_DAILY_FEES", - ]; - - await queryRunner.query( - ` - insert into event_type(name, version) - select unnest($1::text[]), 1 - `, - [events], - ); - - console.log( - await queryRunner.query(` - select * from event_type et where et.name = 'CONTRACT_INVOCATION_DAILY_FEES' and et.version = 1 - `), - ); - - console.log( - await queryRunner.query(` - select * from event_type et where et.name = 'CONTRACT_INVOCATION_DAILY_COUNT' and et.version = 1 - `), - ); - - // Create the `CONTRACT_INVOCATION_DAILY_COUNT` from the `CONTRACT_INVOKED` event - await queryRunner.query(` - with cidc_et as ( - select * from event_type et where et.name = 'CONTRACT_INVOCATION_DAILY_COUNT' and et.version = 1 - ) - insert into event("sourceId", "typeId", "time", "toId", "fromId", "amount") - select e."sourceId", (select "id" from cidc_et), e."time", e."toId", e."fromId", e."amount" - from event e - join event_type insert_et - on insert_et."id" = e."typeId" - where insert_et."name" = 'CONTRACT_INVOKED' - `); - await queryRunner.query(` - with cidf_et as ( - select * from event_type et where et.name = 'CONTRACT_INVOCATION_DAILY_FEES' and et.version = 1 - ) - insert into event("sourceId", "typeId", "time", "toId", "fromId", "amount") - select e."sourceId", (select "id" from cidf_et), e."time", e."toId", e."fromId", CAST(e."size" as float) - from event e - join event_type insert_et - on insert_et."id" = e."typeId" - where insert_et."name" = 'CONTRACT_INVOKED' - `); - await queryRunner.query(` - delete from event e - using event_type et - where e."typeId" = et."id" and et."name" = 'CONTRACT_INVOKED' - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - with ci_et as ( - select * from event_type et where et.name = 'CONTRACT_INVOKED' and et.version = 1 - ) - insert into event("sourceId", "typeId", "time", "toId", "fromId", "amount", "size") - select - count_e."sourceId", - (select c.id from ci_et c), - count_e."time", - count_e."toId", - count_e."fromId", - count_e."amount", - CAST(fee_e.amount as bigint) - from - ( - select - * - from - event inner_count_e - inner join event_type ice_et - on ice_et.id = inner_count_e.typeId - where - ice_et."name" = 'CONTRACT_INVOCATION_DAILY_COUNT' - ) as count_e - inner join - ( - select - * - from - event inner_fee_e - inner join event_type ife_et - on icf_et.id = inner_count_e.typeId - where - ife_et."name" = 'CONTRACT_INVOCATION_DAILY_FEE' - ) as fee_e - `); - await queryRunner.query(` - delete from event e - using event_type et - where e."typeId" = et."id" and et."name" IN ('CONTRACT_INVOCATION_DAILY_FEES', 'CONTRACT_INVOCATION_DAILY_COUNT') - `); - - await queryRunner.query(` - delete from event_type et - where e."name" IN ('CONTRACT_INVOCATION_DAILY_FEES', 'CONTRACT_INVOCATION_DAILY_COUNT') - `); - } -} diff --git a/indexer/src/db/migration/1697509960052-remove-event-size-field.ts b/indexer/src/db/migration/1697509960052-remove-event-size-field.ts deleted file mode 100644 index 80353c51e..000000000 --- a/indexer/src/db/migration/1697509960052-remove-event-size-field.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveEventSizeField1697509960052 implements MigrationInterface { - name = "RemoveEventSizeField1697509960052"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "event" DROP COLUMN "size"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "event" ADD "size" bigint NOT NULL DEFAULT '0'`, - ); - } -} diff --git a/indexer/src/db/migration/1698244448679-MonthlyWeeklyAggregation.ts b/indexer/src/db/migration/1698244448679-MonthlyWeeklyAggregation.ts deleted file mode 100644 index 8232b3cbb..000000000 --- a/indexer/src/db/migration/1698244448679-MonthlyWeeklyAggregation.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class MonthlyWeeklyAggregation1698244448679 - implements MigrationInterface -{ - name = "MonthlyWeeklyAggregation1698244448679"; - - public async up(queryRunner: QueryRunner): Promise { - // Add views - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_daily_to_artifact" - WITH (timescaledb.continuous) - AS SELECT "toId" AS "artifactId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - GROUP BY "artifactId", "typeId", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_to_artifact", - 'SELECT "toId" AS "artifact",\n "typeId",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event" \n GROUP BY "toId", "typeId", "bucketDaily"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_weekly_to_artifact" - WITH (timescaledb.continuous) - AS SELECT "artifactId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDaily") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_to_artifact" - GROUP BY "artifactId", "typeId", "bucketWeekly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_weekly_to_artifact", - 'SELECT "artifactId",\n "typeId",\n time_bucket(INTERVAL \'1 week\', "bucketDaily") AS "bucketWeekly",\n SUM(amount) as "amount"\n FROM "events_daily_by_artifact" \n GROUP BY "toId", "typeId", "bucketWeekly"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_monthly_to_artifact" - WITH (timescaledb.continuous) - AS SELECT "artifactId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDaily") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_to_artifact" - GROUP BY "artifactId", "typeId", "bucketMonthly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_monthly_to_artifact", - 'SELECT "artifactId",\n "typeId",\n time_bucket(INTERVAL \'1 month\', "bucketDaily") AS "bucketMonthly",\n SUM(amount) as "amount"\n FROM "events_daily_by_artifact" \n GROUP BY "toId", "typeId", "bucketMonthly"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_daily_to_project" - WITH (timescaledb.continuous) - AS SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."toId" - GROUP BY "projectId", "typeId", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_to_project", - 'SELECT "projectId",\n "typeId",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event"\n INNER JOIN "project_artifacts_artifact"\n on "project_artifacts_artifact"."artifactId" = "event"."toId"\n GROUP BY "projectId", "typeId", "bucketDaily"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_weekly_to_project" - WITH (timescaledb.continuous) - AS SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDaily") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_to_project" - GROUP BY "projectId", "typeId", "bucketWeekly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_weekly_to_project", - 'SELECT "projectId",\n "typeId",\n time_bucket(INTERVAL \'1 week\', "bucketDaily") AS "bucketWeekly",\n SUM(amount) as "amount"\n FROM "events_daily_by_project" \n GROUP BY "toId", "typeId", "bucketWeekly"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_monthly_to_project" - WITH (timescaledb.continuous) - AS SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDaily") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_to_project" - GROUP BY "projectId", "typeId", "bucketMonthly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_monthly_to_project", - 'SELECT "projectId",\n "typeId",\n time_bucket(INTERVAL \'1 month\', "bucketDaily") AS "bucketMonthly",\n SUM(amount) as "amount"\n FROM "events_daily_by_project" \n GROUP BY "toId", "typeId", "bucketMonthly"\n WITH NO DATA;', - ], - ); - - // Add refresh policies - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_daily_to_artifact', start_offset => INTERVAL '1 month', end_offset => INTERVAL '1 day', schedule_interval => INTERVAL '1 hour');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_weekly_to_artifact', start_offset => INTERVAL '6 month', end_offset => INTERVAL '1 week', schedule_interval => INTERVAL '1 day');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_monthly_to_artifact', start_offset => INTERVAL '1 year', end_offset => INTERVAL '1 month', schedule_interval => INTERVAL '1 week');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_daily_to_project', start_offset => INTERVAL '1 month', end_offset => INTERVAL '1 day', schedule_interval => INTERVAL '1 hour');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_weekly_to_project', start_offset => INTERVAL '6 month', end_offset => INTERVAL '1 week', schedule_interval => INTERVAL '1 day');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_monthly_to_project', start_offset => INTERVAL '1 year', end_offset => INTERVAL '1 month', schedule_interval => INTERVAL '1 week');`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - // Remove refresh policies - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_monthly_to_project');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_weekly_to_project');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_daily_to_project');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_monthly_to_artifact');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_weekly_to_artifact');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_daily_to_artifact');`, - ); - - // Remove views - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_monthly_to_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_monthly_to_project"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_weekly_to_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_weekly_to_project"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_to_project", "public"], - ); - await queryRunner.query(`DROP MATERIALIZED VIEW "events_daily_to_project"`); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_monthly_to_artifact", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_monthly_to_artifact"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_weekly_to_artifact", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_weekly_to_artifact"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_to_artifact", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_daily_to_artifact"`, - ); - } -} diff --git a/indexer/src/db/migration/1698253664753-first-contribution-view.ts b/indexer/src/db/migration/1698253664753-first-contribution-view.ts deleted file mode 100644 index aec3610a1..000000000 --- a/indexer/src/db/migration/1698253664753-first-contribution-view.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FirstContributionView1698253664753 implements MigrationInterface { - name = "FirstContributionView1698253664753"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE MATERIALIZED VIEW "first_contribution" AS - SELECT DISTINCT ON ("toId", "fromId") - "toId", - "fromId", - "time", - "id", - "typeId", - "amount" - FROM "event" - ORDER BY "toId", "fromId", "time" ASC - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "first_contribution", - 'SELECT DISTINCT ON ("toId", "fromId")\n "toId",\n "fromId",\n "time",\n "id",\n "typeId",\n "amount"\n FROM "event"\n ORDER BY "toId", "fromId", "time" ASC \n WITH NO DATA;', - ], - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "first_contribution", "public"], - ); - await queryRunner.query(`DROP MATERIALIZED VIEW "first_contribution"`); - } -} diff --git a/indexer/src/db/migration/1698254920178-events-from-artifact-project.ts b/indexer/src/db/migration/1698254920178-events-from-artifact-project.ts deleted file mode 100644 index c331782b2..000000000 --- a/indexer/src/db/migration/1698254920178-events-from-artifact-project.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class EventsFromArtifactProject1698254920178 - implements MigrationInterface -{ - name = "EventsFromArtifactProject1698254920178"; - - public async up(queryRunner: QueryRunner): Promise { - // Add views - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_daily_from_artifact" - WITH (timescaledb.continuous) - AS SELECT "fromId" AS "artifactId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - GROUP BY "artifactId", "typeId", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_from_artifact", - 'SELECT "fromId" AS "artifactId",\n "typeId",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event" \n GROUP BY "artifactId", "typeId", "bucketDaily"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_weekly_from_artifact" - WITH (timescaledb.continuous) - AS SELECT "artifactId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDaily") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_from_artifact" - GROUP BY "artifactId", "typeId", "bucketWeekly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_weekly_from_artifact", - 'SELECT "artifactId",\n "typeId",\n time_bucket(INTERVAL \'1 week\', "bucketDaily") AS "bucketWeekly",\n SUM(amount) as "amount"\n FROM "events_daily_from_artifact" \n GROUP BY "artifactId", "typeId", "bucketWeekly"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_monthly_from_artifact" - WITH (timescaledb.continuous) - AS SELECT "artifactId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDaily") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_from_artifact" - GROUP BY "artifactId", "typeId", "bucketMonthly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_monthly_from_artifact", - 'SELECT "artifactId",\n "typeId",\n time_bucket(INTERVAL \'1 month\', "bucketDaily") AS "bucketMonthly",\n SUM(amount) as "amount"\n FROM "events_daily_from_artifact" \n GROUP BY "artifactId", "typeId", "bucketMonthly"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_daily_from_project" - WITH (timescaledb.continuous) - AS SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."fromId" - GROUP BY "projectId", "typeId", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_from_project", - 'SELECT "projectId",\n "typeId",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event"\n INNER JOIN "project_artifacts_artifact"\n on "project_artifacts_artifact"."artifactId" = "event"."fromId"\n GROUP BY "projectId", "typeId", "bucketDaily"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_weekly_from_project" - WITH (timescaledb.continuous) - AS SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDaily") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_from_project" - GROUP BY "projectId", "typeId", "bucketWeekly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_weekly_from_project", - 'SELECT "projectId",\n "typeId",\n time_bucket(INTERVAL \'1 week\', "bucketDaily") AS "bucketWeekly",\n SUM(amount) as "amount"\n FROM "events_daily_from_project" \n GROUP BY "projectId", "typeId", "bucketWeekly"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_monthly_from_project" - WITH (timescaledb.continuous) - AS SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDaily") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_from_project" - GROUP BY "projectId", "typeId", "bucketMonthly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_monthly_from_project", - 'SELECT "projectId",\n "typeId",\n time_bucket(INTERVAL \'1 month\', "bucketDaily") AS "bucketMonthly",\n SUM(amount) as "amount"\n FROM "events_daily_from_project" \n GROUP BY "projectId", "typeId", "bucketMonthly"\n WITH NO DATA;', - ], - ); - - // Add refresh policies - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_daily_from_artifact', start_offset => INTERVAL '1 month', end_offset => INTERVAL '1 day', schedule_interval => INTERVAL '1 hour');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_weekly_from_artifact', start_offset => INTERVAL '6 month', end_offset => INTERVAL '1 week', schedule_interval => INTERVAL '1 day');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_monthly_from_artifact', start_offset => INTERVAL '1 year', end_offset => INTERVAL '1 month', schedule_interval => INTERVAL '1 week');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_daily_from_project', start_offset => INTERVAL '1 month', end_offset => INTERVAL '1 day', schedule_interval => INTERVAL '1 hour');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_weekly_from_project', start_offset => INTERVAL '6 month', end_offset => INTERVAL '1 week', schedule_interval => INTERVAL '1 day');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_monthly_from_project', start_offset => INTERVAL '1 year', end_offset => INTERVAL '1 month', schedule_interval => INTERVAL '1 week');`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - // Remove refresh policies - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_monthly_from_project');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_weekly_from_project');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_daily_from_project');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_monthly_from_artifact');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_weekly_from_artifact');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_daily_from_artifact');`, - ); - - // Drop views - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_monthly_from_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_monthly_from_project"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_weekly_from_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_weekly_from_project"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_from_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_daily_from_project"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_monthly_from_artifact", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_monthly_from_artifact"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_weekly_from_artifact", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_weekly_from_artifact"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_from_artifact", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_daily_from_artifact"`, - ); - } -} diff --git a/indexer/src/db/migration/1698257133416-drop-events-by-artifact-project.ts b/indexer/src/db/migration/1698257133416-drop-events-by-artifact-project.ts deleted file mode 100644 index 9a17931a0..000000000 --- a/indexer/src/db/migration/1698257133416-drop-events-by-artifact-project.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DropEventsByArtifactProject1698257133416 - implements MigrationInterface -{ - public async up(queryRunner: QueryRunner): Promise { - // Remove refresh policies - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_daily_by_project');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_daily_by_artifact');`, - ); - - // Drop views - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_by_project", "public"], - ); - await queryRunner.query(`DROP MATERIALIZED VIEW "events_daily_by_project"`); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_by_artifact", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_daily_by_artifact"`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - // Add views - await queryRunner.query(` - CREATE MATERIALIZED VIEW "events_daily_by_artifact" WITH (timescaledb.continuous) AS - SELECT "toId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - GROUP BY "toId", "typeId", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_by_artifact", - 'SELECT "toId",\n "typeId",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event" \n GROUP BY "toId", "typeId", "bucketDaily"\n WITH NO DATA;', - ], - ); - await queryRunner.query(` - CREATE MATERIALIZED VIEW "events_daily_by_project" WITH (timescaledb.continuous) AS - SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."toId" - GROUP BY "projectId", "typeId", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_by_project", - 'SELECT "projectId",\n "typeId",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event"\n INNER JOIN "project_artifacts_artifact"\n on "project_artifacts_artifact"."artifactId" = "event"."toId"\n GROUP BY "projectId", "typeId", "bucketDaily"\n WITH NO DATA;', - ], - ); - - // Add refresh policies - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_daily_by_artifact', start_offset => INTERVAL '1 month', end_offset => INTERVAL '1 day', schedule_interval => INTERVAL '1 hour');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_daily_by_project', start_offset => INTERVAL '1 month', end_offset => INTERVAL '1 day', schedule_interval => INTERVAL '1 hour');`, - ); - } -} diff --git a/indexer/src/db/migration/1698258468674-recreate-events-to-artifact-project.ts b/indexer/src/db/migration/1698258468674-recreate-events-to-artifact-project.ts deleted file mode 100644 index 13a63550c..000000000 --- a/indexer/src/db/migration/1698258468674-recreate-events-to-artifact-project.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RecreateEventsToArtifactProject1698258468674 - implements MigrationInterface -{ - public async up(queryRunner: QueryRunner): Promise { - // Remove refresh policies - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_daily_to_project');`, - ); - - // Drop views - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_monthly_to_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_monthly_to_project"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_weekly_to_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_weekly_to_project"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_to_project", "public"], - ); - await queryRunner.query(`DROP MATERIALIZED VIEW "events_daily_to_project"`); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_monthly_to_artifact", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_monthly_to_artifact"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_weekly_to_artifact", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_weekly_to_artifact"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_to_artifact", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_daily_to_artifact"`, - ); - - // Add views - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_daily_to_artifact" - WITH (timescaledb.continuous) - AS SELECT "toId" AS "artifactId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - GROUP BY "artifactId", "typeId", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_to_artifact", - 'SELECT "toId" AS "artifactId",\n "typeId",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event" \n GROUP BY "artifactId", "typeId", "bucketDaily"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_weekly_to_artifact" - WITH (timescaledb.continuous) - AS SELECT "artifactId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDaily") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_to_artifact" - GROUP BY "artifactId", "typeId", "bucketWeekly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_weekly_to_artifact", - 'SELECT "artifactId",\n "typeId",\n time_bucket(INTERVAL \'1 week\', "bucketDaily") AS "bucketWeekly",\n SUM(amount) as "amount"\n FROM "events_daily_to_artifact" \n GROUP BY "artifactId", "typeId", "bucketWeekly"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_monthly_to_artifact" - WITH (timescaledb.continuous) - AS SELECT "artifactId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDaily") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_to_artifact" - GROUP BY "artifactId", "typeId", "bucketMonthly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_monthly_to_artifact", - 'SELECT "artifactId",\n "typeId",\n time_bucket(INTERVAL \'1 month\', "bucketDaily") AS "bucketMonthly",\n SUM(amount) as "amount"\n FROM "events_daily_to_artifact" \n GROUP BY "artifactId", "typeId", "bucketMonthly"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_daily_to_project" - WITH (timescaledb.continuous) - AS SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."toId" - GROUP BY "projectId", "typeId", "bucketDaily" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_to_project", - 'SELECT "projectId",\n "typeId",\n time_bucket(INTERVAL \'1 day\', "time") AS "bucketDaily",\n SUM(amount) as "amount"\n FROM "event"\n INNER JOIN "project_artifacts_artifact"\n on "project_artifacts_artifact"."artifactId" = "event"."toId"\n GROUP BY "projectId", "typeId", "bucketDaily"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_weekly_to_project" - WITH (timescaledb.continuous) - AS SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDaily") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_to_project" - GROUP BY "projectId", "typeId", "bucketWeekly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_weekly_to_project", - 'SELECT "projectId",\n "typeId",\n time_bucket(INTERVAL \'1 week\', "bucketDaily") AS "bucketWeekly",\n SUM(amount) as "amount"\n FROM "events_daily_to_project" \n GROUP BY "projectId", "typeId", "bucketWeekly"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_monthly_to_project" - WITH (timescaledb.continuous) - AS SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDaily") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_to_project" - GROUP BY "projectId", "typeId", "bucketMonthly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_monthly_to_project", - 'SELECT "projectId",\n "typeId",\n time_bucket(INTERVAL \'1 month\', "bucketDaily") AS "bucketMonthly",\n SUM(amount) as "amount"\n FROM "events_daily_to_project" \n GROUP BY "projectId", "typeId", "bucketMonthly"\n WITH NO DATA;', - ], - ); - - // Add refresh policies - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_daily_to_artifact', start_offset => INTERVAL '1 month', end_offset => INTERVAL '1 day', schedule_interval => INTERVAL '1 hour');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_weekly_to_artifact', start_offset => INTERVAL '6 month', end_offset => INTERVAL '1 week', schedule_interval => INTERVAL '1 day');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_monthly_to_artifact', start_offset => INTERVAL '1 year', end_offset => INTERVAL '1 month', schedule_interval => INTERVAL '1 week');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_daily_to_project', start_offset => INTERVAL '1 month', end_offset => INTERVAL '1 day', schedule_interval => INTERVAL '1 hour');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_weekly_to_project', start_offset => INTERVAL '6 month', end_offset => INTERVAL '1 week', schedule_interval => INTERVAL '1 day');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_monthly_to_project', start_offset => INTERVAL '1 year', end_offset => INTERVAL '1 month', schedule_interval => INTERVAL '1 week');`, - ); - } - - public async down(_queryRunner: QueryRunner): Promise {} -} diff --git a/indexer/src/db/migration/1698264908073-add-artifact-owner-to-collection.ts b/indexer/src/db/migration/1698264908073-add-artifact-owner-to-collection.ts deleted file mode 100644 index 9bda398d2..000000000 --- a/indexer/src/db/migration/1698264908073-add-artifact-owner-to-collection.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddArtifactOwnerToCollection1698264908073 - implements MigrationInterface -{ - name = "AddArtifactOwnerToCollection1698264908073"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "collection" ADD "artifactOwnerId" integer`, - ); - await queryRunner.query( - `ALTER TABLE "collection" ADD CONSTRAINT "FK_e5cf4e13b16b587a9ca6a12d721" FOREIGN KEY ("artifactOwnerId") REFERENCES "artifact"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "collection" DROP CONSTRAINT "FK_e5cf4e13b16b587a9ca6a12d721"`, - ); - await queryRunner.query( - `ALTER TABLE "collection" DROP COLUMN "artifactOwnerId"`, - ); - } -} diff --git a/indexer/src/db/migration/1698430722339-add-collection-type.ts b/indexer/src/db/migration/1698430722339-add-collection-type.ts deleted file mode 100644 index 3f002d64e..000000000 --- a/indexer/src/db/migration/1698430722339-add-collection-type.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddCollectionType1698430722339 implements MigrationInterface { - name = "AddCollectionType1698430722339"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "collection_type" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "name" text NOT NULL, CONSTRAINT "PK_75c673f46e25c52205fae22130c" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_7be682a45f3aabbd9a662cd38c" ON "collection_type" ("name") `, - ); - - // Load initial values into the collection_type table - const collectionTypes = [ - "OSS_DIRECTORY", - "ARTIFACT_DEPENDENCIES", - "ARTIFACT_DEPENDENTS", - "USER", - ]; - - // Initialize the event type table. Not worrying about sql injection - // here. We're the ones providing input. - await queryRunner.query( - ` - INSERT INTO collection_type(name) - SELECT UNNEST($1::text[]) - `, - [collectionTypes], - ); - - await queryRunner.query(`ALTER TABLE "collection" ADD "typeId" integer`); - await queryRunner.query( - `ALTER TABLE "collection" ADD CONSTRAINT "FK_59018e4fc204e8ab8e76bd8005a" FOREIGN KEY ("typeId") REFERENCES "collection_type"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - - // Update all existing collections to use `OSS_DIRECTORY` at the time of - // migration this was the only kind of collection - await queryRunner.query( - ` - UPDATE collection - SET "typeId"=1 - `, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "collection" DROP CONSTRAINT "FK_59018e4fc204e8ab8e76bd8005a"`, - ); - await queryRunner.query(`ALTER TABLE "collection" DROP COLUMN "typeId"`); - await queryRunner.query( - `DROP INDEX "public"."IDX_7be682a45f3aabbd9a662cd38c"`, - ); - await queryRunner.query(`DROP TABLE "collection_type"`); - } -} diff --git a/indexer/src/db/migration/1698573838734-add-schedule-type.ts b/indexer/src/db/migration/1698573838734-add-schedule-type.ts deleted file mode 100644 index 07d08100e..000000000 --- a/indexer/src/db/migration/1698573838734-add-schedule-type.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddScheduleType1698573838734 implements MigrationInterface { - name = "AddScheduleType1698573838734"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX "public"."IDX_93b37df8fba4d12188e1a2fa60"`, - ); - await queryRunner.query( - `ALTER TABLE "job" ADD "scheduleType" text NOT NULL DEFAULT 'main'`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_1aa59c02d7f21ade4eafd273d1" ON "job" ("scheduledTime", "scheduleType", "collector") `, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX "public"."IDX_1aa59c02d7f21ade4eafd273d1"`, - ); - await queryRunner.query(`ALTER TABLE "job" DROP COLUMN "scheduleType"`); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_93b37df8fba4d12188e1a2fa60" ON "job" ("scheduledTime", "collector") `, - ); - } -} diff --git a/indexer/src/db/migration/1698954113655-first-contribution-type.ts b/indexer/src/db/migration/1698954113655-first-contribution-type.ts deleted file mode 100644 index 78cb183dc..000000000 --- a/indexer/src/db/migration/1698954113655-first-contribution-type.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FirstContributionType1698954113655 implements MigrationInterface { - name = "FirstContributionType1698954113655"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "first_contribution", "public"], - ); - await queryRunner.query(`DROP MATERIALIZED VIEW "first_contribution"`); - await queryRunner.query(`CREATE MATERIALIZED VIEW "first_contribution" AS - SELECT DISTINCT ON ("toId", "fromId", "typeId") - "toId", - "fromId", - "typeId", - "time", - "id", - "amount" - FROM "event" - ORDER BY "toId", "fromId", "typeId", "time" ASC - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "first_contribution", - 'SELECT DISTINCT ON ("toId", "fromId", "typeId")\n "toId",\n "fromId",\n "typeId",\n "time",\n "id",\n "amount"\n FROM "event"\n ORDER BY "toId", "fromId", "typeId", "time" ASC \n WITH NO DATA;', - ], - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "first_contribution", "public"], - ); - await queryRunner.query(`DROP MATERIALIZED VIEW "first_contribution"`); - await queryRunner.query(`CREATE MATERIALIZED VIEW "first_contribution" AS SELECT DISTINCT ON ("toId", "fromId") - "toId", - "fromId", - "time", - "id", - "typeId", - "amount" - FROM "event" - ORDER BY "toId", "fromId", "time" ASC - WITH NO DATA;`); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "first_contribution", - 'SELECT DISTINCT ON ("toId", "fromId")\n "toId",\n "fromId",\n "time",\n "id",\n "typeId",\n "amount"\n FROM "event"\n ORDER BY "toId", "fromId", "time" ASC \n WITH NO DATA;', - ], - ); - } -} diff --git a/indexer/src/db/migration/1698960376757-users-monthly-to-project.ts b/indexer/src/db/migration/1698960376757-users-monthly-to-project.ts deleted file mode 100644 index e57c1f494..000000000 --- a/indexer/src/db/migration/1698960376757-users-monthly-to-project.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UsersMonthlyToProject1698960376757 implements MigrationInterface { - name = "UsersMonthlyToProject1698960376757"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE MATERIALIZED VIEW "users_monthly_to_project" AS - WITH Devs AS ( - SELECT - p."id" AS "projectId", - e."fromId" AS "fromId", - time_bucket(INTERVAL '1 month', e."time") AS "bucketMonthly", - CASE - WHEN COUNT(DISTINCT CASE WHEN t."name" = 'COMMIT_CODE' THEN e."time" END) >= 10 THEN 'FULL_TIME_DEV' - WHEN COUNT(DISTINCT CASE WHEN t."name" = 'COMMIT_CODE' THEN e."time" END) >= 1 THEN 'PART_TIME_DEV' - ELSE 'OTHER_CONTRIBUTOR' - END AS "segmentType", - 1 AS amount - FROM event e - JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId" - JOIN project p ON paa."projectId" = p.id - JOIN event_type t ON e."typeId" = t.id - WHERE - t."name" IN ( - 'PULL_REQUEST_CREATED', - 'PULL_REQUEST_MERGED', - 'COMMIT_CODE', - 'ISSUE_CLOSED', - 'ISSUE_CREATED' - ) - GROUP BY - p."id", - e."fromId", - "bucketMonthly" - ), - Users AS ( - SELECT - p."id" AS "projectId", - e."fromId" AS "fromId", - time_bucket(INTERVAL '1 month', e."time") AS "bucketMonthly", - CASE - WHEN SUM(e."amount") >= 1000 THEN 'HIGH_FREQUENCY_USER' - WHEN SUM(e."amount") >= 10 THEN 'HIGH_VALUE_USER' - ELSE 'LOW_VALUE_USER' - END AS "segmentType", - 1 AS amount - FROM event e - JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId" - JOIN project p ON paa."projectId" = p.id - JOIN event_type t ON e."typeId" = t.id - WHERE t."name" = 'CONTRACT_INVOCATION_DAILY_COUNT' - GROUP BY - p."id", - e."fromId", - "bucketMonthly" - ) - SELECT - "projectId", - "segmentType", - "bucketMonthly", - SUM("amount") AS "amount" - FROM - ( - SELECT * FROM Devs - UNION ALL - SELECT * FROM Users - ) combined_data - GROUP BY - "projectId", - "segmentType", - "bucketMonthly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "users_monthly_to_project", - 'WITH Devs AS (\n SELECT \n p."id" AS "projectId",\n e."fromId" AS "fromId",\n time_bucket(INTERVAL \'1 month\', e."time") AS "bucketMonthly",\n CASE \n WHEN COUNT(DISTINCT CASE WHEN t."name" = \'COMMIT_CODE\' THEN e."time" END) >= 10 THEN \'FULL_TIME_DEV\'\n WHEN COUNT(DISTINCT CASE WHEN t."name" = \'COMMIT_CODE\' THEN e."time" END) >= 1 THEN \'PART_TIME_DEV\'\n ELSE \'OTHER_CONTRIBUTOR\'\n END AS "segmentType",\n 1 AS amount\n FROM event e\n JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId"\n JOIN project p ON paa."projectId" = p.id \n JOIN event_type t ON e."typeId" = t.id\n WHERE\n t."name" IN (\n \'PULL_REQUEST_CREATED\',\n \'PULL_REQUEST_MERGED\',\n \'COMMIT_CODE\',\n \'ISSUE_CLOSED\',\n \'ISSUE_CREATED\'\n )\n GROUP BY\n p."id",\n e."fromId",\n "bucketMonthly"\n ),\n Users AS (\n SELECT \n p."id" AS "projectId",\n e."fromId" AS "fromId",\n time_bucket(INTERVAL \'1 month\', e."time") AS "bucketMonthly",\n CASE \n WHEN SUM(e."amount") >= 1000 THEN \'HIGH_FREQUENCY_USER\'\n WHEN SUM(e."amount") >= 10 THEN \'HIGH_VALUE_USER\'\n ELSE \'LOW_VALUE_USER\'\n END AS "segmentType",\n 1 AS amount\n FROM event e\n JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId"\n JOIN project p ON paa."projectId" = p.id\n JOIN event_type t ON e."typeId" = t.id\n WHERE t."name" = \'CONTRACT_INVOCATION_DAILY_COUNT\'\n GROUP BY\n p."id",\n e."fromId",\n "bucketMonthly"\n )\n SELECT\n "projectId",\n "segmentType",\n "bucketMonthly",\n SUM("amount") AS "amount"\n FROM \n (\n SELECT * FROM Devs\n UNION ALL\n SELECT * FROM Users\n ) combined_data\n GROUP BY\n "projectId",\n "segmentType",\n "bucketMonthly";', - ], - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "users_monthly_to_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "users_monthly_to_project"`, - ); - } -} diff --git a/indexer/src/db/migration/1699514548114-add-temp-event-table.ts b/indexer/src/db/migration/1699514548114-add-temp-event-table.ts deleted file mode 100644 index 9894b121c..000000000 --- a/indexer/src/db/migration/1699514548114-add-temp-event-table.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddTempEventTable1699514548114 implements MigrationInterface { - name = "AddTempEventTable1699514548114"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "recording" ("recorderId" uuid NOT NULL, "expiration" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_3cbc085e961540e077702c8bc65" PRIMARY KEY ("recorderId"))`, - ); - await queryRunner.query( - `CREATE TABLE "recorder_temp_event" ("id" SERIAL NOT NULL, "recorderId" uuid NOT NULL, "batchId" integer NOT NULL, "sourceId" text NOT NULL, "typeId" integer NOT NULL, "time" TIMESTAMP WITH TIME ZONE NOT NULL, "toName" text NOT NULL, "toNamespace" "public"."artifact_namespace_enum" NOT NULL, "toType" "public"."artifact_type_enum" NOT NULL, "toUrl" text, "fromName" text, "fromNamespace" "public"."artifact_namespace_enum", "fromType" "public"."artifact_type_enum", "fromUrl" text, "amount" double precision NOT NULL, "details" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_5dd06a9a9251fe1b43cd3c1967b" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE TABLE "recorder_temp_duplicate_event" ("id" SERIAL NOT NULL, "recorderId" uuid NOT NULL, "typeId" integer NOT NULL, "sourceId" text NOT NULL, CONSTRAINT "PK_aaf868290e25b0361902b1b0c9b" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_c57efc44a97ba219c4ac7b6c15" ON "recorder_temp_duplicate_event" ("typeId", "sourceId", "recorderId") `, - ); - await queryRunner.query(`CREATE VIEW "recorder_temp_event_artifact" AS - WITH to_artifacts AS ( - SELECT - rte_to."toName" as "name", - rte_to."toNamespace" as "namespace", - rte_to."toType" as "type", - rte_to."toUrl" as "url", - rte_to."recorderId" as "recorderId", - rte_to."batchId" as "batchId" - FROM recorder_temp_event as rte_to - ), - from_artifacts AS ( - SELECT - rte_from."fromName" as "name", - rte_from."fromNamespace" as "namespace", - rte_from."fromType" as "type", - rte_from."fromUrl" as "url", - rte_from."recorderId" as "recorderId", - rte_from."batchId" as "batchId" - FROM recorder_temp_event as rte_from - ), all_artifacts AS ( - select * from to_artifacts - UNION - select * from from_artifacts - ) - SELECT - * - FROM all_artifacts a - WHERE - a."name" IS NOT NULL AND - a."namespace" IS NOT NULL AND - a."type" IS NOT NULL - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "VIEW", - "recorder_temp_event_artifact", - 'WITH to_artifacts AS (\n SELECT\n rte_to."toName" as "name",\n rte_to."toNamespace" as "namespace",\n rte_to."toType" as "type",\n rte_to."toUrl" as "url",\n rte_to."recorderId" as "recorderId",\n rte_to."batchId" as "batchId"\n FROM recorder_temp_event as rte_to\n ),\n from_artifacts AS (\n SELECT\n rte_from."fromName" as "name",\n rte_from."fromNamespace" as "namespace",\n rte_from."fromType" as "type",\n rte_from."fromUrl" as "url",\n rte_from."recorderId" as "recorderId",\n rte_from."batchId" as "batchId"\n FROM recorder_temp_event as rte_from\n ), all_artifacts AS (\n select * from to_artifacts\n UNION\n select * from from_artifacts\n )\n SELECT\n * \n FROM all_artifacts a\n WHERE \n a."name" IS NOT NULL AND\n a."namespace" IS NOT NULL AND\n a."type" IS NOT NULL', - ], - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["VIEW", "recorder_temp_event_artifact", "public"], - ); - await queryRunner.query(`DROP VIEW "recorder_temp_event_artifact"`); - await queryRunner.query( - `DROP INDEX "public"."IDX_c57efc44a97ba219c4ac7b6c15"`, - ); - await queryRunner.query(`DROP TABLE "recorder_temp_duplicate_event"`); - await queryRunner.query(`DROP TABLE "recorder_temp_event"`); - await queryRunner.query(`DROP TABLE "recording"`); - } -} diff --git a/indexer/src/db/migration/1699701990684-last-contribution.ts b/indexer/src/db/migration/1699701990684-last-contribution.ts deleted file mode 100644 index e2c4c9de1..000000000 --- a/indexer/src/db/migration/1699701990684-last-contribution.ts +++ /dev/null @@ -1,235 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class LastContribution1699701990684 implements MigrationInterface { - name = "LastContribution1699701990684"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "users_monthly_to_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "users_monthly_to_project"`, - ); - await queryRunner.query(`DROP MATERIALIZED VIEW "first_contribution"`); - await queryRunner.query(`CREATE MATERIALIZED VIEW "first_contribution_to_project" AS - SELECT DISTINCT ON ("typeId", "projectId", "fromId") - "typeId", - "projectId", - "fromId", - "time", - "id", - "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."toId" - ORDER BY "typeId", "projectId", "fromId", "time" ASC - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "first_contribution_to_project", - 'SELECT DISTINCT ON ("typeId", "projectId", "fromId")\n "typeId",\n "projectId",\n "fromId",\n "time",\n "id",\n "amount"\n FROM "event"\n INNER JOIN "project_artifacts_artifact"\n on "project_artifacts_artifact"."artifactId" = "event"."toId"\n ORDER BY "typeId", "projectId", "fromId", "time" ASC \n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "last_contribution_to_project" AS - SELECT DISTINCT ON ("typeId", "projectId", "fromId") - "typeId", - "projectId", - "fromId", - "time", - "id", - "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."toId" - ORDER BY "typeId", "projectId", "fromId", "time" DESC - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "last_contribution_to_project", - 'SELECT DISTINCT ON ("typeId", "projectId", "fromId")\n "typeId",\n "projectId",\n "fromId",\n "time",\n "id",\n "amount"\n FROM "event"\n INNER JOIN "project_artifacts_artifact"\n on "project_artifacts_artifact"."artifactId" = "event"."toId"\n ORDER BY "typeId", "projectId", "fromId", "time" DESC\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "users_monthly_to_project" AS - WITH Devs AS ( - SELECT - p."id" AS "projectId", - e."fromId" AS "fromId", - time_bucket(INTERVAL '1 month', e."time") AS "bucketMonthly", - CASE - WHEN COUNT(DISTINCT CASE WHEN t."name" = 'COMMIT_CODE' THEN e."time" END) >= 10 THEN 'FULL_TIME_DEV' - WHEN COUNT(DISTINCT CASE WHEN t."name" = 'COMMIT_CODE' THEN e."time" END) >= 1 THEN 'PART_TIME_DEV' - ELSE 'OTHER_CONTRIBUTOR' - END AS "segmentType", - 1 AS amount - FROM event e - JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId" - JOIN project p ON paa."projectId" = p.id - JOIN event_type t ON e."typeId" = t.id - WHERE - t."name" IN ( - 'PULL_REQUEST_CREATED', - 'PULL_REQUEST_MERGED', - 'COMMIT_CODE', - 'ISSUE_CLOSED', - 'ISSUE_CREATED' - ) - GROUP BY - p."id", - e."fromId", - "bucketMonthly" - ), - Users AS ( - SELECT - p."id" AS "projectId", - e."fromId" AS "fromId", - time_bucket(INTERVAL '1 month', e."time") AS "bucketMonthly", - CASE - WHEN SUM(e."amount") >= 1000 THEN 'HIGH_FREQUENCY_USER' - WHEN SUM(e."amount") >= 10 THEN 'HIGH_VALUE_USER' - ELSE 'LOW_VALUE_USER' - END AS "segmentType", - 1 AS amount - FROM event e - JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId" - JOIN project p ON paa."projectId" = p.id - JOIN event_type t ON e."typeId" = t.id - WHERE t."name" = 'CONTRACT_INVOCATION_DAILY_COUNT' - GROUP BY - p."id", - e."fromId", - "bucketMonthly" - ) - SELECT - "projectId", - "segmentType", - "bucketMonthly", - SUM("amount") AS "amount" - FROM - ( - SELECT * FROM Devs - UNION ALL - SELECT * FROM Users - ) combined_data - GROUP BY - "projectId", - "segmentType", - "bucketMonthly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "users_monthly_to_project", - 'WITH Devs AS (\n SELECT\n p."id" AS "projectId",\n e."fromId" AS "fromId",\n time_bucket(INTERVAL \'1 month\', e."time") AS "bucketMonthly",\n CASE \n WHEN COUNT(DISTINCT CASE WHEN t."name" = \'COMMIT_CODE\' THEN e."time" END) >= 10 THEN \'FULL_TIME_DEV\'\n WHEN COUNT(DISTINCT CASE WHEN t."name" = \'COMMIT_CODE\' THEN e."time" END) >= 1 THEN \'PART_TIME_DEV\'\n ELSE \'OTHER_CONTRIBUTOR\'\n END AS "segmentType",\n 1 AS amount\n FROM event e\n JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId"\n JOIN project p ON paa."projectId" = p.id \n JOIN event_type t ON e."typeId" = t.id\n WHERE\n t."name" IN (\n \'PULL_REQUEST_CREATED\',\n \'PULL_REQUEST_MERGED\',\n \'COMMIT_CODE\',\n \'ISSUE_CLOSED\',\n \'ISSUE_CREATED\'\n )\n GROUP BY\n p."id",\n e."fromId",\n "bucketMonthly"\n ),\n Users AS (\n SELECT \n p."id" AS "projectId",\n e."fromId" AS "fromId",\n time_bucket(INTERVAL \'1 month\', e."time") AS "bucketMonthly",\n CASE \n WHEN SUM(e."amount") >= 1000 THEN \'HIGH_FREQUENCY_USER\'\n WHEN SUM(e."amount") >= 10 THEN \'HIGH_VALUE_USER\'\n ELSE \'LOW_VALUE_USER\'\n END AS "segmentType",\n 1 AS amount\n FROM event e\n JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId"\n JOIN project p ON paa."projectId" = p.id\n JOIN event_type t ON e."typeId" = t.id\n WHERE t."name" = \'CONTRACT_INVOCATION_DAILY_COUNT\'\n GROUP BY\n p."id",\n e."fromId",\n "bucketMonthly"\n )\n SELECT\n "projectId",\n "segmentType",\n "bucketMonthly",\n SUM("amount") AS "amount"\n FROM \n (\n SELECT * FROM Devs\n UNION ALL\n SELECT * FROM Users\n ) combined_data\n GROUP BY\n "projectId",\n "segmentType",\n "bucketMonthly"\n WITH NO DATA;', - ], - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "users_monthly_to_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "users_monthly_to_project"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "last_contribution_to_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "last_contribution_to_project"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "first_contribution_to_project", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "first_contribution_to_project"`, - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "users_monthly_to_project" AS WITH Devs AS ( - SELECT - p."id" AS "projectId", - e."fromId" AS "fromId", - time_bucket(INTERVAL '1 month', e."time") AS "bucketMonthly", - CASE - WHEN COUNT(DISTINCT CASE WHEN t."name" = 'COMMIT_CODE' THEN e."time" END) >= 10 THEN 'FULL_TIME_DEV' - WHEN COUNT(DISTINCT CASE WHEN t."name" = 'COMMIT_CODE' THEN e."time" END) >= 1 THEN 'PART_TIME_DEV' - ELSE 'OTHER_CONTRIBUTOR' - END AS "segmentType", - 1 AS amount - FROM event e - JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId" - JOIN project p ON paa."projectId" = p.id - JOIN event_type t ON e."typeId" = t.id - WHERE - t."name" IN ( - 'PULL_REQUEST_CREATED', - 'PULL_REQUEST_MERGED', - 'COMMIT_CODE', - 'ISSUE_CLOSED', - 'ISSUE_CREATED' - ) - GROUP BY - p."id", - e."fromId", - "bucketMonthly" - ), - Users AS ( - SELECT - p."id" AS "projectId", - e."fromId" AS "fromId", - time_bucket(INTERVAL '1 month', e."time") AS "bucketMonthly", - CASE - WHEN SUM(e."amount") >= 1000 THEN 'HIGH_FREQUENCY_USER' - WHEN SUM(e."amount") >= 10 THEN 'HIGH_VALUE_USER' - ELSE 'LOW_VALUE_USER' - END AS "segmentType", - 1 AS amount - FROM event e - JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId" - JOIN project p ON paa."projectId" = p.id - JOIN event_type t ON e."typeId" = t.id - WHERE t."name" = 'CONTRACT_INVOCATION_DAILY_COUNT' - GROUP BY - p."id", - e."fromId", - "bucketMonthly" - ) - SELECT - "projectId", - "segmentType", - "bucketMonthly", - SUM("amount") AS "amount" - FROM - ( - SELECT * FROM Devs - UNION ALL - SELECT * FROM Users - ) combined_data - GROUP BY - "projectId", - "segmentType", - "bucketMonthly";`); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "users_monthly_to_project", - 'WITH Devs AS (\n SELECT \n p."id" AS "projectId",\n e."fromId" AS "fromId",\n time_bucket(INTERVAL \'1 month\', e."time") AS "bucketMonthly",\n CASE \n WHEN COUNT(DISTINCT CASE WHEN t."name" = \'COMMIT_CODE\' THEN e."time" END) >= 10 THEN \'FULL_TIME_DEV\'\n WHEN COUNT(DISTINCT CASE WHEN t."name" = \'COMMIT_CODE\' THEN e."time" END) >= 1 THEN \'PART_TIME_DEV\'\n ELSE \'OTHER_CONTRIBUTOR\'\n END AS "segmentType",\n 1 AS amount\n FROM event e\n JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId"\n JOIN project p ON paa."projectId" = p.id \n JOIN event_type t ON e."typeId" = t.id\n WHERE\n t."name" IN (\n \'PULL_REQUEST_CREATED\',\n \'PULL_REQUEST_MERGED\',\n \'COMMIT_CODE\',\n \'ISSUE_CLOSED\',\n \'ISSUE_CREATED\'\n )\n GROUP BY\n p."id",\n e."fromId",\n "bucketMonthly"\n ),\n Users AS (\n SELECT \n p."id" AS "projectId",\n e."fromId" AS "fromId",\n time_bucket(INTERVAL \'1 month\', e."time") AS "bucketMonthly",\n CASE \n WHEN SUM(e."amount") >= 1000 THEN \'HIGH_FREQUENCY_USER\'\n WHEN SUM(e."amount") >= 10 THEN \'HIGH_VALUE_USER\'\n ELSE \'LOW_VALUE_USER\'\n END AS "segmentType",\n 1 AS amount\n FROM event e\n JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId"\n JOIN project p ON paa."projectId" = p.id\n JOIN event_type t ON e."typeId" = t.id\n WHERE t."name" = \'CONTRACT_INVOCATION_DAILY_COUNT\'\n GROUP BY\n p."id",\n e."fromId",\n "bucketMonthly"\n )\n SELECT\n "projectId",\n "segmentType",\n "bucketMonthly",\n SUM("amount") AS "amount"\n FROM \n (\n SELECT * FROM Devs\n UNION ALL\n SELECT * FROM Users\n ) combined_data\n GROUP BY\n "projectId",\n "segmentType",\n "bucketMonthly";', - ], - ); - } -} diff --git a/indexer/src/db/migration/1699775810960-events-to-collection.ts b/indexer/src/db/migration/1699775810960-events-to-collection.ts deleted file mode 100644 index c8a362840..000000000 --- a/indexer/src/db/migration/1699775810960-events-to-collection.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class EventsToCollection1699775810960 implements MigrationInterface { - name = "EventsToCollection1699775810960"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_daily_to_collection" - WITH (timescaledb.continuous) - AS SELECT "collectionId", - "typeId", - time_bucket(INTERVAL '1 day', "bucketDaily") AS "bucketDay", - SUM(amount) as "amount" - FROM "events_daily_to_project" - INNER JOIN "collection_projects_project" - ON "collection_projects_project"."projectId" = "events_daily_to_project"."projectId" - GROUP BY "collectionId", "typeId", "bucketDay" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_daily_to_collection", - 'SELECT "collectionId",\n "typeId",\n time_bucket(INTERVAL \'1 day\', "bucketDaily") AS "bucketDay",\n SUM(amount) as "amount"\n FROM "events_daily_to_project"\n INNER JOIN "collection_projects_project"\n ON "collection_projects_project"."projectId" = "events_daily_to_project"."projectId"\n GROUP BY "collectionId", "typeId", "bucketDay"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_weekly_to_collection" - WITH (timescaledb.continuous) - AS SELECT "collectionId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDay") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_to_collection" - GROUP BY "collectionId", "typeId", "bucketWeekly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_weekly_to_collection", - 'SELECT "collectionId",\n "typeId",\n time_bucket(INTERVAL \'1 week\', "bucketDay") AS "bucketWeekly",\n SUM(amount) as "amount"\n FROM "events_daily_to_collection" \n GROUP BY "collectionId", "typeId", "bucketWeekly"\n WITH NO DATA;', - ], - ); - await queryRunner.query(`CREATE MATERIALIZED VIEW "events_monthly_to_collection" - WITH (timescaledb.continuous) - AS SELECT "collectionId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDay") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_to_collection" - GROUP BY "collectionId", "typeId", "bucketMonthly" - WITH NO DATA; - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "MATERIALIZED_VIEW", - "events_monthly_to_collection", - 'SELECT "collectionId",\n "typeId",\n time_bucket(INTERVAL \'1 month\', "bucketDay") AS "bucketMonthly",\n SUM(amount) as "amount"\n FROM "events_daily_to_collection" \n GROUP BY "collectionId", "typeId", "bucketMonthly"\n WITH NO DATA;', - ], - ); - - // Add refresh policies - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_daily_to_collection', start_offset => INTERVAL '1 month', end_offset => INTERVAL '1 day', schedule_interval => INTERVAL '1 hour');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_weekly_to_collection', start_offset => INTERVAL '6 month', end_offset => INTERVAL '1 week', schedule_interval => INTERVAL '1 day');`, - ); - await queryRunner.query( - `SELECT add_continuous_aggregate_policy('events_monthly_to_collection', start_offset => INTERVAL '1 year', end_offset => INTERVAL '1 month', schedule_interval => INTERVAL '1 week');`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - // Remove refresh policies - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_monthly_to_collection');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_weekly_to_collection');`, - ); - await queryRunner.query( - `SELECT remove_continuous_aggregate_policy('events_daily_to_collection');`, - ); - - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_monthly_to_collection", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_monthly_to_collection"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_weekly_to_collection", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_weekly_to_collection"`, - ); - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["MATERIALIZED_VIEW", "events_daily_to_collection", "public"], - ); - await queryRunner.query( - `DROP MATERIALIZED VIEW "events_daily_to_collection"`, - ); - } -} diff --git a/indexer/src/db/migration/1700078985727-add-gas-used-events.ts b/indexer/src/db/migration/1700078985727-add-gas-used-events.ts deleted file mode 100644 index ebf963458..000000000 --- a/indexer/src/db/migration/1700078985727-add-gas-used-events.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -const GAS_USED_EVENT_TYPES = [ - "CONTRACT_INVOCATION_DAILY_L2_GAS_USED", - "CONTRACT_INVOCATION_DAILY_L1_GAS_USED", -]; - -export class AddGasUsedEvents1700078985727 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - ` - insert into event_type(name, version) - select unnest($1::text[]), 1 - `, - [GAS_USED_EVENT_TYPES], - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - ` - delete from event_type et - where et."name" = ANY($1) AND et."version" = 1 - `, - [GAS_USED_EVENT_TYPES], - ); - } -} diff --git a/indexer/src/db/migration/1700946423231-remove-fks.ts b/indexer/src/db/migration/1700946423231-remove-fks.ts deleted file mode 100644 index cbc9b3743..000000000 --- a/indexer/src/db/migration/1700946423231-remove-fks.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveFks1700946423231 implements MigrationInterface { - name = "RemoveFks1700946423231"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "event" DROP CONSTRAINT "FK_255cc0faa667931c91431716165"`, - ); - await queryRunner.query( - `ALTER TABLE "event" DROP CONSTRAINT "FK_404b3d263eafc41aae2044e9b85"`, - ); - await queryRunner.query( - `ALTER TABLE "event" DROP CONSTRAINT "FK_b36ab188856dd8cf3d6c7ec4f48"`, - ); - - await queryRunner.query( - `ALTER TABLE "event" ALTER COLUMN "typeId" SET NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "event" ALTER COLUMN "toId" SET NOT NULL`, - ); - - await queryRunner.query( - `ALTER TABLE "recorder_temp_event" ADD "toId" integer`, - ); - await queryRunner.query( - `ALTER TABLE "recorder_temp_event" ADD "fromId" integer`, - ); - await queryRunner.query( - `ALTER TABLE "recorder_temp_event" ALTER COLUMN "toName" DROP NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "recorder_temp_event" ALTER COLUMN "toNamespace" DROP NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "recorder_temp_event" ALTER COLUMN "toType" DROP NOT NULL`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "recorder_temp_event" ALTER COLUMN "toType" SET NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "recorder_temp_event" ALTER COLUMN "toNamespace" SET NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "recorder_temp_event" ALTER COLUMN "toName" SET NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "recorder_temp_event" DROP COLUMN "fromId"`, - ); - await queryRunner.query( - `ALTER TABLE "recorder_temp_event" DROP COLUMN "toId"`, - ); - - await queryRunner.query( - `ALTER TABLE "event" ALTER COLUMN "toId" DROP NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "event" ALTER COLUMN "typeId" DROP NOT NULL`, - ); - - await queryRunner.query( - `ALTER TABLE "event" ADD CONSTRAINT "FK_b36ab188856dd8cf3d6c7ec4f48" FOREIGN KEY ("fromId") REFERENCES "artifact"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "event" ADD CONSTRAINT "FK_404b3d263eafc41aae2044e9b85" FOREIGN KEY ("toId") REFERENCES "artifact"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "event" ADD CONSTRAINT "FK_255cc0faa667931c91431716165" FOREIGN KEY ("typeId") REFERENCES "event_type"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } -} diff --git a/indexer/src/db/migration/1700982391227-add-to-id-to-event-index.ts b/indexer/src/db/migration/1700982391227-add-to-id-to-event-index.ts deleted file mode 100644 index 510f13103..000000000 --- a/indexer/src/db/migration/1700982391227-add-to-id-to-event-index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddToIdToEventIndex1700982391227 implements MigrationInterface { - name = "AddToIdToEventIndex1700982391227"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX "public"."IDX_1bbe6d5332074e612550af9f35"`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_0b427e8ea6458c4a6a64212ce1" ON "event" ("sourceId", "typeId", "toId", "time") `, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX "public"."IDX_0b427e8ea6458c4a6a64212ce1"`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_1bbe6d5332074e612550af9f35" ON "event" ("sourceId", "time", "typeId") `, - ); - } -} diff --git a/indexer/src/db/migration/1701159604199-add-repo-deps.ts b/indexer/src/db/migration/1701159604199-add-repo-deps.ts deleted file mode 100644 index 904243add..000000000 --- a/indexer/src/db/migration/1701159604199-add-repo-deps.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddRepoDeps1701159604199 implements MigrationInterface { - name = "AddRepoDeps1701159604199"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "repo_dependency" ("id" SERIAL NOT NULL, "repoId" integer, "dependencyId" integer, CONSTRAINT "PK_b0be9917812af14b5aa86eaa7e6" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `ALTER TABLE "repo_dependency" ADD CONSTRAINT "FK_f04c98d3fe037b1e900b15c2dd2" FOREIGN KEY ("repoId") REFERENCES "artifact"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "repo_dependency" ADD CONSTRAINT "FK_c6410ea255899b5c189297031fc" FOREIGN KEY ("dependencyId") REFERENCES "artifact"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "repo_dependency" DROP CONSTRAINT "FK_c6410ea255899b5c189297031fc"`, - ); - await queryRunner.query( - `ALTER TABLE "repo_dependency" DROP CONSTRAINT "FK_f04c98d3fe037b1e900b15c2dd2"`, - ); - await queryRunner.query(`DROP TABLE "repo_dependency"`); - } -} diff --git a/indexer/src/db/migration/1701215135932-add-project-package-dependency-view.ts b/indexer/src/db/migration/1701215135932-add-project-package-dependency-view.ts deleted file mode 100644 index 927adb3b4..000000000 --- a/indexer/src/db/migration/1701215135932-add-project-package-dependency-view.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddProjectPackageDependencyView1701215135932 - implements MigrationInterface -{ - name = "AddProjectPackageDependencyView1701215135932"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE VIEW "project_package_dependency" AS - SELECT - paa_r."projectId" as "dependentProjectId", - "repoId" as "dependentRepoId", - paa_d."projectId" as "dependencyProjectId", - rd."dependencyId" as "dependencyId" - FROM "repo_dependency" rd - INNER JOIN "project_artifacts_artifact" paa_r - on "paa_r"."artifactId" = rd."repoId" - LEFT OUTER JOIN "project_artifacts_artifact" paa_d - on "paa_d"."artifactId" = rd."dependencyId" - `); - await queryRunner.query( - `INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, - [ - "public", - "VIEW", - "project_package_dependency", - 'SELECT \n paa_r."projectId" as "dependentProjectId",\n "repoId" as "dependentRepoId",\n paa_d."projectId" as "dependencyProjectId",\n rd."dependencyId" as "dependencyId"\n FROM "repo_dependency" rd\n INNER JOIN "project_artifacts_artifact" paa_r\n on "paa_r"."artifactId" = rd."repoId"\n LEFT OUTER JOIN "project_artifacts_artifact" paa_d\n on "paa_d"."artifactId" = rd."dependencyId"', - ], - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, - ["VIEW", "project_package_dependency", "public"], - ); - await queryRunner.query(`DROP VIEW "project_package_dependency"`); - } -} diff --git a/indexer/src/db/orm-entities.ts b/indexer/src/db/orm-entities.ts deleted file mode 100644 index 5d03f4331..000000000 --- a/indexer/src/db/orm-entities.ts +++ /dev/null @@ -1,1274 +0,0 @@ -import { - BaseEntity, - BeforeInsert, - BeforeUpdate, - Column, - ViewColumn, - Entity, - ViewEntity, - Index, - JoinTable, - ManyToMany, - ManyToOne, - OneToMany, - PrimaryColumn, - PrimaryGeneratedColumn, - CreateDateColumn, - UpdateDateColumn, - JoinColumn, -} from "typeorm"; -import { IsUrl, IsOptional, validateOrReject } from "class-validator"; -import type { Brand } from "utility-types"; -import { normalizeToObject } from "../utils/common.js"; - -/****************************** - * ENUMS - ******************************/ - -export enum EventTypeEnum { - FUNDING = "FUNDING", - PULL_REQUEST_CREATED = "PULL_REQUEST_CREATED", - PULL_REQUEST_MERGED = "PULL_REQUEST_MERGED", - COMMIT_CODE = "COMMIT_CODE", - ISSUE_FILED = "ISSUE_FILED", - ISSUE_CLOSED = "ISSUE_CLOSED", - DOWNSTREAM_DEPENDENCY_COUNT = "DOWNSTREAM_DEPENDENCY_COUNT", - UPSTREAM_DEPENDENCY_COUNT = "UPSTREAM_DEPENDENCY_COUNT", - DOWNLOADS = "DOWNLOADS", - CONTRACT_INVOKED = "CONTRACT_INVOKED", - USERS_INTERACTED = "USERS_INTERACTED", - CONTRACT_INVOKED_AGGREGATE_STATS = "CONTRACT_INVOKED_AGGREGATE_STATS", - PULL_REQUEST_CLOSED = "PULL_REQUEST_CLOSED", - STAR_AGGREGATE_STATS = "STAR_AGGREGATE_STATS", - PULL_REQUEST_REOPENED = "PULL_REQUEST_REOPENED", - PULL_REQUEST_REMOVED_FROM_PROJECT = "PULL_REQUEST_REMOVED_FROM_PROJECT", - PULL_REQUEST_APPROVED = "PULL_REQUEST_APPROVED", - ISSUE_CREATED = "ISSUE_CREATED", - ISSUE_REOPENED = "ISSUE_REOPENED", - ISSUE_REMOVED_FROM_PROJECT = "ISSUE_REMOVED_FROM_PROJECT", - STARRED = "STARRED", - FORK_AGGREGATE_STATS = "FORK_AGGREGATE_STATS", - FORKED = "FORKED", - WATCHER_AGGREGATE_STATS = "WATCHER_AGGREGATE_STATS", -} - -export enum ArtifactType { - EOA_ADDRESS = "EOA_ADDRESS", - SAFE_ADDRESS = "SAFE_ADDRESS", - CONTRACT_ADDRESS = "CONTRACT_ADDRESS", - FACTORY_ADDRESS = "FACTORY_ADDRESS", - GIT_REPOSITORY = "GIT_REPOSITORY", - GIT_EMAIL = "GIT_EMAIL", - GIT_NAME = "GIT_NAME", - GITHUB_ORG = "GITHUB_ORG", - GITHUB_USER = "GITHUB_USER", - NPM_PACKAGE = "NPM_PACKAGE", -} - -export enum ArtifactNamespace { - ETHEREUM = "ETHEREUM", - OPTIMISM = "OPTIMISM", - GOERLI = "GOERLI", - GITHUB = "GITHUB", - GITLAB = "GITLAB", - NPM_REGISTRY = "NPM_REGISTRY", -} - -export enum JobStatus { - PENDING = "PENDING", - COMPLETE = "COMPLETE", - // Used to prevent a job from scheduling temporarily - MANUALLY_LOCKED = "MANUALLY_LOCKED", -} - -export enum JobExecutionStatus { - ACTIVE = "ACTIVE", - COMPLETE = "COMPLETE", - FAILED = "FAILED", -} - -/****************************** - * TABLES - ******************************/ - -abstract class Base extends BaseEntity { - @PrimaryGeneratedColumn() - id: Brand; - - @CreateDateColumn({ type: "timestamptz" }) - createdAt: Date; - @UpdateDateColumn({ type: "timestamptz" }) - updatedAt: Date; - @Column("timestamptz", { nullable: true }) - deletedAt: Date | null; - - toJSON() { - return normalizeToObject(this); - } - - @BeforeInsert() - @BeforeUpdate() - async validate() { - await validateOrReject(this); - } -} - -// We need this to prevent circular references for Typeorm's relational mapping. -// This interface should only need to be internal to this file. -interface IArtifact { - id: Brand; - type: ArtifactType; - namespace: ArtifactNamespace; - name: string; - url: string | null; -} - -@Entity() -@Index(["name"], { unique: true }) -export class CollectionType extends Base<"CollectionTypeId"> { - @Column("text") - name: string; - - @OneToMany(() => Collection, (c) => c.type) - collections: Collection[]; -} - -@Entity() -export class Collection extends Base<"CollectionId"> { - @Column("text") - name: string; - - @Column("text", { nullable: true }) - @IsOptional() - description: string | null; - - @Column("boolean", { default: false }) - verified: boolean; - - @Column("text", { unique: true }) - slug: string; - - @ManyToOne(() => CollectionType, (t) => t.collections) - @JoinColumn() - type: CollectionType; - - @ManyToMany(() => Project, (project) => project.collections) - @JoinTable() - projects: Project[]; - - // Allow artifacts to own collections. These can be dependents or maybe some - // other form of project relations. - @ManyToOne("Artifact", "collections", { - nullable: true, - }) - @JoinColumn() - @IsOptional() - artifactOwner?: IArtifact; - - @OneToMany(() => EventsDailyToCollection, (e) => e.collection) - eventsDailyToCollection: EventsDailyToCollection[]; - @OneToMany(() => EventsWeeklyToCollection, (e) => e.collection) - eventsWeeklyToCollection: EventsWeeklyToCollection[]; - @OneToMany(() => EventsMonthlyToCollection, (e) => e.collection) - eventsMonthlyToCollection: EventsMonthlyToCollection[]; -} - -@Entity() -export class Project extends Base<"ProjectId"> { - @Column("text") - name: string; - - @Column("text", { nullable: true }) - @IsOptional() - description: string | null; - - @Column("boolean", { default: false }) - verified: boolean; - - @Column("text", { unique: true }) - slug: string; - - @ManyToMany(() => Collection, (collection) => collection.projects) - collections: Collection[]; - - @ManyToMany(() => Artifact, (artifact) => artifact.projects) - @JoinTable() - artifacts: Artifact[]; - - @OneToMany(() => EventsDailyToProject, (e) => e.project) - eventsDailyToProject: EventsDailyToProject[]; - @OneToMany(() => EventsWeeklyToProject, (e) => e.project) - eventsWeeklyToProject: EventsWeeklyToProject[]; - @OneToMany(() => EventsMonthlyToProject, (e) => e.project) - eventsMonthlyToProject: EventsMonthlyToProject[]; - @OneToMany(() => EventsDailyFromProject, (e) => e.project) - eventsDailyFromProject: EventsDailyFromProject[]; - @OneToMany(() => EventsWeeklyFromProject, (e) => e.project) - eventsWeeklyFromProject: EventsWeeklyFromProject[]; - @OneToMany(() => EventsMonthlyFromProject, (e) => e.project) - eventsMonthlyFromProject: EventsMonthlyFromProject[]; - - @OneToMany(() => ProjectPackageDependency, (d) => d.dependentProject) - packageDependencies: ProjectPackageDependency[]; - - @OneToMany(() => ProjectPackageDependency, (d) => d.dependencyProject) - dependents: ProjectPackageDependency[]; - - @OneToMany(() => FirstContributionToProject, (event) => event.project) - firstContributionToProjectAsTo: FirstContributionToProject[]; - @OneToMany(() => LastContributionToProject, (event) => event.project) - lastContributionToProjectAsTo: LastContributionToProject[]; -} - -export type ArtifactId = Brand; - -@Entity() -@Index(["namespace", "name"], { unique: true }) -export class Artifact extends Base<"ArtifactId"> { - @Column("enum", { enum: ArtifactType }) - type: ArtifactType; - - @Column("enum", { enum: ArtifactNamespace }) - namespace: ArtifactNamespace; - - @Column("text") - name: string; - - @Column("text", { nullable: true }) - @IsUrl() - @IsOptional() - url: string | null; - - @ManyToMany(() => Project, (project) => project.artifacts) - projects: Project[]; - - @OneToMany(() => Collection, (collection) => collection.artifactOwner) - collections: Collection[]; - - @OneToMany(() => EventsDailyToArtifact, (e) => e.artifact) - eventsDailyToArtifact: EventsDailyToArtifact[]; - @OneToMany(() => EventsWeeklyToArtifact, (e) => e.artifact) - eventsWeeklyToArtifact: EventsWeeklyToArtifact[]; - @OneToMany(() => EventsMonthlyToArtifact, (e) => e.artifact) - eventsMonthlyToArtifact: EventsMonthlyToArtifact[]; - @OneToMany(() => EventsDailyFromArtifact, (e) => e.artifact) - eventsDailyFromArtifact: EventsDailyFromArtifact[]; - @OneToMany(() => EventsWeeklyFromArtifact, (e) => e.artifact) - eventsWeeklyFromArtifact: EventsWeeklyFromArtifact[]; - @OneToMany(() => EventsMonthlyFromArtifact, (e) => e.artifact) - eventsMonthlyFromArtifact: EventsMonthlyFromArtifact[]; - - @OneToMany(() => FirstContributionToProject, (event) => event.from) - firstContributionToProjectAsFrom: FirstContributionToProject[]; - @OneToMany(() => LastContributionToProject, (event) => event.from) - lastContributionToProjectAsFrom: LastContributionToProject[]; - - @OneToMany(() => EventPointer, (eventPointer) => eventPointer.artifact) - eventPointers: EventPointer[]; -} - -@Entity({ name: "event_type" }) -@Index(["name", "version"], { unique: true }) -export class EventType extends Base<"EventTypeId"> { - @Column("varchar", { length: 50 }) - name: string; - - // Allow versioning of events for gradual migrations of data. - @Column("smallint") - version: number; - - @OneToMany(() => FirstContributionToProject, (event) => event.type) - firstContributionsToProject: FirstContributionToProject[]; - @OneToMany(() => LastContributionToProject, (event) => event.type) - lastContributionsToProject: LastContributionToProject[]; -} - -type EventId = Brand; -@Entity() -@Index(["time"]) -@Index(["id", "time"], { unique: true }) -@Index(["sourceId", "typeId", "toId", "time"], { unique: true }) -export class Event { - @PrimaryColumn("integer", { generated: "increment" }) - id: EventId; - - @Column("text") - sourceId: string; - - // The TS property name here is temporary. Will eventually be `type` - @Column("int") - typeId: number; - - @PrimaryColumn("timestamptz") - time: Date; - - @Column("int") - toId: number; - - @Column("int", { nullable: true }) - @IsOptional() - fromId: number | null; - - @Column("float") - amount: number; - - @Column("jsonb", { default: {} }) - details: Record; -} - -export type EventWeakRef = Pick; - -@Entity() -export class RepoDependency { - @PrimaryGeneratedColumn() - id: Brand; - - @ManyToOne(() => Artifact) - repo: Artifact; - - @ManyToOne(() => Artifact) - dependency: Artifact; -} - -@ViewEntity({ - materialized: false, - expression: ` - SELECT - paa_r."projectId" as "dependentProjectId", - "repoId" as "dependentRepoId", - paa_d."projectId" as "dependencyProjectId", - rd."dependencyId" as "dependencyId" - FROM "repo_dependency" rd - INNER JOIN "project_artifacts_artifact" paa_r - on "paa_r"."artifactId" = rd."repoId" - LEFT OUTER JOIN "project_artifacts_artifact" paa_d - on "paa_d"."artifactId" = rd."dependencyId" - `, -}) -export class ProjectPackageDependency { - @ManyToOne(() => Project, (project) => project.packageDependencies) - @ViewColumn() - dependentProject: Project; - - @ViewColumn() - dependentRepoId: number; - - @ManyToOne(() => Project, (project) => project.dependents) - @ViewColumn() - dependencyProject: Project | null; - - @ViewColumn() - dependencyId: number; -} - -@Entity() -export class Recording { - @PrimaryColumn("uuid") - recorderId: string; - - @Column("timestamptz") - expiration: Date; -} - -/** - * A temporary event table used by the Recorder to load events into the - * database. This removes any need for the indexer to ensure that artifacts are - * created _before_ creating events as this is handled in batch by the database. - * - * Additionally, this table has no unique restrictions. This allows us to - * leave in progress writes to inspect errors. - */ -@Entity() -export class RecorderTempEvent { - @PrimaryGeneratedColumn() - id: Brand; - - // This is related to the Recording Entity but is not an explicit FK. To - // hopefully improve write speed. The recorderId is only used during garbage - // collection of this table anyhow. - @Column("uuid") - recorderId: string; - - // Writes to the main event database are made in batches. Anything that fails - // within a specific batch will be left in this database until it's cleaned - // up. This is for debugging purposes. - @Column("int") - batchId: number; - - @Column("text") - sourceId: string; - - // To improve write speed this is not an FK. - @Column("int") - typeId: number; - - @Column("timestamptz") - time: Date; - - @Column("int", { nullable: true }) - toId: number | null; - - @Column("text", { nullable: true }) - @IsOptional() - toName: string | null; - - @Column("enum", { - enum: ArtifactNamespace, - enumName: "artifact_namespace_enum", - nullable: true, - }) - @IsOptional() - toNamespace: ArtifactNamespace | null; - - @Column("enum", { - enum: ArtifactType, - enumName: "artifact_type_enum", - nullable: true, - }) - @IsOptional() - toType: ArtifactType | null; - - @Column("text", { nullable: true }) - @IsOptional() - toUrl?: string | null; - - @Column("int", { nullable: true }) - @IsOptional() - fromId: number | null; - - @Column("text", { nullable: true }) - @IsOptional() - fromName?: string | null; - - @Column("enum", { - enum: ArtifactNamespace, - enumName: "artifact_namespace_enum", - nullable: true, - }) - @IsOptional() - fromNamespace?: string | null; - - @Column("enum", { - enum: ArtifactType, - enumName: "artifact_type_enum", - nullable: true, - }) - @IsOptional() - fromType?: string | null; - - @Column("text", { nullable: true }) - @IsOptional() - fromUrl?: string | null; - - @Column("float") - amount: number; - - @Column("jsonb", { default: {} }) - details: Record; -} - -@ViewEntity({ - expression: ` - WITH to_artifacts AS ( - SELECT - rte_to."toName" as "name", - rte_to."toNamespace" as "namespace", - rte_to."toType" as "type", - rte_to."toUrl" as "url", - rte_to."recorderId" as "recorderId", - rte_to."batchId" as "batchId" - FROM recorder_temp_event as rte_to - ), - from_artifacts AS ( - SELECT - rte_from."fromName" as "name", - rte_from."fromNamespace" as "namespace", - rte_from."fromType" as "type", - rte_from."fromUrl" as "url", - rte_from."recorderId" as "recorderId", - rte_from."batchId" as "batchId" - FROM recorder_temp_event as rte_from - ), all_artifacts AS ( - select * from to_artifacts - UNION - select * from from_artifacts - ) - SELECT - * - FROM all_artifacts a - WHERE - a."name" IS NOT NULL AND - a."namespace" IS NOT NULL AND - a."type" IS NOT NULL - `, -}) -export class RecorderTempEventArtifact { - @ViewColumn() - name: string; - - @ViewColumn() - namespace: ArtifactNamespace; - - @ViewColumn() - type: ArtifactType; - - @ViewColumn() - url?: string | null; - - @ViewColumn() - recorderId: string; - - @ViewColumn() - batchId: number; -} - -/** - * A recorder specific database used to track duplicate events for a given - * collector so that errors can be tracked. - */ -@Entity() -@Index(["typeId", "sourceId", "recorderId"], { unique: true }) -export class RecorderTempDuplicateEvent { - @PrimaryGeneratedColumn() - id: Brand; - - @Column("uuid") - recorderId: string; - - @Column("int") - typeId: number; - - @Column("text") - sourceId: string; -} - -@Entity() -@Index(["artifact", "collector", "startDate", "endDate"], { unique: true }) -export class EventPointer extends Base<"EventPointerId"> { - @ManyToOne(() => Artifact, (artifact) => artifact.eventPointers) - artifact: Artifact; - - @Column("text") - collector: string; - - @Column("timestamptz") - startDate: Date; - - @Column("timestamptz") - endDate: Date; - - @Column("int") - version: number; -} - -@Entity() -@Index(["scheduledTime", "scheduleType", "collector"], { unique: true }) -export class Job extends Base<"JobId"> { - @Column("text", { nullable: true }) - group: string | null; - - @Column("timestamptz") - scheduledTime: Date; - - @Column("text") - collector: string; - - // This is a hack to enable backfill. Fill this with a random value and it - // will allow for specifying "scheduledTime" values that overlap. - @Column("text", { default: "main" }) - scheduleType: string; - - @Column("enum", { enum: JobStatus }) - status: JobStatus; - - // Used to prevent concurrent writes to this object - @Column("integer", { - default: 0, - }) - version: number; - - @Column("jsonb", { - default: {}, - }) - options: Record; - - @OneToMany(() => JobExecution, (jobExecution) => jobExecution.job) - executions: JobExecution[]; -} - -@Entity() -@Index(["name"], { unique: true }) -export class JobGroupLock extends Base<"JobGroupId"> { - @Column("text") - name: string; -} - -@Entity() -@Index(["job", "attempt"], { unique: true }) -export class JobExecution extends Base<"JobExecutionId"> { - @Column("enum", { enum: JobExecutionStatus }) - status: JobExecutionStatus; - - @Column("integer") - attempt: number; - - // Using the updatedAt field to maintain a row lock here doesn't seem to work - // properly. Just using a simple counter. - @Column("integer", { default: 0 }) - version: number; - - @ManyToOne(() => Job, (job) => job.executions) - job: Job; - - @OneToMany(() => Log, (log) => log.execution) - log: Log[]; -} - -@Entity() -export class Log extends Base<"LogId"> { - @Column("text") - level: string; - - @Column("jsonb") - body: Record; - - @ManyToOne(() => JobExecution, (jobExecution) => jobExecution.log) - execution: JobExecution; -} - -/****************************** - * MATERIALIZED VIEWS - * Not all views are possible via TimescaleDB continuous aggregates (e.g. DISTINCT) - ******************************/ - -/** - * For each (to, from, type) tuple, get the first contribution event in time. - */ -@ViewEntity({ - materialized: true, - expression: ` - SELECT DISTINCT ON ("typeId", "projectId", "fromId") - "typeId", - "projectId", - "fromId", - "time", - "id", - "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."toId" - ORDER BY "typeId", "projectId", "fromId", "time" ASC - WITH NO DATA; - `, -}) -export class FirstContributionToProject { - @ManyToOne( - () => EventType, - (eventType) => eventType.firstContributionsToProject, - ) - @ViewColumn() - type: EventType; - - @ManyToOne(() => Project, (project) => project.firstContributionToProjectAsTo) - @ViewColumn() - project: Project; - - @ManyToOne( - () => Artifact, - (artifact) => artifact.firstContributionToProjectAsFrom, - { - nullable: true, - }, - ) - @IsOptional() - @ViewColumn() - from: Artifact | null; - - @ViewColumn() - time: Date; - - @ViewColumn() - id: Brand; - - @ViewColumn() - amount: number; -} - -/** - * For each (to, from, type) tuple, get the last contribution event in time. - */ -@ViewEntity({ - materialized: true, - expression: ` - SELECT DISTINCT ON ("typeId", "projectId", "fromId") - "typeId", - "projectId", - "fromId", - "time", - "id", - "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."toId" - ORDER BY "typeId", "projectId", "fromId", "time" DESC - WITH NO DATA; - `, -}) -export class LastContributionToProject { - @ManyToOne( - () => EventType, - (eventType) => eventType.lastContributionsToProject, - ) - @ViewColumn() - type: EventType; - - @ManyToOne(() => Project, (project) => project.lastContributionToProjectAsTo) - @ViewColumn() - project: Project; - - @ManyToOne( - () => Artifact, - (artifact) => artifact.lastContributionToProjectAsFrom, - { - nullable: true, - }, - ) - @IsOptional() - @ViewColumn() - from: Artifact | null; - - @ViewColumn() - time: Date; - - @ViewColumn() - id: Brand; - - @ViewColumn() - amount: number; -} - -/** - * Get monthly counts of different types of users - */ -@ViewEntity({ - materialized: true, - expression: ` - WITH Devs AS ( - SELECT - p."id" AS "projectId", - e."fromId" AS "fromId", - time_bucket(INTERVAL '1 month', e."time") AS "bucketMonthly", - CASE - WHEN COUNT(DISTINCT CASE WHEN t."name" = 'COMMIT_CODE' THEN e."time" END) >= 10 THEN 'FULL_TIME_DEV' - WHEN COUNT(DISTINCT CASE WHEN t."name" = 'COMMIT_CODE' THEN e."time" END) >= 1 THEN 'PART_TIME_DEV' - ELSE 'OTHER_CONTRIBUTOR' - END AS "segmentType", - 1 AS amount - FROM event e - JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId" - JOIN project p ON paa."projectId" = p.id - JOIN event_type t ON e."typeId" = t.id - WHERE - t."name" IN ( - 'PULL_REQUEST_CREATED', - 'PULL_REQUEST_MERGED', - 'COMMIT_CODE', - 'ISSUE_CLOSED', - 'ISSUE_CREATED' - ) - GROUP BY - p."id", - e."fromId", - "bucketMonthly" - ), - Users AS ( - SELECT - p."id" AS "projectId", - e."fromId" AS "fromId", - time_bucket(INTERVAL '1 month', e."time") AS "bucketMonthly", - CASE - WHEN SUM(e."amount") >= 1000 THEN 'HIGH_FREQUENCY_USER' - WHEN SUM(e."amount") >= 10 THEN 'HIGH_VALUE_USER' - ELSE 'LOW_VALUE_USER' - END AS "segmentType", - 1 AS amount - FROM event e - JOIN project_artifacts_artifact paa ON e."toId" = paa."artifactId" - JOIN project p ON paa."projectId" = p.id - JOIN event_type t ON e."typeId" = t.id - WHERE t."name" = 'CONTRACT_INVOCATION_DAILY_COUNT' - GROUP BY - p."id", - e."fromId", - "bucketMonthly" - ) - SELECT - "projectId", - "segmentType", - "bucketMonthly", - SUM("amount") AS "amount" - FROM - ( - SELECT * FROM Devs - UNION ALL - SELECT * FROM Users - ) combined_data - GROUP BY - "projectId", - "segmentType", - "bucketMonthly" - WITH NO DATA; - `, -}) -export class UsersMonthlyToProject { - @ViewColumn() - project: Project; - - @ViewColumn() - segmentType: string; - - @ViewColumn() - bucketMonthly: Date; - - @ViewColumn() - amount: number; -} - -/****************************** - * TIMESCALEDB CONTINUOUS AGGREGATES - ******************************/ - -/** - * Continuous aggregations to an artifact - */ -@ViewEntity({ - materialized: true, - expression: ` - SELECT "toId" AS "artifactId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - GROUP BY "artifactId", "typeId", "bucketDaily" - WITH NO DATA; - `, -}) -export class EventsDailyToArtifact { - @ManyToOne(() => Artifact, (artifact) => artifact.eventsDailyToArtifact) - @ViewColumn() - artifact: Artifact; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketDaily: Date; - - @ViewColumn() - amount: number; -} - -@ViewEntity({ - materialized: true, - expression: ` - SELECT "artifactId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDaily") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_to_artifact" - GROUP BY "artifactId", "typeId", "bucketWeekly" - WITH NO DATA; - `, -}) -export class EventsWeeklyToArtifact { - @ManyToOne(() => Artifact, (artifact) => artifact.eventsWeeklyToArtifact) - @ViewColumn() - artifact: Artifact; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketWeekly: Date; - - @ViewColumn() - amount: number; -} - -@ViewEntity({ - materialized: true, - expression: ` - SELECT "artifactId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDaily") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_to_artifact" - GROUP BY "artifactId", "typeId", "bucketMonthly" - WITH NO DATA; - `, -}) -export class EventsMonthlyToArtifact { - @ManyToOne(() => Artifact, (artifact) => artifact.eventsMonthlyToArtifact) - @ViewColumn() - artifact: Artifact; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketMonthly: Date; - - @ViewColumn() - amount: number; -} - -/** - * Continuous aggregations to a project - */ -@ViewEntity({ - materialized: true, - expression: ` - SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."toId" - GROUP BY "projectId", "typeId", "bucketDaily" - WITH NO DATA; - `, -}) -export class EventsDailyToProject { - @ManyToOne(() => Project, (project) => project.eventsDailyToProject) - @ViewColumn() - project: Project; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketDaily: Date; - - @ViewColumn() - amount: number; -} - -@ViewEntity({ - materialized: true, - expression: ` - SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDaily") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_to_project" - GROUP BY "projectId", "typeId", "bucketWeekly" - WITH NO DATA; - `, -}) -export class EventsWeeklyToProject { - @ManyToOne(() => Project, (project) => project.eventsWeeklyToProject) - @ViewColumn() - project: Project; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketWeekly: Date; - - @ViewColumn() - amount: number; -} - -@ViewEntity({ - materialized: true, - expression: ` - SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDaily") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_to_project" - GROUP BY "projectId", "typeId", "bucketMonthly" - WITH NO DATA; - `, -}) -export class EventsMonthlyToProject { - @ManyToOne(() => Project, (project) => project.eventsMonthlyToProject) - @ViewColumn() - project: Project; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketMonthly: Date; - - @ViewColumn() - amount: number; -} - -/** - * Continuous aggregations to a Collection - */ -@ViewEntity({ - materialized: true, - expression: ` - SELECT "collectionId", - "typeId", - time_bucket(INTERVAL '1 day', "bucketDaily") AS "bucketDay", - SUM(amount) as "amount" - FROM "events_daily_to_project" - INNER JOIN "collection_projects_project" - ON "collection_projects_project"."projectId" = "events_daily_to_project"."projectId" - GROUP BY "collectionId", "typeId", "bucketDay" - WITH NO DATA; - `, -}) -export class EventsDailyToCollection { - @ManyToOne( - () => Collection, - (collection) => collection.eventsDailyToCollection, - ) - @ViewColumn() - collection: Collection; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketDaily: Date; - - @ViewColumn() - amount: number; -} - -@ViewEntity({ - materialized: true, - expression: ` - SELECT "collectionId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDay") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_to_collection" - GROUP BY "collectionId", "typeId", "bucketWeekly" - WITH NO DATA; - `, -}) -export class EventsWeeklyToCollection { - @ManyToOne( - () => Collection, - (collection) => collection.eventsWeeklyToCollection, - ) - @ViewColumn() - collection: Collection; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketWeekly: Date; - - @ViewColumn() - amount: number; -} - -@ViewEntity({ - materialized: true, - expression: ` - SELECT "collectionId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDay") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_to_collection" - GROUP BY "collectionId", "typeId", "bucketMonthly" - WITH NO DATA; - `, -}) -export class EventsMonthlyToCollection { - @ManyToOne( - () => Collection, - (collection) => collection.eventsMonthlyToCollection, - ) - @ViewColumn() - collection: Collection; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketMonthly: Date; - - @ViewColumn() - amount: number; -} - -/** - * Continuous aggregates from an artifact - */ -@ViewEntity({ - materialized: true, - expression: ` - SELECT "fromId" AS "artifactId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - GROUP BY "artifactId", "typeId", "bucketDaily" - WITH NO DATA; - `, -}) -export class EventsDailyFromArtifact { - @ManyToOne(() => Artifact, (artifact) => artifact.eventsDailyFromArtifact) - @ViewColumn() - artifact: Artifact; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketDaily: Date; - - @ViewColumn() - amount: number; -} - -@ViewEntity({ - materialized: true, - expression: ` - SELECT "artifactId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDaily") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_from_artifact" - GROUP BY "artifactId", "typeId", "bucketWeekly" - WITH NO DATA; - `, -}) -export class EventsWeeklyFromArtifact { - @ManyToOne(() => Artifact, (artifact) => artifact.eventsWeeklyFromArtifact) - @ViewColumn() - artifact: Artifact; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketWeekly: Date; - - @ViewColumn() - amount: number; -} - -@ViewEntity({ - materialized: true, - expression: ` - SELECT "artifactId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDaily") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_from_artifact" - GROUP BY "artifactId", "typeId", "bucketMonthly" - WITH NO DATA; - `, -}) -export class EventsMonthlyFromArtifact { - @ManyToOne(() => Artifact, (artifact) => artifact.eventsMonthlyFromArtifact) - @ViewColumn() - artifact: Artifact; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketMonthly: Date; - - @ViewColumn() - amount: number; -} - -/** - * Continuous aggregates from a project - */ -@ViewEntity({ - materialized: true, - expression: ` - SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 day', "time") AS "bucketDaily", - SUM(amount) as "amount" - FROM "event" - INNER JOIN "project_artifacts_artifact" - on "project_artifacts_artifact"."artifactId" = "event"."fromId" - GROUP BY "projectId", "typeId", "bucketDaily" - WITH NO DATA; - `, -}) -export class EventsDailyFromProject { - @ManyToOne(() => Project, (project) => project.eventsDailyFromProject) - @ViewColumn() - project: Project; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketDaily: Date; - - @ViewColumn() - amount: number; -} - -@ViewEntity({ - materialized: true, - expression: ` - SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 week', "bucketDaily") AS "bucketWeekly", - SUM(amount) as "amount" - FROM "events_daily_from_project" - GROUP BY "projectId", "typeId", "bucketWeekly" - WITH NO DATA; - `, -}) -export class EventsWeeklyFromProject { - @ManyToOne(() => Project, (project) => project.eventsWeeklyFromProject) - @ViewColumn() - project: Project; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketWeekly: Date; - - @ViewColumn() - amount: number; -} - -@ViewEntity({ - materialized: true, - expression: ` - SELECT "projectId", - "typeId", - time_bucket(INTERVAL '1 month', "bucketDaily") AS "bucketMonthly", - SUM(amount) as "amount" - FROM "events_daily_from_project" - GROUP BY "projectId", "typeId", "bucketMonthly" - WITH NO DATA; - `, -}) -export class EventsMonthlyFromProject { - @ManyToOne(() => Project, (project) => project.eventsMonthlyFromProject) - @ViewColumn() - project: Project; - - @ViewColumn() - type: EventType; - - @ViewColumn() - bucketMonthly: Date; - - @ViewColumn() - amount: number; -} diff --git a/indexer/src/db/project.ts b/indexer/src/db/project.ts deleted file mode 100644 index ada8812ff..000000000 --- a/indexer/src/db/project.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { In } from "typeorm"; -import { AppDataSource } from "./data-source.js"; -import { ArtifactType, Project } from "./orm-entities.js"; - -export const ProjectRepository = AppDataSource.getRepository(Project).extend({ - async allFundableProjectsWithAddresses() { - return await this.find({ - relations: { - artifacts: true, - }, - where: { - artifacts: { - type: In([ArtifactType.EOA_ADDRESS, ArtifactType.SAFE_ADDRESS]), - }, - }, - }); - }, - async nonCanonical() { - const sumString = - 'SUM(CASE WHEN LOWER(p."slug") = p."slug" THEN 0 ELSE 1 END)'; - return (await this.manager - .createQueryBuilder() - .select() - .addSelect('p."name"', "name") - .addSelect('p."slug"', "slug") - .addSelect(sumString, "nonCanonicalCount") - .from(Project, "p") - .groupBy("1,2") - .having(`${sumString} > 0`) - .getRawMany()) as { - name: string; - slug: string; - nonCanonicalCount: number; - }[]; - }, - // Find any duplicates - async duplicates() { - return (await this.manager - .createQueryBuilder() - .select() - .addSelect('lower(p."name")', "name") - .addSelect('lower(p."slug")', "slug") - .addSelect('count(p."id")', "count") - .addSelect('array_agg(p."id")', "ids") - .addSelect('array_agg(p."name")', "names") - .from(Project, "p") - .groupBy("1,2") - .having('count(p."id") > 1') - .getRawMany()) as { - name: string; - slug: string; - count: number; - ids: number[]; - names: string[]; - }[]; - }, -}); diff --git a/indexer/src/db/testing.ts b/indexer/src/db/testing.ts deleted file mode 100644 index 6b995eea5..000000000 --- a/indexer/src/db/testing.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { TEST_ONLY_ALLOW_CLEAR_DB, ENABLE_DB_TESTS } from "../config.js"; -import { logger } from "../utils/logger.js"; -import { AppDataSource } from "./data-source.js"; -import { it, describe, beforeEach, afterAll } from "@jest/globals"; -import { randomUUID } from "crypto"; -import { EventTypeEnum } from "./orm-entities.js"; - -// Testing utilities for the database -export async function clearDb() { - if (!TEST_ONLY_ALLOW_CLEAR_DB) { - logger.warn( - "a code path attempted to clear the database when it is intended only for testing", - ); - throw new Error("clearing the database is not allowed"); - } - - const c = await initializeOnce(); - const entities = c.entityMetadatas; - for (const entity of entities) { - if (entity.tableMetadataArgs.type === "view") { - continue; - } - const repository = AppDataSource.getRepository(entity.name); - await repository.query( - `TRUNCATE ${entity.tableName} RESTART IDENTITY CASCADE`, - ); - } - // Initialize the event type table. It's fairly critical and the application - // tends to treat it as static. - const enumValues = Object.keys(EventTypeEnum); - const keys = Object.values(enumValues); - - // Initialize the event type table. Not worrying about sql injection - // here. We're the ones providing input. - await c.createQueryRunner().query( - ` - insert into event_type(name, version) - select unnest($1::text[]), 1 - `, - [keys], - ); -} - -type IT_PARAMS = Parameters; -type DESCRIBE_PARAMS = Parameters; - -/** - * Helper for writing db tests. Should only be used as a top level describe. Use - * `describe()` for any subsequent levels of describe. - */ -export function withDbDescribe(...args: DESCRIBE_PARAMS) { - const id = randomUUID(); - if (ENABLE_DB_TESTS) { - describe(`Database setup for ${args[0]}: ${id}`, () => { - beforeEach(async () => { - try { - await clearDb(); - } catch (err) { - // eslint-disable-next-line no-restricted-properties - console.error(err); - throw err; - } - }); - - afterAll(async () => { - try { - await AppDataSource.destroy(); - } catch (_e) { - console.log("data source already disconnected"); - } - }); - - describe(...args); - }); - } else { - describe.skip(...args); - } -} - -export function withDbIt(...args: IT_PARAMS) { - if (ENABLE_DB_TESTS) { - return it(...args); - } else { - return it.skip(...args); - } -} - -export async function initializeOnce() { - if (AppDataSource.isInitialized) { - await AppDataSource.destroy(); - } - return await AppDataSource.initialize(); -} diff --git a/indexer/src/events/funding_rounds.py b/indexer/src/events/funding_rounds.py deleted file mode 100644 index f1c95839e..000000000 --- a/indexer/src/events/funding_rounds.py +++ /dev/null @@ -1,98 +0,0 @@ -from dotenv import load_dotenv -from datetime import datetime -import json -import os -import requests - -from web3 import Web3 - - -load_dotenv() -ALCHEMY_KEY = os.environ['ALCHEMY_KEY'] -ALCHEMY_KEY_OP = os.environ['ALCHEMY_KEY_OP'] -ETHERSCAN = os.environ['ETHERSCAN_KEY'] -ETHERSCAN_OP = os.environ['ETHERSCAN_KEY_OP'] - - -# create a web3 connection -w3 = Web3(Web3.HTTPProvider(f"https://eth-mainnet.g.alchemy.com/v2/{ALCHEMY_KEY}")) -op = Web3(Web3.HTTPProvider(f"https://opt-mainnet.g.alchemy.com/v2/{ALCHEMY_KEY_OP}")) - -# Grants Database -# TODO refactor to pull from Supabase -GRANTS = { - "Optimism RetroPGF": { - "chain": "optimism", - "address": "0x19793c7824be70ec58bb673ca42d2779d12581be", - "action": "tokentx", - "token_symbols": ['OP'] - }, - "Gitcoin Grants - Test": { - "chain": "mainnet", - "address": "0x7d655c57f71464B6f83811C55D84009Cd9f5221C", - "action": "txlist", - "token_symbols": ['ETH', 'DAI'] - } -} - - -def convert_timestamp(ts): - - fmt = "%Y-%m-%d %H:%M:%S" - return datetime.fromtimestamp(int(ts)).strftime(fmt) - - -def get_txs(grant_data, start=0): - - if grant_data['chain'] == 'optimism': - subdomain = "api-optimistic" - apikey = ETHERSCAN_OP - else: - subdomain = "api" - apikey = ETHERSCAN - - end = 999999999 - headers = {"Accept": "application/json"} - - apicall = "&".join([ - f"https://{subdomain}.etherscan.io/api?module=account", - f"address={grant_data['address']}", - f"action={grant_data['action']}", - f"startblock={start}&endblock={end}", - f"apikey={apikey}" ]) - - response = requests.get(apicall, headers=headers) - json_data = response.json() - if json_data['message'] == 'OK': - result = json_data['result'] - return result - - -def get_transfers(grant): - - tx_data = get_txs(grant) - - if not tx_data: - return - - data = [] - for x in tx_data: - data.append({ - 'timestamp': convert_timestamp(x['timeStamp']), - 'data_source': 'etherscan', - 'event_type': 'grant', - 'amount': float(x['value']) / (10**int(x.get('tokenDecimal', 18))), - 'details': x - }) - - return data - - -if __name__ == '__main__': - - grant = GRANTS.get("Optimism RetroPGF") - data = get_transfers(grant) - print(data[-1]) - print(len(data)) - - #data = get_gitcoin_grants_transfers() \ No newline at end of file diff --git a/indexer/src/events/github_events.py b/indexer/src/events/github_events.py deleted file mode 100644 index 5de3b360c..000000000 --- a/indexer/src/events/github_events.py +++ /dev/null @@ -1,145 +0,0 @@ -from datetime import datetime, timedelta -from dotenv import load_dotenv -import logging -import os -import requests - -from events.graphql_queries import QUERIES - -# Configure logging -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(levelname)s - %(message)s', - filename='logging.log' -) - -# -------------- HELPER FUNCTIONS -------------- # - -date_fmt = "%Y-%m-%dT%H:%M:%SZ" - -def to_date(date): - return date.strftime(date_fmt) - - -def find_dict_with_pageinfo(data): - if isinstance(data, dict): - for k, v in data.items(): - if v and 'pageInfo' in v: - return data[k] - result = find_dict_with_pageinfo(v) - if result is not None: - return result - return None - - -def flatten_dict(d): - flattened_dict = {} - for key, value in d.items(): - if isinstance(value, dict): - inner_dict = flatten_dict(value) - flattened_dict.update({f"{key}.{inner_key}": inner_value for inner_key, inner_value in inner_dict.items()}) - elif isinstance(value, list): - flattened_dict.update({f"{key}.{i}": item for i, item in enumerate(value)}) - else: - flattened_dict[key] = value - return flattened_dict - - -# -------------- QUERY CONSTRUCTORS -------------- # - -def run_query_for_org(query_func, github_org, start_date, end_date): - load_dotenv() - github_token = os.getenv('GITHUB_TOKEN') - - end_cursor = "null" - has_next_page = True - rate_limit_hit = False - events = [] - - while has_next_page: - query_string = query_func( - org=github_org, - first=100, - after=end_cursor, - since=start_date, - until=end_date - ) - response = requests.post( - 'https://api.github.com/graphql', - json={'query': query_string}, - headers={'Authorization': f'token {github_token}'} - ) - response_json = response.json() - data = find_dict_with_pageinfo(response_json.get('data')) - if not data: - logging.info(f"No items found for {github_org}.") - break - - key = data.get('edges') and "edges" or "nodes" - events.extend(data.get(key, [])) - - has_next_page = data['pageInfo']['hasNextPage'] - if has_next_page: - end_cursor = data['pageInfo']['endCursor'] - # cursor must be passed in quotes - end_cursor = f'"{end_cursor}"' - - if len(events) == 1000: - rate_limit_hit = True - logging.warning("Hit max limit of 1000 results. Retrying") - - return events, rate_limit_hit - - -def paginate_query_for_org(query_func, github_org, start_date, end_date): - start_dt = datetime.strptime(start_date, date_fmt) - max_dt = datetime.strptime(end_date, date_fmt) - delta_days = 180 - events = [] - - while start_dt < max_dt: - end_dt = start_dt + timedelta(days=delta_days) - if end_dt > max_dt: - end_dt = max_dt - logging.info("Attempting: %s - %s", to_date(start_dt), to_date(end_dt)) - new_events, rate_limit_hit = run_query_for_org(query_func, github_org, to_date(start_dt), to_date(end_dt)) - if rate_limit_hit: - delta_days //= 3 - else: - events.extend(new_events) - start_dt += timedelta(days=delta_days) - - return events - - -def execute_org_query(query_idx, github_org, start_date, end_date): - query = QUERIES[query_idx] - query_func = query['func'] - - events, rate_limit_hit = run_query_for_org(query_func, github_org, start_date, end_date) - if rate_limit_hit: - events = paginate_query_for_org(query_func, github_org, start_date, end_date) - - logging.info("Query %s: %d events found for `%s`", query['name'], len(events), github_org) - - records = [] - for event in events: - details = { - "source": "github", - "data": flatten_dict(event) - } - timestamp = details['data'].get(query.get('timestamp')) - contributor = details['data'].get(query.get('contributor')) - record = { - "event_time": timestamp or None, - "event_type": query['name'], - "contributor": contributor or None, - "amount": 1, - "details": details - } - records.append(record) - return records - - -if __name__ == "__main__": - test = execute_org_query(0, "gitcoinco", "2022-01-01T00:00:00Z", "2023-04-22T00:00:00Z") diff --git a/indexer/src/events/github_user.py b/indexer/src/events/github_user.py deleted file mode 100644 index d829f07c0..000000000 --- a/indexer/src/events/github_user.py +++ /dev/null @@ -1,56 +0,0 @@ -from datetime import datetime, timedelta -from dotenv import load_dotenv -import os -import requests - - -def validate_github_user(username, start_date=None, end_date=None): - - if not isinstance(username, str): - return None - - date_fmt = "%Y-%m-%dT%H:%M:%SZ" - if not start_date and not end_date: - start_date = (datetime.today() - timedelta(days=365)).strftime(date_fmt) - end_date = datetime.today().strftime(date_fmt) - - query = """ - query ContributionsView($username: String!, $from: DateTime!, $to: DateTime!) { - user(login: $username) { - contributionsCollection(from: $from, to: $to) { - totalCommitContributions - totalIssueContributions - totalPullRequestContributions - totalPullRequestReviewContributions - } - } - } - """ - - load_dotenv() - token = os.getenv('GITHUB_TOKEN') - headers = {'Authorization': f'token {token}'} - variables = { - "username": username, - "from": start_date, - "to": end_date - } - response = requests.post( - 'https://api.github.com/graphql', - json={'query': query, 'variables': variables}, - headers=headers - ) - try: - response_json = response.json() - data = response_json['data'] - contributions = data['user']['contributionsCollection'] - return contributions - except: - print(f"Encountered error retrieving contributions for https://github.com/{username}.") - print(response_json) - return None - - -if __name__ == "__main__": - # test case - validate_github_user("ccerv1") diff --git a/indexer/src/events/graphql_queries.py b/indexer/src/events/graphql_queries.py deleted file mode 100644 index 983af68ad..000000000 --- a/indexer/src/events/graphql_queries.py +++ /dev/null @@ -1,177 +0,0 @@ -# GraphQL Query Strings - - -def query_commits(org, first, after, since, until): - # currently not active / working - return f''' - {{ - repository( - org: "{org}" - ) {{ - defaultBranchRef {{ - target {{ - ... on Commit {{ - history( - first: {first} - after: {after} - since: "{since}" - until: "{until}" - ) {{ - pageInfo {{ - hasNextPage - endCursor - }} - node {{ - committedDate - author {{ - user {{ - login - }} - }} - additions - deletions - message - repository {{ - name - }} - url - }} - }} - }} - }} - }} - }} - }} - ''' - - -def query_merged_prs(org, first, after, since, until): - return f''' - {{ - search( - query: "org:{org} is:pr is:merged merged:{since}..{until}" - first: {first} - after: {after} - type: ISSUE - ) {{ - pageInfo {{ - hasNextPage - endCursor - }} - nodes {{ - ... on PullRequest {{ - createdAt - mergedAt - mergedBy {{ - login - }} - author {{ - login - }} - title - repository {{ - name - }} - url - }} - }} - }} - }} - ''' - - -def query_created_prs(org, first, after, since, until): - return f''' - {{ - search( - query: "org:{org} is:pr created:{since}..{until}" - first: {first} - after: {after} - type: ISSUE - ) {{ - pageInfo {{ - hasNextPage - endCursor - }} - nodes {{ - ... on PullRequest {{ - createdAt - mergedAt - mergedBy {{ - login - }} - author {{ - login - }} - title - repository {{ - name - }} - url - }} - }} - }} - }} - ''' - - -def query_issues(org, first, after, since, until): - return f''' - {{ - search( - query: "org:{org} is:issue -reason:NOT_PLANNED created:{since}..{until}" - first: {first} - after: {after} - type: ISSUE - ) {{ - pageInfo {{ - hasNextPage - endCursor - }} - nodes {{ - ... on Issue {{ - createdAt - closedAt - author {{ - login - }} - title - repository {{ - name - }} - url - state - stateReason - }} - }} - }} - }} - ''' - - -QUERIES = [ - # { - # "name": "commits", - # "func": query_commits, - # "timestamp": "committedDate", - # "contributor": "author.user.login" - # }, - { - "name": "merged PR", - "func": query_merged_prs, - "timestamp": "mergedAt", - "contributor": "mergedBy.login" - }, - { - "name": "issue", - "func": query_issues, - "timestamp": "createdAt", - "contributor": "author.login" - }, - { - "name": "created PR", - "func": query_created_prs, - "timestamp": "createdAt", - "contributor": "author.login" - } -] \ No newline at end of file diff --git a/indexer/src/events/readme.md b/indexer/src/events/readme.md deleted file mode 100644 index 68210bf1c..000000000 --- a/indexer/src/events/readme.md +++ /dev/null @@ -1,93 +0,0 @@ -# Github Event Tracking - -## User Story - -As a contributor or analyst, I'd like to see documentation that shows the current set of queries that are being tracked as GitHub events as well as a GraphQL sandbox for testing out the queries. - -## Query Tracking - -The following queries are currently being tracked as events for a given Github organization: - -- Created PR -- Merged PR -- Created Issue - -## Creating New Queries - -New queries should have the following fields: - -- `id` (auto-generated) -- `name` (see examples above) -- `source` (set to "github") -- `units` (most queries return an array of event nodes, so this can be set to "nodes") -- `query_function` (see below) - -### Example `query_function`: Merged PR - -**Parameters:** - -- `$org`: the Github organization name (eg, `hypercerts-org`) -- `$since`: a Github compatible timestamp, eg, `2022-01-01T00:00:00Z` -- `$until`: a Github compatible timestamp, eg, `2023-04-22T00:00:00Z` - -**Query:** - -```graphql -{ - search( - query: "org:$org is:pr is:merged merged:$since..$until" - first: 100 - type: ISSUE - ) { - pageInfo { - hasNextPage - endCursor - } - nodes { - ... on PullRequest { - title - url - createdAt - mergedAt - mergedBy { - login - } - author { - login - } - } - } - } -} -``` - -## Normalizing Query Results - -The result should then be normalized as follows: - -- `id` (auto-generated) -- `project_id` (the name of the Github organization being queried) -- `observer_id` (the name of the query) -- `timestamp` (when the event occured on Github) -- `amount` (always set to 1.0) -- `details` (a flattened JSON of all the other information returned by the query) - -## Testing Queries - -To test out the queries, you can use GitHub's GraphQL API Explorer. - -1. Login to GitHub and go to the [Explorer](https://docs.github.com/en/graphql/overview/explorer). Once there, you will see an interactive GraphQL environment where you can enter queries. - -2. Enter your desired query in the provided input field. - -3. Click the "Play" button to execute the query. The results of the query will be displayed in the response panel below. - -4. Analyze the results and iterate on your query as needed. - -### Query Variables - -If your query requires variables, you can provide them in the "Query Variables" section of the GraphQL Explorer. This allows you to pass dynamic values to your queries. - -### Rate Limiting - -The GitHub API limits each request to 100 records and 10 pages. This means you can't extract more than 1000 records from a single query. You can break the query up into smaller pieces by reducing the date range, or providing other filtering parameters. diff --git a/indexer/src/events/zerion_scraper.py b/indexer/src/events/zerion_scraper.py deleted file mode 100644 index d65b571fa..000000000 --- a/indexer/src/events/zerion_scraper.py +++ /dev/null @@ -1,230 +0,0 @@ -from dotenv import load_dotenv -import json -import os -import pandas as pd -import shutil -import time - -from supabase import create_client, Client - -from selenium import webdriver -from selenium.webdriver.chrome.options import Options -from selenium.webdriver.common.by import By -from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.support.ui import WebDriverWait - -from webdriver_manager.chrome import ChromeDriverManager - -# -------------------- DATABASE -------------------- # - -load_dotenv() -url = os.environ.get("SUPABASE_URL") -key = os.environ.get("SUPABASE_KEY") -supabase: Client = create_client(url, key) - - -def fetch_list_of_wallets(): - response = (supabase - .table('wallets') - .select('address, project_id') - .execute()) - - return response.data - -# -------------------- SCRAPER -------------------- # - -DOWNLOAD_DIR = os.getcwd() -STORAGE_DIR = "data/temp" - -SLEEP = 10 -MAX_TRIES = 5 - - -def download_zerion_history(driver, wallet_address): - destination = f'{STORAGE_DIR}/{wallet_address}.csv' - url = f'https://app.zerion.io/{wallet_address}/history' - - driver.get(url) - print(f"Loading Zerion history at: {url}") - time.sleep(SLEEP) # wait for page to load - try: - accept_button = driver.find_element(By.XPATH, "//*[contains(text(),'Accept')]/ancestor::button") - accept_button.click() - time.sleep(SLEEP) # wait for cookies to be accepted - except: - pass - - try: - print("Clicking download button...") - button = WebDriverWait(driver, SLEEP * 2).until( - EC.element_to_be_clickable((By.XPATH, "//button[contains(.,'Download CSV')]")) - ) - button.click() - except: - print("Zerion appears unable to support analytics for this address:", url) - return - - for tries in range(0, 5): - time.sleep(SLEEP) # wait for download to complete - files = [ - os.path.join(DOWNLOAD_DIR, f) - for f in os.listdir(DOWNLOAD_DIR) - if ".csv" in f - ] - if files: - downloaded_file = max(files, key=os.path.getctime) - if wallet_address in downloaded_file.lower(): - shutil.move(downloaded_file, destination) - print(f"Successfully downloaded Zerion history to: {destination}.") - return - print("Most recent file:", downloaded_file) - - print("Unable to find a history for this address:", url) - - -def retrieve_wallet_history(wallet_address, override=False): - wallet_address = wallet_address.lower() - if not override: - destination = f'{STORAGE_DIR}/{wallet_address}.csv' - if os.path.isfile(destination): - print(f"File for {wallet_address} already exists. Skipping download.") - return - - chrome_options = Options() - chrome_options.add_argument('--headless') - - # Automatically download and configure the appropriate ChromeDriver - driver = webdriver.Chrome(ChromeDriverManager().install(), options=chrome_options) - - download_zerion_history(driver, wallet_address) - driver.quit() - - -def run_scraper(override=False): - - try: - - test = retrieve_wallet_history('0xc44F30Be3eBBEfdDBB5a85168710b4f0e18f4Ff0') - - wallet_data = fetch_list_of_wallets() - for w in wallet_data: - addr = w['address'] - retrieve_wallet_history(addr, override=override) - - except Exception as e: - print(f"An error occurred: {e}") - print("Attempting to download the appropriate ChromeDriver...") - - try: - webdriver.Chrome(ChromeDriverManager().install()) - print("ChromeDriver installation successful.") - print("Please run the script again to continue.") - except Exception as e: - print("Failed to download ChromeDriver. Please install it manually.") - print(f"Error message: {e}") - -# -------------------- ETL HELPERS -------------------- # - -STR_FIELDS = [ - 'Transaction Type', 'Status', 'Chain', - 'Buy Currency', 'Buy Currency Address', 'Buy Fiat Currency', - 'Sell Currency', 'Sell Currency Address', 'Sell Fiat Currency', - 'Fee Currency', 'Fee Fiat Currency', - 'Tx Hash', 'Link', 'Timestamp' -] - -FLT_FIELDS = [ - 'Buy Amount', 'Buy Fiat Amount', - 'Sell Amount', 'Sell Fiat Amount', - 'Fee Amount', 'Fee Fiat Amount' -] - -FIELDS = STR_FIELDS + FLT_FIELDS - - -def to_numeric(x): - if pd.isna(x): - return 0 - try: - return float(x) - except: - if isinstance(x, str): - if "\n" in x: - return to_numeric(x.split("\n")[0]) - - -def classify_event(event_type, status, buy, sell, fee): - if status == 'Confirmed': - if event_type == 'receive': - return ('funds received', buy) - if event_type == 'send': - return ('funds sent', sell+fee) - return ('transaction executed', fee) - - -def consolidate_csvs(storage_dir): - - wallet_data = fetch_list_of_wallets() - wallet_mapping = {w['address'].lower(): w['project_id'] for w in wallet_data} - csv_paths = [f for f in os.listdir(storage_dir) if f.endswith(".csv")] - - dfs = [] - for file in csv_paths: - df = pd.read_csv(os.path.join(storage_dir, file), usecols=FIELDS) - for c in df.columns: - if c in FLT_FIELDS: - df[c] = df[c].apply(to_numeric) - df.fillna("", inplace=True) - - wallet = os.path.splitext(file)[0].lower() - project_id = wallet_mapping.get(wallet) - if not project_id: - print(f"Unable to locate a project associated with wallet {wallet}") - continue - - df['details'] = df.apply(lambda x: {'data': dict(x), 'source': 'zerion'}, axis=1) - df['project_id'] = project_id - df['event_time'] = df['Timestamp'] - df['event_type'], df['amount'] = zip( - *df.apply(lambda x: classify_event( - x['Transaction Type'], - x['Status'], - x['Buy Fiat Amount'], - x['Sell Fiat Amount'], - x['Fee Fiat Amount'] - ), axis=1) - ) - - df.drop(columns=FIELDS, inplace=True) - dfs.append(df) - - return pd.concat(dfs, axis=0, ignore_index=True) - - -def convert_csvs_to_records(): - df = consolidate_csvs(STORAGE_DIR) - records = df.to_dict(orient='records') - return records - - -def identify_funding_events(): - records = convert_csvs_to_records() - funding_events = [] - for r in records: - if r['event_type'] != 'funds received': - continue - details = r.pop('details')['data'] - r.update(details) - funding_events.append(r) - return funding_events - - - -# -------------------- MAIN SCRIPT -------------------- # - -if __name__ == '__main__': - - run_scraper(override=False) - # convert_csvs_to_records() - - diff --git a/indexer/src/index.ts b/indexer/src/index.ts deleted file mode 100644 index 47806af40..000000000 --- a/indexer/src/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { - Event, - EventPointer, - EventType, - Artifact, - ArtifactNamespace, - ArtifactType, - Project, - Collection, -} from "./db/orm-entities.js"; -export { - Event, - EventType, - EventPointer, - Artifact, - ArtifactNamespace, - ArtifactType, - Project, - Collection, -}; - -import { - getCollectionBySlug, - getProjectBySlug, - getArtifactByName, -} from "./db/entities.js"; -export { getCollectionBySlug, getProjectBySlug, getArtifactByName }; - -import { initializeDataSource } from "./db/data-source.js"; -export { initializeDataSource }; - -import * as utils from "./utils/common.js"; -export { utils }; - -//import { NpmDownloadsArgs, npmDownloads } from "./events/npm.js"; -//export { NpmDownloadsArgs, npmDownloads }; diff --git a/indexer/src/recorder/actors.ts b/indexer/src/recorder/actors.ts deleted file mode 100644 index dbb6feec1..000000000 --- a/indexer/src/recorder/actors.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { Artifact, ArtifactNamespace } from "../db/orm-entities.js"; -import { - IActorDirectory, - IncompleteArtifact, - IncompleteActor, - UnknownActor, -} from "./types.js"; - -type IdType = string | number; - -export class ActorLookup< - I extends IdType, - V extends IncompleteActor & { id: I }, - N extends string, - M extends ArtifactNamespace, -> { - private incompleteLookup: Record>; - private idLookup: Record; - - constructor() { - this.incompleteLookup = {} as Record>; - this.idLookup = {} as Record; - } - - add(item: V) { - const namespaceLookup = this.getNamespaceLookup(item.namespace); - namespaceLookup[item.name] = item; - - this.idLookup[item.id] = item; - } - - private getNamespaceLookup(namespace: M) { - let namespaceLookup = this.incompleteLookup[namespace]; - if (!namespaceLookup) { - this.incompleteLookup[namespace] = {} as Record; - namespaceLookup = this.incompleteLookup[namespace]; - } - return namespaceLookup; - } - - byString(item: IncompleteActor): V { - return this.getNamespaceLookup(item.namespace)[item.name]; - } - - byId(id: I): V { - return this.idLookup[id]; - } - - knownSetOf(actors: IncompleteActor[]): V[] { - const known: V[] = []; - actors.forEach((actor) => { - const resolved = this.byString(actor); - if (resolved) { - known.push(resolved); - } - }); - return known; - } - - unknownSetOf>(actors: T[]): T[] { - return actors.filter((actor) => { - return !this.byString(actor); - }); - } -} - -export class InmemActorResolver implements IActorDirectory { - private artifactsLookup: ActorLookup< - number, - Artifact, - string, - ArtifactNamespace - >; - - constructor() { - this.artifactsLookup = new ActorLookup(); - } - - async fromId(id: number): Promise { - return this.artifactsLookup.byId(id); - } - - async resolveArtifactId(artifact: IncompleteArtifact): Promise { - const resolved = this.artifactsLookup.byString(artifact); - if (!resolved) { - throw new UnknownActor( - `Artifact unknown name=${artifact.name} namespace=${artifact.namespace} ${artifact.type}`, - ); - } - return resolved.id; - } - - loadArtifact(artifact: Artifact) { - this.artifactsLookup.add(artifact); - } - - knownArtifactsFrom(artifacts: IncompleteArtifact[]): Artifact[] { - return this.artifactsLookup.knownSetOf(artifacts); - } - - unknownArtifactsFrom(artifacts: IncompleteArtifact[]): IncompleteArtifact[] { - return this.artifactsLookup.unknownSetOf(artifacts); - } -} diff --git a/indexer/src/recorder/commit-result.ts b/indexer/src/recorder/commit-result.ts deleted file mode 100644 index 26cee0aa7..000000000 --- a/indexer/src/recorder/commit-result.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { AsyncResults } from "../utils/async-results.js"; -import { - ICommitResult, - RecordHandle, - RecordResponse, - RecorderError, -} from "./types.js"; -import _ from "lodash"; - -export class CommitResult implements ICommitResult { - committed: string[]; - skipped: string[]; - invalid: string[]; - errors: unknown[]; - - constructor() { - this.committed = []; - this.skipped = []; - this.invalid = []; - this.errors = []; - } - - collectResultsForHandles( - handles: RecordHandle[], - ): AsyncResults { - const handleIds = handles.map((h) => h.id); - - const committed = _.intersection(this.committed, handleIds); - const skipped = _.intersection(this.skipped, handleIds); - const invalid = _.intersection(this.invalid, handleIds); - const missing = _.difference( - handleIds, - _.union(committed, skipped, invalid), - ); - - const results: AsyncResults = { - success: _.union(committed, skipped), - errors: [], - }; - - invalid.forEach((i) => { - results.errors.push(new RecorderError(`invalid result for ${i}`)); - }); - missing.forEach((m) => { - results.errors.push(new RecorderError(`missing result for ${m}`)); - }); - return results; - } -} diff --git a/indexer/src/recorder/group.ts b/indexer/src/recorder/group.ts deleted file mode 100644 index 1f30ca6c7..000000000 --- a/indexer/src/recorder/group.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { EventEmitter } from "node:events"; -import { - IEventRecorderClient, - IEventGroupRecorder, - IncompleteEvent, - IncompleteArtifact, - RecordHandle, -} from "./types.js"; - -export type EventGrouperFn = (event: IncompleteEvent) => T; -export type GroupObjToStrFn = (group: T) => string; - -/** - * Allows for organizing a large set of pending events for specific groups - * (usually artifacts) - */ -export class EventGroupRecorder implements IEventGroupRecorder { - private groupRecordHandles: Record; - private grouperFn: EventGrouperFn; - private groupToStrFn: GroupObjToStrFn; - private emitter: EventEmitter; - private recorder: IEventRecorderClient; - - constructor( - recorder: IEventRecorderClient, - grouperFn: EventGrouperFn, - groupObjToStrFn: GroupObjToStrFn, - ) { - this.groupRecordHandles = {}; - this.grouperFn = grouperFn; - this.groupToStrFn = groupObjToStrFn; - this.emitter = new EventEmitter(); - this.recorder = recorder; - } - - async record(event: IncompleteEvent): Promise { - const group = this.grouperFn(event); - const promises = this.getGroupRecordHandles(group); - promises.push(await this.recorder.record(event)); - } - - private getGroupRecordHandles(group: T): RecordHandle[] { - const id = this.groupToStrFn(group); - if (!this.groupRecordHandles[id]) { - this.groupRecordHandles[id] = []; - } - return this.groupRecordHandles[id]; - } - - handlesForGroup(group: T): RecordHandle[] { - const id = this.groupToStrFn(group); - return this.groupRecordHandles[id] || []; - } - - addListener( - message: "error" | "group-completed", - cb: (...args: any) => void, - ): EventEmitter { - return this.emitter.addListener(message, cb); - } - - removeListener( - message: "error" | "group-completed", - cb: (...args: any) => void, - ) { - return this.emitter.removeListener(message, cb); - } -} - -export class ArtifactGroupRecorder extends EventGroupRecorder { - constructor(recorder: IEventRecorderClient) { - const artifactToString = (a: IncompleteArtifact) => { - return `${a.name}:${a.namespace}:${a.type}`; - }; - super(recorder, (event) => event.to, artifactToString); - } -} diff --git a/indexer/src/recorder/index.ts b/indexer/src/recorder/index.ts deleted file mode 100644 index a0c4ebf25..000000000 --- a/indexer/src/recorder/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./types.js"; diff --git a/indexer/src/recorder/recorder.test.ts b/indexer/src/recorder/recorder.test.ts deleted file mode 100644 index 515557852..000000000 --- a/indexer/src/recorder/recorder.test.ts +++ /dev/null @@ -1,339 +0,0 @@ -import { DateTime } from "luxon"; -import { EventRepository } from "../db/events.js"; -import { - ArtifactId, - ArtifactNamespace, - ArtifactType, - EventType, - Recording, -} from "../db/orm-entities.js"; -import { withDbDescribe } from "../db/testing.js"; -import { BatchEventRecorder, IFlusher } from "./recorder.js"; -import { IncompleteEvent, RecordHandle } from "./types.js"; -import { - AppDataSource, - createAndConnectDataSource, -} from "../db/data-source.js"; -import { randomInt, randomUUID } from "node:crypto"; -import _ from "lodash"; -import { createClient } from "redis"; -import { ArtifactRepository } from "../db/artifacts.js"; - -type Callback = () => void; - -class TestFlusher implements IFlusher { - flushCallback: Callback | undefined; - lastNotify: number; - - clear(): void {} - - flush(): void { - if (this.flushCallback) { - this.flushCallback(); - } - } - - onFlush(cb: () => void): void { - this.flushCallback = cb; - } - - notify(size: number): void { - this.lastNotify = size; - } -} - -type RandomCommitEventOptions = { - fromProbability: number; - repoNameGenerator: () => string; - usernameGenerator: () => string; -}; - -function randomCommitEventsGenerator( - count: number, - options?: Partial, -): IncompleteEvent[] { - const opts: RandomCommitEventOptions = _.merge( - { - fromProbability: 1, - repoNameGenerator: () => `repo-${randomUUID()}`, - usernameGenerator: () => `user-${randomUUID()}`, - }, - options, - ); - - const events: IncompleteEvent[] = []; - - for (let i = 0; i < count; i++) { - const randomToRepoName = opts.repoNameGenerator(); - const randomFromUsername = opts.usernameGenerator(); - const randomTime = DateTime.now() - .minus({ days: 10 }) - .plus({ minutes: randomInt(14400) }); - const randomSourceId = randomUUID(); - const event: IncompleteEvent = { - time: randomTime, - type: { - version: 1, - name: "COMMIT_CODE", - }, - to: { - type: ArtifactType.GIT_REPOSITORY, - name: randomToRepoName, - namespace: ArtifactNamespace.GITHUB, - }, - sourceId: randomSourceId, - amount: 1, - details: {}, - }; - // probabilistically add a from field - if (Math.random() > 1.0 - opts.fromProbability) { - event.from = { - type: ArtifactType.GITHUB_USER, - name: randomFromUsername, - namespace: ArtifactNamespace.GITHUB, - }; - } - events.push(event); - } - return events; -} - -withDbDescribe("BatchEventRecorder", () => { - let flusher: TestFlusher; - let redisClient: ReturnType; - beforeEach(async () => { - flusher = new TestFlusher(); - redisClient = createClient({ - url: "redis://redis:6379", - }); - await redisClient.connect(); - }); - - afterEach(async () => { - await redisClient.flushAll(); - await redisClient.disconnect(); - }); - - it("should setup the recorder", async () => { - const recorder = new BatchEventRecorder( - AppDataSource, - [], - AppDataSource.getRepository(Recording), - AppDataSource.getRepository(EventType), - () => Promise.resolve(redisClient), - { - maxBatchSize: 3, - timeoutMs: 30000, - flusher: flusher, - }, - ); - await recorder.loadEventTypes(); - recorder.setRange({ - startDate: DateTime.now().minus({ month: 1 }), - endDate: DateTime.now().plus({ month: 1 }), - }); - recorder.setActorScope( - [ArtifactNamespace.GITHUB], - [ArtifactType.GITHUB_USER, ArtifactType.GIT_REPOSITORY], - ); - const testEvent: IncompleteEvent = { - amount: Math.random(), - time: DateTime.now(), - type: { - name: "COMMIT_CODE", - version: 1, - }, - to: { - name: "test", - namespace: ArtifactNamespace.GITHUB, - type: ArtifactType.GIT_REPOSITORY, - }, - from: { - name: "contributor", - namespace: ArtifactNamespace.GITHUB, - type: ArtifactType.GITHUB_USER, - }, - sourceId: "test123", - }; - - await recorder.begin(); - - await recorder.record(testEvent); - //flusher.flush(); - await recorder.commit(); - - // Check that the values are correct - const results = await EventRepository.find(); - expect(results.length).toEqual(1); - expect(results[0].sourceId).toEqual(testEvent.sourceId); - expect(results[0].amount).toEqual(testEvent.amount); - expect(results[0].fromId).toBeDefined(); - expect(results[0].toId).toBeDefined(); - - const toArtifact = await ArtifactRepository.findOneByOrFail({ - id: results[0].toId as ArtifactId, - }); - const fromArtifact = await ArtifactRepository.findOneByOrFail({ - id: results[0].fromId as ArtifactId, - }); - - expect(toArtifact.name).toEqual(testEvent.to.name); - expect(toArtifact.namespace).toEqual(testEvent.to.namespace); - expect(toArtifact.type).toEqual(testEvent.to.type); - - expect(fromArtifact.name).toEqual(testEvent.from?.name); - expect(fromArtifact.namespace).toEqual(testEvent.from?.namespace); - expect(fromArtifact.type).toEqual(testEvent.from?.type); - }); - - describe("various recorder scenarios", () => { - let recorder: BatchEventRecorder; - let testEvent: IncompleteEvent; - let errors: unknown[]; - beforeEach(async () => { - errors = []; - recorder = new BatchEventRecorder( - AppDataSource, - [ - await createAndConnectDataSource("test1"), - await createAndConnectDataSource("test2"), - await createAndConnectDataSource("test3"), - await createAndConnectDataSource("test4"), - await createAndConnectDataSource("test5"), - ], - AppDataSource.getRepository(Recording), - AppDataSource.getRepository(EventType), - () => Promise.resolve(redisClient), - { - maxBatchSize: 100000, - flushIntervalMs: 1000, - timeoutMs: 300000, - }, - ); - await recorder.loadEventTypes(); - recorder.setRange({ - startDate: DateTime.now().minus({ month: 1 }), - endDate: DateTime.now().plus({ month: 1 }), - }); - recorder.setActorScope( - [ArtifactNamespace.GITHUB], - [ArtifactType.GITHUB_USER, ArtifactType.GIT_REPOSITORY], - ); - recorder.addListener("error", (err) => { - errors.push(err); - }); - testEvent = { - amount: Math.random(), - time: DateTime.now(), - type: { - name: "COMMIT_CODE", - version: 1, - }, - to: { - name: "test", - namespace: ArtifactNamespace.GITHUB, - type: ArtifactType.GIT_REPOSITORY, - }, - from: { - name: "contributor", - namespace: ArtifactNamespace.GITHUB, - type: ArtifactType.GITHUB_USER, - }, - sourceId: "test123", - }; - await recorder.begin(); - await recorder.record(testEvent); - }); - - afterEach(async () => { - await recorder.close(); - }); - - it("should do a large set of writes", async () => { - const eventCountToWrite = 1000000; - const events = randomCommitEventsGenerator(eventCountToWrite, { - fromProbability: 0.7, - repoNameGenerator: () => `repo-${randomInt(100)}`, - usernameGenerator: () => `user-${randomInt(10000)}`, - }); - const handles: RecordHandle[] = []; - for (const event of events) { - handles.push(await recorder.record(event)); - } - const completion = recorder.wait(handles); - await recorder.commit(); - await completion; - - // Check that the events are in the database - const eventCount = await EventRepository.count(); - expect(eventCount).toEqual(eventCountToWrite + 1); - }, 300000); - - // In the current iteration of the recorder, dupes aren't errors, they're - // merged and one of the events is chosen. We treat this as a failure of the - // collector to clean up and do best effort to simply keep recordings - // flowing. This is useful for somethings that might _expect_ duplicates. - it("should try to write duplicates", async () => { - const eventCountToWrite = 10; - const events = randomCommitEventsGenerator(eventCountToWrite, { - fromProbability: 0.7, - repoNameGenerator: () => `repo-${randomInt(100)}`, - usernameGenerator: () => `user-${randomInt(10000)}`, - }); - const handles: RecordHandle[] = []; - for (const event of events) { - handles.push(await recorder.record(event)); - } - // Try to dupe twice - for (const event of events.slice(0, eventCountToWrite / 2)) { - handles.push(await recorder.record(event)); - } - for (const event of events.slice(0, eventCountToWrite / 2)) { - handles.push(await recorder.record(event)); - } - const completion = recorder.wait(handles); - const results = await recorder.commit(); - await completion; - - // Check that the events are in the database - const eventCount = await EventRepository.count(); - expect(eventCount).toEqual(eventCountToWrite + 1); - expect(results.committed.length).toEqual(eventCountToWrite + 1); - - expect(results.invalid.length).toEqual(0); - expect(results.errors.length).toEqual(0); - expect(results.skipped.length).toEqual(0); - }, 60000); - - // In the current iteration of the recorder, dupes aren't errors. skipping - // for now. - it.skip("should record errors when we write some duplicates over multiple batches", async () => { - const events = randomCommitEventsGenerator(10); - - const errs: unknown[] = []; - - recorder.addListener("error", (err) => { - errs.push(err); - }); - - let handles: RecordHandle[] = []; - for (const event of events) { - handles.push(await recorder.record(event)); - } - const results0 = await recorder.wait(handles); - expect(results0.errors.length).toEqual(0); - - // Add an event for this test to pass - events.push(...randomCommitEventsGenerator(1)); - - // Try to write them again - handles = []; - for (const event of events) { - handles.push(await recorder.record(event)); - } - - await recorder.wait(handles); - expect(errs.length).toEqual(10); - }); - }); -}); diff --git a/indexer/src/recorder/recorder.ts b/indexer/src/recorder/recorder.ts deleted file mode 100644 index 0d77f2eb0..000000000 --- a/indexer/src/recorder/recorder.ts +++ /dev/null @@ -1,1779 +0,0 @@ -import { - Artifact, - ArtifactNamespace, - ArtifactType, - EventType, - EventWeakRef, - Recording, -} from "../db/orm-entities.js"; -import { - IEventRecorder, - IncompleteEvent, - IncompleteArtifact, - RecorderError, - EventRecorderOptions, - RecordHandle, - IRecorderEvent, - IRecorderEventType, - ICommitResult as IRecorderCommitResult, - ICommitResult, -} from "./types.js"; -import { Repository } from "typeorm"; -import { UniqueArray, asyncBatchFlattened } from "../utils/array.js"; -import { logger } from "../utils/logger.js"; -import _ from "lodash"; -import { Range, isWithinRange } from "../utils/ranges.js"; -import { EventEmitter } from "node:events"; -import { randomUUID } from "node:crypto"; -import { DateTime } from "luxon"; -import { RecordResponse } from "./types.js"; -import { AsyncResults } from "../utils/async-results.js"; -import { ensure, ensureNumber, ensureString } from "../utils/common.js"; -import { setTimeout as asyncTimeout } from "node:timers/promises"; -import { DataSource } from "typeorm/browser"; -import { timer } from "../utils/debug.js"; -import { createClient } from "redis"; -import { LRUCache } from "lru-cache"; -import { CommitResult } from "./commit-result.js"; - -type RedisClient = ReturnType; - -export interface BatchEventRecorderOptions { - maxBatchSize: number; - timeoutMs: number; - flushIntervalMs: number; - tempTableExpirationDays: number; - flusher?: IFlusher; - redisArtifactMetaKey: string; -} - -const defaultBatchEventRecorderOptions: BatchEventRecorderOptions = { - maxBatchSize: 100000, - - // 30 minute timeout as our db seems to be having trouble keeping up - timeoutMs: 1800000, - - flushIntervalMs: 2000, - - tempTableExpirationDays: 1, - - redisArtifactMetaKey: "artifact::_meta", -}; - -export type FlusherCallback = () => Promise; - -export interface IFlusher { - notify(size: number): void; - clear(): void; - onFlush(cb: FlusherCallback): void; -} - -/** - * A flusher that accumulates some arbitrary size (assumed to be the batch size) - * and chooses to flush once that size is reached or a timeout has been met. - */ -export class TimeoutBatchedFlusher implements IFlusher { - private timeout: NodeJS.Timeout | null; - private timeoutMs: number; - private waitMs: number; - private size: number; - private cb: FlusherCallback | undefined; - private maxSize: number; - private triggered: boolean; - private nextFlush: DateTime; - private stopped: boolean; - - constructor(timeoutMs: number, maxSize: number) { - if (timeoutMs < 100) { - throw new Error("timeout is too short"); - } - this.timeoutMs = timeoutMs; - this.waitMs = timeoutMs; - this.timeout = null; - this.size = 0; - this.maxSize = maxSize; - this.triggered = false; - this.stopped = false; - this.setNextFlush(); - } - - private setNextFlush() { - this.nextFlush = DateTime.now().plus({ milliseconds: this.timeoutMs }); - } - - notify(size: number): void { - if (this.triggered) { - return; - } - this.size += size; - - // if the size is 0 then this was called without anything in the queue. That - // means we should slow down processing the queue. - if (this.size == 0) { - if (this.waitMs == 0) { - this.waitMs = this.timeoutMs / 2; - } - this.waitMs += this.waitMs; - } else { - this.waitMs = this.timeoutMs; - } - - // We should allow the flush notification to get pushed while the size of - // the flush is small. - if (this.size < this.maxSize) { - this.resetTimeout(); - } - } - - private resetTimeout() { - if (this.stopped) { - if (this.timeout) { - clearTimeout(this.timeout); - } - } - // Only reset the timeout if the flusher is currently untriggered. A - // triggered state means that this flusher is executing the callback - if (!this.triggered) { - if (this.timeout) { - clearTimeout(this.timeout); - } - - this.timeout = setTimeout(() => { - // It is expected that the consuming class will clear the flusher. - this.triggered = true; - - if (this.cb) { - this.cb() - .then(() => { - this.triggered = false; - // Reset size which is just a count of notifications This allows the - // timeout to backoff if the notify function is continously called and - // this.size remains 0 - this.size = 0; - this.setNextFlush(); - this.notify(0); - }) - .catch((err) => { - // This should not happen and will trigger a failure. It is - // expected that the callback will capture all errors. - logger.error("recorder is not catching some errors"); - logger.error(err); - - // This will cause an uncaughtException error to be thrown and the - // entire application to stop. - throw err; - }); - } - }, this.waitMs); - } - } - - onFlush(cb: FlusherCallback): void { - if (this.timeout) { - clearTimeout(this.timeout); - } - this.cb = cb; - } - - clear(): void { - this.size = 0; - this.waitMs = 0; - if (this.timeout) { - clearTimeout(this.timeout); - } - this.timeout = null; - this.triggered = false; - } -} - -function eventUniqueId( - event: Pick, -): string { - return `${event.type}:${event.sourceId}:${event.to.name}:${event.to.namespace}`; -} - -function dbEventUniqueId(event: EventWeakRef): string { - return `${event.typeId}:::${event.toId}:::${event.sourceId}`; -} - -function splitDbEventUniqueId(id: string): EventWeakRef { - const split = _.split(id, ":::"); - return { - sourceId: split[2], - typeId: parseInt(split[0]), - toId: parseInt(split[1]), - }; -} - -export class RecorderEventType implements IRecorderEventType { - private _name: string; - private _version: number; - - constructor(name: string, version: number) { - this._name = name; - this._version = version; - } - - static fromDBEventType(eventType: EventType) { - return new RecorderEventType(eventType.name, eventType.version); - } - - get name(): string { - return this._name; - } - get version(): number { - return this._version; - } - - toString() { - return `${this.name}:v${this.version}`; - } -} - -type ArtifactMatch = [IncompleteArtifact, number | null]; -type ArtifactMatches = ArtifactMatch[]; -interface IArtifactMap { - resolveToIds( - artifacts: (IncompleteArtifact | null)[], - ): Promise<(number | null)[]>; - resolveToIncompleteArtifacts( - ids: (number | null)[], - ): Promise<(IncompleteArtifact | null)[]>; - - getId(artifact: IncompleteArtifact): Promise; - getIncomplete(id: number): Promise; - - add(pairs: { id: number; artifact: IncompleteArtifact }[]): void; -} - -export class RecorderEvent implements IRecorderEvent { - private _id: number | null; - private _time: DateTime; - private _type: { name: string; version: number }; - private _sourceId: string; - private _to: IncompleteArtifact; - private _from: IncompleteArtifact | null | undefined; - private _amount: number; - private _details: object; - - private constructor( - id: number | null, - time: DateTime, - type: IRecorderEventType, - sourceId: string, - to: IncompleteArtifact, - from: IncompleteArtifact | null | undefined, - amount: number, - details?: object, - ) { - this._id = id; - this._time = time; - this._type = type; - this._sourceId = sourceId; - this._to = to; - this._from = from; - this._amount = amount; - this._details = details || {}; - } - - static fromIncompleteEvent(event: IncompleteEvent) { - const e = new RecorderEvent( - null, - event.time, - event.type, - event.sourceId, - event.to, - event.from, - event.amount, - event.details, - ); - try { - e.ensureIsValid(); - } catch (err) { - logger.debug(`errors converting IncompleteEvent to a RecorderEvent`); - logger.error(err); - throw err; - } - return e; - } - - get id(): number | null { - return this._id; - } - - get time(): DateTime { - return this._time; - } - - get type(): IRecorderEventType { - const type = this._type; - return { - get name(): string { - return type.name; - }, - get version(): number { - return type.version; - }, - toString() { - return `${type.name}:v${type.version}`; - }, - }; - } - - get sourceId(): string { - return this._sourceId; - } - - get to(): IncompleteArtifact { - return { - name: this._to.name, - namespace: this._to.namespace, - type: this._to.type, - }; - } - - get from(): IncompleteArtifact | null { - if (!this._from) { - return null; - } - return { - name: this._from.name, - namespace: this._from.namespace, - type: this._from.type, - }; - } - - get amount(): number { - return this._amount; - } - - get details(): object { - return this._details; - } - - ensureIsValid() { - ensureNumber(this._amount); - ensureString(this._sourceId); - ensure(this._to, "artifact for `to` must be defined"); - if (!this._time.isValid) { - throw new Error("record date is invalid"); - } - if (this._sourceId === "") { - throw new Error("sourceId cannot be empty"); - } - } -} - -type OrNull = T | null; - -interface PGRecorderTempEventBatchInput { - sourceIds: string[]; - typeIds: number[]; - times: Date[]; - toIds: number[]; - fromIds: OrNull[]; - amounts: number[]; - details: Record[]; - uniqueIds: string[]; -} - -export type RecorderEventRef = Pick; - -type ArtifactMetaRedis = { - lastUpdatedAt: string; -}; - -export interface ArtifactResolverOptions { - metaKey: string; - prefix: string; - separator: string; - maxBatchSize: number; - localLruSize: number; -} - -const defaultArtifactResolverOptions: ArtifactResolverOptions = { - metaKey: "_meta", - prefix: "artifact", - separator: ":::", - maxBatchSize: 100000, - localLruSize: 300000, -}; - -type ArtifactLRUStorage = [string, ArtifactNamespace, ArtifactType]; - -class LRUArtifactMap implements IArtifactMap { - private toIds: LRUCache; - private toIncomplete: LRUCache; - private resolver: ArtifactResolver; - - constructor(resolver: ArtifactResolver, size: number) { - this.toIds = new LRUCache({ - max: size, - }); - this.toIncomplete = new LRUCache({ - max: size, - }); - this.resolver = resolver; - } - - async resolveToIds( - artifacts: (IncompleteArtifact | null)[], - ): Promise<(number | null)[]> { - const unknownArtifacts = artifacts.filter((a) => { - if (!a) { - return false; - } - const r = this.toIds.get(this.incompleteKey(a)); - return !r; - }) as IncompleteArtifact[]; - if (unknownArtifacts.length > 0) { - await this.resolver.idsFromIncompleteArtifacts(unknownArtifacts); - } - return artifacts.map((a) => { - if (!a) { - return null; - } - const id = this.toIds.get(this.incompleteKey(a)); - if (!id) { - return null; - } - return id; - }); - } - - async resolveToIncompleteArtifacts( - ids: (number | null)[], - ): Promise<(IncompleteArtifact | null)[]> { - const unknownIds = ids.filter((id) => { - if (!id) { - return false; - } - const r = this.toIncomplete.get(id); - return !r; - }) as number[]; - if (unknownIds.length > 0) { - await this.resolver.incompleteArtifactsByIds(unknownIds); - } - return unknownIds.map((id) => { - if (!id) { - return null; - } - const r = this.toIncomplete.get(id); - if (!r) { - return null; - } - return this.storageToIncompleteArtifact(r); - }); - } - - async getId(artifact: IncompleteArtifact): Promise { - let id: number | null | undefined = this.toIds.get( - this.incompleteKey(artifact), - ); - if (!id) { - id = await this.resolver.idFromIncompleteArtifact(artifact); - if (!id) { - throw new Error( - `artifact is not currently known by incomplete reference "${this.incompleteKey( - artifact, - )}"`, - ); - } - } - return id; - } - - async getIncomplete(id: number): Promise { - const incomplete = this.toIncomplete.get(id); - if (!incomplete) { - const resolverIncomplete = await this.resolver.incompleteArtifactById(id); - if (!resolverIncomplete) { - throw new Error(`artifact is not currently known by id ${id}`); - } - return resolverIncomplete; - } - return this.storageToIncompleteArtifact(incomplete); - } - - add(pairs: { id: number; artifact: IncompleteArtifact }[]): void { - pairs.forEach(({ id, artifact }) => { - this.toIds.set(this.incompleteKey(artifact), id); - this.toIncomplete.set(id, this.incompleteArtifactToStorage(artifact)); - }); - } - - private incompleteArtifactToStorage( - a: IncompleteArtifact, - ): ArtifactLRUStorage { - return [a.name, a.namespace, a.type]; - } - - private storageToIncompleteArtifact( - s: ArtifactLRUStorage, - ): IncompleteArtifact { - return { - name: s[0], - namespace: s[1], - type: s[2], - }; - } - - private incompleteKey(artifact: IncompleteArtifact) { - return `${artifact.name}:${artifact.namespace}:${artifact.type}`; - } -} - -/** - * Mostly an internal class used to resolve artifacts for recording events. This is - * intended to speed up writes. - */ -export class ArtifactResolver { - private redis: RedisClient; - private dataSource: DataSource; - private options: ArtifactResolverOptions; - private localMap: LRUArtifactMap; - - constructor( - dataSource: DataSource, - redis: RedisClient, - options?: Partial, - ) { - this.dataSource = dataSource; - this.redis = redis; - this.options = _.merge(defaultArtifactResolverOptions, options); - this.localMap = new LRUArtifactMap(this, this.options.localLruSize); - } - - async *fullLoadArtifactPages() { - // during a full load we need to load based on id and calculate the last updated at - let cursor: number | undefined = 1; - let lastUpdatedAt = DateTime.fromISO("1970-09-21T00:00:00Z"); - const repo = this.dataSource.getRepository(Artifact); - - logger.debug("load all the artifacts into redis"); - logger.debug("no artifact cache found. running a full load of artifacts"); - do { - const query = repo - .createQueryBuilder("artifact") - .where('artifact."id" > :id', { id: cursor }); - - const page = await query - .orderBy("artifact.id", "ASC") - .limit(this.options.maxBatchSize) - .getMany(); - - for (const artifact of page) { - const currentUpdatedAt = DateTime.fromJSDate(artifact.updatedAt); - if (currentUpdatedAt > lastUpdatedAt) { - lastUpdatedAt = currentUpdatedAt; - } - yield artifact; - } - - if (page.length < this.options.maxBatchSize) { - cursor = undefined; - } else { - // Ensure that we have everything by including this item - cursor = page.slice(-1)[0].id; - logger.debug(`next artifact page starting from ${cursor}`); - } - } while (cursor !== undefined); - } - - async *latestArtifacts(lastUpdatedAt: DateTime) { - logger.debug("load latest artifacts by updatedAt"); - const formatString = "yyyy-LL-dd HH:mm:ss"; - - //let lastUpdatedAt = DateTime.fromISO("1970-09-21T00:00:00Z"); - const repo = this.dataSource.getRepository(Artifact); - let offset = 0; - - logger.debug( - `loading artifacts since ${lastUpdatedAt.toFormat( - formatString, - )} (${lastUpdatedAt.toISO()})`, - ); - let cursor: DateTime | undefined = lastUpdatedAt; - while (cursor !== undefined) { - const query = repo - .createQueryBuilder("artifact") - .where('artifact."updatedAt" > :updatedAt', { - updatedAt: cursor.toFormat(formatString), - }); - - const page = await query - .orderBy("artifact.updatedAt", "ASC") - .limit(this.options.maxBatchSize) - .offset(offset) - .getMany(); - - offset = this.options.maxBatchSize; - for (const artifact of page) { - const currentUpdatedAt = DateTime.fromJSDate(artifact.updatedAt); - if (currentUpdatedAt.toMillis() != lastUpdatedAt.toMillis()) { - lastUpdatedAt = currentUpdatedAt; - offset = 0; - } - yield artifact; - } - - if (page.length < this.options.maxBatchSize) { - cursor = undefined; - } else { - // Ensure that we have everything by including this item - cursor = DateTime.fromJSDate(page.slice(-1)[0].updatedAt).minus({ - second: 1, - }); - logger.debug(`next artifact page starting from ${cursor.toISO()}`); - } - } - } - - async loadArtifacts() { - const formatString = "yyyy-LL-dd HH:mm:ss"; - const artifactMetaJson = await this.redis.get(this.metaKey); - const artifactMeta: ArtifactMetaRedis = - artifactMetaJson === null - ? { - lastUpdatedAt: DateTime.fromISO("1970-09-21T00:00:00Z") - .toUTC() - .toISO(), - } - : JSON.parse(artifactMetaJson); - - let lastUpdatedAt = DateTime.fromISO(artifactMeta.lastUpdatedAt); - const artifactsGenerator = !artifactMetaJson - ? this.fullLoadArtifactPages() - : this.latestArtifacts(lastUpdatedAt); - - const loadArtifactsTimer = timer( - `load artifacts since ${lastUpdatedAt.toFormat( - formatString, - )} (${lastUpdatedAt.toISO()})`, - ); - let count = 0; - for await (const artifact of artifactsGenerator) { - count += 1; - const currentUpdatedAt = DateTime.fromJSDate(artifact.updatedAt); - if (currentUpdatedAt > lastUpdatedAt) { - lastUpdatedAt = currentUpdatedAt; - } - await this.saveArtifact(artifact); - } - loadArtifactsTimer(); - - // If the lastUpdatedAt time is earlier than 1 min ago. Set the - // lastUpdatedAt to 1 min ago. This will eventually speed up look ups - const oneMinAgo = DateTime.now().toUTC().minus({ minute: 1 }); - if (lastUpdatedAt < oneMinAgo) { - lastUpdatedAt = oneMinAgo; - } - - // Set the metadata so we only ever get diffs - artifactMeta.lastUpdatedAt = lastUpdatedAt.startOf("second").toISO()!; - - await this.redis.set(this.metaKey, JSON.stringify(artifactMeta)); - logger.debug( - `loaded ${count} artifacts into redis after ${artifactMeta.lastUpdatedAt}`, - ); - } - - async saveArtifact(artifact: Artifact) { - const byIncompleteArtifact = this.redis.set( - this.getKeyByIncompleteArtifact(artifact), - artifact.id, - ); - const byId = this.redis.set( - this.getKeyById(artifact.id), - JSON.stringify([artifact.name, artifact.namespace, artifact.type]), - ); - this.addArtifactToLocalMap(artifact); - return Promise.all([byId, byIncompleteArtifact]); - } - - addArtifactToLocalMap(artifact: Artifact) { - this.localMap.add([ - { - id: artifact.id, - artifact: { - name: artifact.name, - namespace: artifact.namespace, - type: artifact.type, - }, - }, - ]); - } - - addIncompleteArtifactToLocalMap(id: number, artifact: IncompleteArtifact) { - this.localMap.add([ - { - id: id, - artifact: { - name: artifact.name, - namespace: artifact.namespace, - type: artifact.type, - }, - }, - ]); - } - - async idFromIncompleteArtifact( - artifact: IncompleteArtifact, - ): Promise { - const res = await this.redis.get(this.getKeyByIncompleteArtifact(artifact)); - if (!res) { - return null; - } - const id = parseInt(res); - this.addIncompleteArtifactToLocalMap(id, artifact); - return id; - } - - async idsFromIncompleteArtifacts(artifacts: IncompleteArtifact[]) { - const res = await this.redis.mGet( - artifacts.map((a) => this.getKeyByIncompleteArtifact(a)), - ); - return res.map((r, i) => { - if (!r) { - return null; - } - const id = parseInt(r); - this.addIncompleteArtifactToLocalMap(id, artifacts[i]); - return id; - }); - } - - private parseIncompleteArtifact( - result: string | null, - ): IncompleteArtifact | null { - if (!result) { - return null; - } - const parsed = JSON.parse(result) as [ - string, - ArtifactNamespace, - ArtifactType, - ]; - return { - name: parsed[0], - namespace: parsed[1], - type: parsed[2], - }; - } - - async incompleteArtifactById(id: number): Promise { - const result = await this.redis.get(this.getKeyById(id)); - const artifact = this.parseIncompleteArtifact(result); - if (artifact) { - this.addIncompleteArtifactToLocalMap(id, artifact); - } - return artifact; - } - - async incompleteArtifactsByIds( - ids: number[], - ): Promise> { - const results = await this.redis.mGet(ids.map((i) => this.getKeyById(i))); - return results.map((r, i) => { - const artifact = this.parseIncompleteArtifact(r); - if (artifact) { - this.addIncompleteArtifactToLocalMap(ids[i], artifact); - } - return artifact; - }); - } - - private getKey(...parts: string[]) { - const allParts = [this.options.prefix]; - allParts.push(...parts); - return allParts.join("..."); - } - - private get metaKey() { - return this.getKey(this.options.metaKey); - } - - getKeyByIncompleteArtifact(artifact: IncompleteArtifact) { - return this.getKey("incomplete", artifact.name, artifact.namespace); - } - - getKeyById(id: number) { - return this.getKey("id", `${id}`); - } - - get map(): IArtifactMap { - return this.localMap; - } -} - -type BatchResult = EventWeakRef; - -export type RedisClientFactory = () => Promise; - -export class BatchEventRecorder implements IEventRecorder { - private eventQueue: Array; - private options: BatchEventRecorderOptions; - private dataSource: DataSource; - private dataSourcePool: DataSource[]; - private eventTypeRepository: Repository; - private recordingRepository: Repository; - private namespaces: ArtifactNamespace[]; - private types: ArtifactType[]; - private flusher: IFlusher; - private range: Range | undefined; - private emitter: EventEmitter; - private closing: boolean; - private recorderOptions: EventRecorderOptions; - private queueSize: number; - private recordedHistory: Record; - private eventTypeIdMap: Record; - private recorderEventTypeStringIdMap: Record; - private eventTypeNameAndVersionMap: Record>; - private recorderId: string; - private batchCounter: number; - private tableCounter: number; - private initialized: boolean; - private preCommitCount: number; - private _preCommitTableName: string; - private isPreCommitTableOpen: boolean; - private committing: boolean; - private redis: RedisClient; - private redisFactory: RedisClientFactory; - private artifactResolver: ArtifactResolver; - private commitResult: ICommitResult | null; - - constructor( - dataSource: DataSource, - dataSourcePool: DataSource[], - recordingRepository: Repository, - eventTypeRepository: Repository, - redisFactory: RedisClientFactory, - options?: Partial, - ) { - this.dataSourcePool = dataSourcePool; - this.dataSource = dataSource; - this.recordingRepository = recordingRepository; - this.eventTypeRepository = eventTypeRepository; - this.options = _.merge(defaultBatchEventRecorderOptions, options); - this.flusher = - options?.flusher || - new TimeoutBatchedFlusher( - this.options.flushIntervalMs, - this.options.maxBatchSize, - ); - this.queueSize = 0; - this.emitter = new EventEmitter(); - this.closing = false; - this.recorderOptions = { - overwriteExistingEvents: false, - }; - this.recordedHistory = {}; - this.eventTypeIdMap = {}; - this.eventTypeNameAndVersionMap = {}; - this.recorderEventTypeStringIdMap = {}; - this.recorderId = randomUUID(); - this.eventQueue = []; - this.batchCounter = 0; - this.tableCounter = 0; - this.initialized = false; - this.isPreCommitTableOpen = false; - this._preCommitTableName = ""; - this.committing = false; - this.redisFactory = redisFactory; - this.preCommitCount = 0; - this.commitResult = null; - - this.emitter.setMaxListeners(this.options.maxBatchSize * 3); - // Setup flush event handler - this.flusher.onFlush(async () => { - logger.debug(`flushing all queued events`); - try { - await this.flushAll(); - logger.debug("flush cycle completed"); - this.emitter.emit("flush"); - } catch (err) { - logger.error(`error caught flushing ${err}`); - logger.error(err); - this.emitter.emit("error", err); - } - }); - } - - async begin(): Promise { - if (!this.initialized) { - logger.debug("saving recording details for later cleanup"); - await this.recordingRepository.insert({ - recorderId: this.recorderId, - expiration: DateTime.now().plus({ day: 1 }).toJSDate(), - }); - this.initialized = true; - } - - this.redis = await this.redisFactory(); - - this.artifactResolver = new ArtifactResolver(this.dataSource, this.redis, { - maxBatchSize: this.options.maxBatchSize, - }); - - await this.loadArtifacts(); - - if (this.isPreCommitTableOpen) { - throw new RecorderError("Only one temporary table may be set at a time"); - } - - // Create a temporary table to collect precommit events - const formattedRecorderId = this.recorderId.replace(/-/g, "_"); - this._preCommitTableName = `recorder_pre_commit_${formattedRecorderId}_${this.tableCounter}`; - this.tableCounter += 1; - this.isPreCommitTableOpen = true; - this.preCommitCount = 0; - this.commitResult = new CommitResult(); - - await this.dataSource.query(` - CREATE TABLE ${this.preCommitTableName} AS - TABLE "event" - WITH NO DATA - `); - logger.debug(`created temporary table ${this.preCommitTableName}`); - } - - private get preCommitTableName(): string { - if (this._preCommitTableName === "") { - throw new RecorderError("need to call begin before recording"); - } - return this._preCommitTableName; - } - - async rollback(): Promise { - if (this.isPreCommitTableOpen) { - await this.clean(); - } - } - - private async clean() { - logger.debug("dropping the temporary table"); - const tmDropTable = timer("drop table"); - this.commitResult = null; - - await this.dataSource.query(` - DROP TABLE ${this.preCommitTableName} - `); - try { - await this.redis.del(this.preCommitTableName); - } catch (err) { - logger.error("error attempting to delete the redis set"); - logger.error(err); - } - tmDropTable(); - - this._preCommitTableName = ""; - this.isPreCommitTableOpen = false; - this.committing = false; - } - - async commit(): Promise { - logger.debug(`committing changes to ${this.preCommitTableName}`); - this.committing = true; - // Flush whatever is in the - this.flusher.clear(); - - try { - await this.flushAll(); - } catch (err) { - // No need to stop committing. We will commit what can possibly be - // committed - logger.debug( - "error on flush before commit. errors will appear with events", - ); - } - - logger.debug("flush complete. beginning recorder commit"); - - // Using the recorder_temp_event table we can commit all of the events - // Remove anything with duplicates - const tmDelDupes = timer("delete event dupes"); - const commitResult = this.commitResult!; - const toIdsToResolve: number[] = []; - try { - const invalid = (await this.dataSource.query(` - WITH events_with_duplicates AS ( - SELECT - pct."sourceId", - pct."typeId", - pct."toId", - COUNT(*) as "count" - FROM ${this.preCommitTableName} AS pct - GROUP BY 1,2,3 - HAVING COUNT(*) > 1 - ), deleted_events AS ( - DELETE FROM ${this.preCommitTableName} p - USING events_with_duplicates ewd - WHERE p."sourceId" = ewd."sourceId" AND - p."typeId" = ewd."typeId" AND - p."toId" = ewd."toId" AND - ewd."typeId" IS NOT NULL AND - ewd."sourceId" IS NOT NULL AND - ewd."toId" IS NOT NULL - RETURNING p."sourceId", p."typeId", p."toId", p."time", p."fromId", p."amount", p."details" - ) - INSERT INTO ${this.preCommitTableName} ("sourceId", "typeId", "toId", "time", "fromId", "amount", "details") - SELECT - DISTINCT ON ("sourceId", "typeId", "toId") - d."sourceId", - d."typeId", - d."toId", - d."time", - d."fromId", - d."amount", - d."details" - FROM deleted_events d - RETURNING "sourceId", "typeId", "toId" - `)) as { sourceId: string; typeId: number; toId: number }[]; - tmDelDupes(); - logger.debug(`merged ${invalid.length} events of duplicates`); - - const eventsToCommitCount = this.preCommitCount - invalid.length; - - logger.debug( - `committing ${eventsToCommitCount} events to event database`, - ); - // Write back into the main database - - if (this.recorderOptions.overwriteExistingEvents) { - logger.debug(`removing existing events`); - // Delete existing events - const tmDelEvents = timer("delete existing events"); - await this.dataSource.query( - ` - DELETE FROM event e - USING ${this.preCommitTableName} p - WHERE p."sourceId" = e."sourceId" AND - p."typeId" = e."typeId" AND - p."toId" = e."toId" - `, - ); - tmDelEvents(); - } - - const tmCommitEvents = timer("commit to event table"); - const pool = [this.dataSource]; - pool.push(...this.dataSourcePool); - - const limitSize = Math.round(eventsToCommitCount / pool.length); - - const concurrent = pool.reduce[]>((a, ds, i) => { - const offset = i * limitSize; - - const result = async () => { - const written = (await ds.query(` - INSERT INTO event ("sourceId", "typeId", "time", "toId", "fromId", "amount", "details") - SELECT - "sourceId", - "typeId", - "time", - "toId", - "fromId", - "amount", - "details" - FROM ${this.preCommitTableName} - OFFSET ${offset} - LIMIT ${limitSize} - ON CONFLICT ("sourceId", "typeId", "time", "toId") DO NOTHING - - RETURNING "sourceId", "typeId", "toId" - `)) as BatchResult[]; - - const writtenIds = written.map((w) => { - toIdsToResolve.push(w.toId); - return dbEventUniqueId({ - sourceId: w.sourceId, - typeId: w.typeId, - toId: w.toId, - }); - }); - - if (writtenIds.length > 0) { - await this.redis.sRem(this.preCommitTableName, writtenIds); - } - return written; - }; - a.push(result()); - return a; - }, []); - - const results = await Promise.all(concurrent); - const committedRefs = results.reduce((a, c) => { - // Don't use the spread operator `...` or we may experience call stack - // overflows. - c.forEach((r) => { - return a.push(r); - }); - - return a; - }, []); - - // Check redis for the skipped values - const skippedRedisMembers = await this.redis.sMembers( - this.preCommitTableName, - ); - const skippedRefs = skippedRedisMembers.map((s) => { - return splitDbEventUniqueId(s); - }); - logger.debug("skipped refs %d", skippedRefs.length); - - tmCommitEvents(); - - const uncommittedEvents = new UniqueArray( - eventUniqueId, - ); - - const artifactMap = this.artifactResolver.map; - const convertBatch = async (weakRefs: EventWeakRef[]) => { - const events: RecorderEventRef[] = []; - for (const ref of weakRefs) { - const artifact = await artifactMap.getIncomplete(ref.toId); - const t = RecorderEventType.fromDBEventType( - this.eventTypeIdMap[ref.typeId], - ); - events.push({ - sourceId: ref.sourceId, - to: artifact, - type: t, - }); - } - return events; - }; - - const committedEvents = await asyncBatchFlattened( - committedRefs, - this.options.maxBatchSize, - convertBatch, - ); - const skippedEvents = await asyncBatchFlattened( - skippedRefs, - this.options.maxBatchSize, - convertBatch, - ); - const invalidEvents = await asyncBatchFlattened( - invalid, - this.options.maxBatchSize, - convertBatch, - ); - invalidEvents.forEach((e) => { - uncommittedEvents.push(e); - }); - - if (committedEvents.length > 0) { - logger.debug( - `notifying of event completions for ${committedEvents.length} committed events`, - ); - this.notifySuccess(committedEvents); - } - if (skippedEvents.length > 0) { - logger.debug( - `notifying of event completions for ${skippedEvents.length} skipped events`, - ); - this.notifySuccess(skippedEvents); - } - committedEvents.forEach((e) => { - commitResult.committed.push(eventUniqueId(e)); - }); - skippedEvents.forEach((e) => { - commitResult.skipped.push(eventUniqueId(e)); - }); - } catch (err) { - logger.error(`error during commit`); - logger.error(`err=${JSON.stringify(err)}`); - this.emitter.emit("commit-error", err); - throw new RecorderError("commit error. failed to commit properly"); - } - await this.clean(); - return commitResult; - } - - async loadArtifacts() { - return this.artifactResolver.loadArtifacts(); - } - - private incompleteArtifactRedisKey(artifact: IncompleteArtifact) { - return `artifact::${artifact.name}::${artifact.namespace}`; - } - - private async getArtifactId(artifact: IncompleteArtifact) { - const res = await this.redis.get(this.incompleteArtifactRedisKey(artifact)); - if (!res) { - return null; - } - return parseInt(res); - } - - private async matchArtifactIds(artifacts: IncompleteArtifact[]) { - const res = await this.artifactResolver.map.resolveToIds(artifacts); - const matches = artifacts.map((a, i) => { - const id: number | null = res[i]; - return [a, id] as [IncompleteArtifact, number | null]; - }); - return matches; - } - - private async matchArtifactIdsOrFail( - artifacts: IncompleteArtifact[], - ): Promise { - const matches = await this.matchArtifactIds(artifacts); - const nulls = matches.filter((m) => { - return m[1] === null; - }); - if (nulls.length > 0) { - // Log for debugging purposes - nulls.forEach((n) => { - logger.debug( - `missing artifactId for Artifact[name=${n[0].name}, namespace=${n[0].namespace}]`, - ); - }); - throw new RecorderError(`missing ${nulls.length} artifact ids`); - } - return matches; - } - - private async uniqArtifactsFromEvents(events: IRecorderEvent[]) { - const determineUniqArtifactsTimers = timer("deteremine unique artifacts"); - const uniqArtifacts = new UniqueArray((a) => { - return `${a.name}:${a.namespace}`; - }); - - for (const event of events) { - const toId: number | null = null; - const fromId: number | null = null; - - if (!toId) { - uniqArtifacts.push(event.to); - } - - if (event.from) { - if (!fromId) { - uniqArtifacts.push(event.from); - } - } - } - const matches = await this.matchArtifactIds(uniqArtifacts.items()); - const newArtifacts = matches.reduce((a, c) => { - if (c[1] === null) { - a.push(c[0]); - } - return a; - }, []); - determineUniqArtifactsTimers(); - return [uniqArtifacts.items(), newArtifacts]; - } - - async wait( - handles: RecordHandle[], - timeoutMs?: number, - ): Promise> { - timeoutMs = timeoutMs === undefined ? this.options.timeoutMs : timeoutMs; - - const results: AsyncResults = { - success: [], - errors: [], - }; - - const expectedMap: Record = {}; - let expectedCount = 0; - - handles.forEach((h) => { - // We can skip things that have already been recorded - if (this.isKnownByIdStr(h.id)) { - return; - } - - expectedMap[h.id] = 1; - expectedCount += 1; - }); - if (expectedCount === 0) { - return results; - } - - return new Promise((resolve, reject) => { - let count = 0; - const timeout = setTimeout(() => { - return reject( - new RecorderError("timed out waiting for recordings to complete"), - ); - }, timeoutMs); - - const stopListening = () => { - this.emitter.removeListener("event-record-failure", failure); - this.emitter.removeListener("event-record-success", success); - clearTimeout(timeout); - }; - - const eventCallback = (err: unknown | null, uniqueId: string) => { - if (expectedMap[uniqueId] === 1) { - expectedMap[uniqueId] = 0; - count += 1; - - if (err) { - results.errors.push(err); - } else { - results.success.push(uniqueId); - } - - // Why's it greater tho? - if (expectedCount >= count) { - stopListening(); - return resolve(results); - } - } - }; - - const failure = (err: unknown, uniqueId: string) => { - eventCallback(err, uniqueId); - }; - const success = (uniqueId: string) => { - eventCallback(null, uniqueId); - }; - - this.emitter.addListener("commit-error", (err: unknown) => { - stopListening(); - return reject(err); - }); - - this.emitter.addListener("event-record-failure", failure); - - this.emitter.addListener("event-record-success", success); - }); - } - - private async waitTillAvailable(): Promise { - if (!this.isQueueFull()) { - return; - } - logger.debug("recorder: waiting till available"); - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new Error("timed out waiting for recorder to become available")); - }, this.options.timeoutMs); - - const checkQueue = () => { - // Start event waiting loop for flushes - if (!this.isQueueFull()) { - clearTimeout(timeout); - resolve(); - } else { - logger.debug("recorder queue full. applying backpressure"); - this.emitter.once("flush", checkQueue); - } - }; - checkQueue(); - }); - } - - private isQueueFull() { - return this.queueSize >= this.options.maxBatchSize; - } - - setActorScope(namespaces: ArtifactNamespace[], types: ArtifactType[]) { - this.namespaces = namespaces; - this.types = types; - } - - setRange(range: Range) { - // This is used to optimize queries. - this.range = range; - } - - setOptions(options: EventRecorderOptions): void { - if (options.overwriteExistingEvents) { - logger.debug("setting recorder to overwrite existing events"); - } - this.recorderOptions = options; - } - - async setup() { - await this.loadEventTypes(); - } - - async loadEventTypes() { - // Load all event types from the database. This is not expected to change during - // execution so this should only happen once. - const eventTypes = await this.eventTypeRepository.find(); - - eventTypes.forEach((t) => { - this.eventTypeIdMap[t.id] = t; - const recorderEventType = RecorderEventType.fromDBEventType(t); - this.recorderEventTypeStringIdMap[recorderEventType.toString()] = - recorderEventType; - const nameAndVersionMap: Record = - this.eventTypeNameAndVersionMap[t.name] || {}; - - nameAndVersionMap[t.version] = t; - this.eventTypeNameAndVersionMap[t.name] = nameAndVersionMap; - }); - } - - private isKnownByIdStr(id: string) { - return this.recordedHistory[id] === true; - } - - async record(input: IncompleteEvent): Promise { - if (this.committing) { - throw new RecorderError("recorder is committing. writes disallowed"); - } - if (this.closing) { - throw new RecorderError("recorder is closing. writes disallowed"); - } - const event = RecorderEvent.fromIncompleteEvent(input); - - await this.waitTillAvailable(); - - // Queue an event for writing - this.eventQueue.push(event); - this.queueSize += 1; - - // Upon the first "record" begin the flush sequence - this.scheduleFlush(1); - - const uniqueId = eventUniqueId(event); - - return { - id: uniqueId, - }; - } - - addListener(listener: string, callback: (...args: any) => void) { - return this.emitter.on(listener, callback); - } - - removeListener(listener: string, callback: (...args: any) => void) { - this.emitter.removeListener(listener, callback); - } - - private scheduleFlush(size: number) { - this.flusher.notify(size); - } - - private async flushAll() { - const batchId = this.batchCounter; - this.batchCounter += 1; - const processing = this.eventQueue; - this.eventQueue = []; - try { - await this.processEvents(batchId, processing); - } catch (err) { - logger.error( - `error caught while processing one of the events in batch[${this.recorderId}:${batchId}]`, - err, - ); - - // Report errors to all of the promises listening on specific events - this.notifyFailure(processing, err); - throw err; - } - - this.queueSize = 0; - } - - async close(): Promise { - if (this.closing) { - throw new RecorderError("there should only be one call to close()"); - } - - // If we have something open we need to commit it and close; - - // Lock the recorder - this.closing = true; - return new Promise((resolve, reject) => { - logger.debug("closing the recorder"); - - const timeout = setTimeout(() => { - reject(new RecorderError("timed out closing the recorder")); - }, this.options.timeoutMs); - - const closeAll = () => { - this.emitter.removeAllListeners(); - this.flusher.clear(); - clearTimeout(timeout); - }; - if (this.isPreCommitTableOpen) { - this.commit() - .then(() => { - logger.debug("recorder flushed and committed"); - closeAll(); - resolve(); - }) - .catch((err) => { - closeAll(); - reject(err); - }); - } else { - closeAll(); - resolve(); - } - }); - } - - private async processEvents( - batchId: number, - processing: IRecorderEvent[], - ): Promise { - if (processing.length === 0) { - logger.debug(`nothing queued for batch[${this.recorderId}:${batchId}]`); - return; - } - logger.debug(`processing ${processing.length} events`); - - const newEvents = []; - // Filter things that are out of the expected range - for (const event of processing) { - // Ignore events outside the range of events if we're not overwriting - // things otherwise everything is in play - if ( - !isWithinRange(this.range!, event.time) && - !this.recorderOptions.overwriteExistingEvents - ) { - logger.debug(`${batchId}: received event out of range. skipping`); - console.log("%j", event); - this.commitResult!.skipped.push(eventUniqueId(event)); - this.notifySuccess([event]); - continue; - } - newEvents.push(event); - } - - // Get all of the unique artifacts - const [allArtifacts, newArtifacts] = - await this.uniqArtifactsFromEvents(newEvents); - - const writeArtifacts = async () => { - const tmWriteArtifacts = timer("writing new artifacts"); - if (newArtifacts.length > 0) { - const artifactResponse = (await this.dataSource.query( - ` - INSERT INTO artifact("name", "namespace", "type", "url") - SELECT * FROM unnest( - $1::text[], $2::artifact_namespace_enum[], $3::artifact_type_enum[], $4::text[] - ) - ON CONFLICT ("name", "namespace") DO NOTHING - RETURNING "name", "namespace", "id" - `, - newArtifacts.reduce< - [string[], string[], string[], (string | null)[]] - >( - (a, c) => { - a[0].push(c.name); - a[1].push(c.namespace); - a[2].push(c.type); - a[3].push(c.url || null); - return a; - }, - [[], [], [], []], - ), - )) as { name: string; namespace: string; id: number }[]; - tmWriteArtifacts(); - logger.debug(`added ${artifactResponse.length} new artifacts`); - } - }; - - try { - await this.retryDbCall(writeArtifacts); - } catch (err) { - logger.error("encountered an error writing to the artifacts table"); - logger.debug(typeof err); - logger.error(`Error type=${typeof err}`); - //logger.error(`Error as JSON=${JSON.stringify(err)}`); - logger.error(err); - this.notifyFailure(newEvents, err); - this.emitter.emit("error", err); - } - - const loadArtifactIdsTimers = timer( - `load ${allArtifacts.length} artifact ids for current flush`, - ); - await this.loadArtifacts(); - loadArtifactIdsTimers(); - - const dbEvents = await this.createEventsFromRecorderEvents( - batchId, - newEvents, - ); - - const precommitWrites = async () => { - const precommitTimer = timer("write to precommit table"); - const pgWrite = this.dataSource.query( - ` - INSERT INTO ${this.preCommitTableName} - ( - "sourceId", "typeId", "time", - "toId", "fromId", "amount", "details" - ) - ( - select * from unnest( - $1::text[], $2::int[], $3::timestamptz[], - $4::int[], - $5::int[], - $6::float[], $7::jsonb[] - ) - ) - RETURNING "id" - `, - [ - dbEvents.sourceIds, - dbEvents.typeIds, - dbEvents.times, - dbEvents.toIds, - dbEvents.fromIds, - dbEvents.amounts, - dbEvents.details, - ], - ); - await this.redis.sAdd(this.preCommitTableName, dbEvents.uniqueIds); - const res = (await pgWrite) as { id: number }[]; - precommitTimer(); - return res; - }; - - // Insert events into the pre commit event table - try { - const result = await this.retryDbCall(precommitWrites); - if (result.length !== newEvents.length) { - throw new RecorderError( - `recorder writes failed. Expected ${newEvents.length} writes but only received ${result.length}`, - ); - } - this.preCommitCount += result.length; - logger.debug( - `completed writing batch of ${result.length} to the temporary table`, - ); - } catch (err) { - logger.error("encountered an error writing to the temporary table"); - logger.debug(typeof err); - logger.error(`Error type=${typeof err}`); - logger.error(`Error as JSON=${JSON.stringify(err)}`); - logger.error(err); - this.notifyFailure(newEvents, err); - this.emitter.emit("error", err); - } - - logger.info(`finished flushing for batch[${this.recorderId}:${batchId}]`); - } - - protected async retryDbCall( - cb: () => Promise, - retryCount: number = 10, - ): Promise { - let timeoutMs = 100; - for (let i = 0; i < retryCount; i++) { - try { - return await cb(); - } catch (err) { - // Throw the final error - if (i === retryCount - 1) { - throw err; - } - if (err === undefined) { - logger.debug( - `received an undefined error from the database. sleeping then retrying`, - ); - await asyncTimeout(timeoutMs); - timeoutMs += timeoutMs; - } else { - throw err; - } - } - } - throw new RecorderError(`maximum retries for database call reached.`); - } - - protected notifySuccess(events: RecorderEventRef[]) { - events.forEach((e) => { - // Mark as known event - const uniqueId = eventUniqueId(e); - this.recordedHistory[uniqueId] = true; - - // Notify any subscribers that the event has been recorded - this.emitter.emit(uniqueId, null, uniqueId); - this.emitter.emit("event-record-success", uniqueId); - }); - } - - protected notifyFailure(events: RecorderEventRef[], err: unknown) { - events.forEach((e) => { - // Notify any subscribers that the event has failed to record - const uniqueId = eventUniqueId(e); - this.recordedHistory[uniqueId] = true; - - this.emitter.emit(uniqueId, err, ""); - this.emitter.emit("event-record-failure", err, uniqueId); - // FIXME... this should be a wrapped error - this.emitter.emit( - "error", - new RecorderError(`error recording ${uniqueId}`), - ); - }); - } - - protected async createEventsFromRecorderEvents( - batchId: number, - recorderEvents: IRecorderEvent[], - ): Promise { - const inputs: PGRecorderTempEventBatchInput = { - sourceIds: [], - typeIds: [], - times: [], - toIds: [], - fromIds: [], - amounts: [], - details: [], - uniqueIds: [], - }; - const toArtifacts: IncompleteArtifact[] = []; - const fromArtifacts: (IncompleteArtifact | null)[] = []; - for (const e of recorderEvents) { - const eventType = this.resolveEventType(e.type); - toArtifacts.push(e.to); - - if (e.from) { - fromArtifacts.push(e.from); - } - - inputs.sourceIds.push(e.sourceId); - inputs.typeIds.push(eventType.id.valueOf()); - inputs.times.push(e.time.toJSDate()); - inputs.amounts.push(e.amount); - inputs.details.push(e.details); - //{ sourceId: e.sourceId, typeId: eventType.id.valueOf(), toId })); - } - inputs.toIds = (await this.artifactResolver.map.resolveToIds( - toArtifacts, - )) as number[]; - inputs.fromIds = - await this.artifactResolver.map.resolveToIds(fromArtifacts); - - inputs.toIds.forEach((toId, i) => { - inputs.uniqueIds.push( - dbEventUniqueId({ - sourceId: inputs.sourceIds[i], - typeId: inputs.typeIds[i], - toId: toId, - }), - ); - }); - - return inputs; - } - - protected resolveEventTypeById(typeId: number) { - return this.eventTypeIdMap[typeId]; - } - - protected resolveEventType(type: IRecorderEventType) { - const versions = this.eventTypeNameAndVersionMap[type.name] || {}; - const resolved = versions[type.version]; - if (!resolved) { - throw new RecorderError( - `Event type ${type.name} v${type.version} does not exist`, - ); - } - return resolved; - } -} diff --git a/indexer/src/recorder/types.ts b/indexer/src/recorder/types.ts deleted file mode 100644 index 7d1222791..000000000 --- a/indexer/src/recorder/types.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { - Artifact, - Event, - ArtifactNamespace, - ArtifactType, -} from "../db/orm-entities.js"; -import { FindOptionsWhere } from "typeorm"; -import { DateTime } from "luxon"; -import { GenericError } from "../common/errors.js"; -import { Range } from "../utils/ranges.js"; -import { EventEmitter } from "node:events"; -import { AsyncResults } from "../utils/async-results.js"; - -export class RecorderError extends GenericError {} -export class GrouperError extends GenericError {} - -export class UnknownActor extends RecorderError {} - -export interface EventRecorderOptions { - overwriteExistingEvents: boolean; -} - -export type RecordResponse = string; - -export interface RecordHandle { - id: string; -} - -export interface ICommitResult { - committed: string[]; - skipped: string[]; - invalid: string[]; - errors: unknown[]; - - collectResultsForHandles( - handles: RecordHandle[], - ): AsyncResults; -} - -export interface IEventRecorderClient { - // Record a single event. These are batched - record(event: IncompleteEvent): Promise; -} - -export interface IEventRecorder extends IEventRecorderClient { - setup(): Promise; - - setActorScope(namespaces: ArtifactNamespace[], types: ArtifactType[]): void; - - setRange(range: Range): void; - - setOptions(options: EventRecorderOptions): void; - - begin(): Promise; - commit(): Promise; - rollback(): Promise; - - // Call this when you're done recording - close(): Promise; - - addListener(listener: "error", cb: (err: unknown) => void): EventEmitter; - addListener(listener: "flush-complete", cb: () => void): EventEmitter; - - removeListener(listener: "error", cb: (err: unknown) => void): void; - removeListener(listener: "flush-complete", cb: (err: unknown) => void): void; -} - -export type RecorderFactory = () => Promise; - -export interface IActorDirectory { - fromId(id: number): Promise; - - resolveArtifactId(artifact: IncompleteArtifact): Promise; - - knownArtifactsFrom(artifacts: IncompleteArtifact[]): Artifact[]; - - unknownArtifactsFrom(artifacts: IncompleteArtifact[]): IncompleteArtifact[]; -} - -export interface IEventTypeStrategy { - // This library assumes that each event type has a way of determining - // uniqueness with events. This is for idempotency's sake. Most of the time - // this likely doesn't need to be used. - //uniqueEventsQueryFor(resolver: IActorResolver, events: IncompleteEvent[]): Prisma.EventWhereInput; - type: IRecorderEventType; - - // This is the query to use to get all of the events of a specific EventType - all(directory: IActorDirectory): FindOptionsWhere; - - idFromEvent(directory: IActorDirectory, event: Event): Promise; - - idFromIncompleteEvent( - directory: IActorDirectory, - event: IRecorderEvent, - ): Promise; -} - -export type IncompleteActor = { - name: N; - namespace: M; -}; - -export type IncompleteArtifact = { - name: string; - namespace: ArtifactNamespace; - type: ArtifactType; -} & Partial; - -export interface IRecorderEventType { - name: string; - version: number; - toString(): string; -} - -export type IncompleteEventType = Pick; - -export interface IncompleteEvent { - time: DateTime; - type: IRecorderEventType; - sourceId: string; - to: IncompleteArtifact; - from?: IncompleteArtifact; - amount: number; - details?: object; - size?: bigint; -} - -export interface IRecorderEvent { - id: number | null; - time: DateTime; - type: IRecorderEventType; - sourceId: string; - to: IncompleteArtifact; - from: IncompleteArtifact | null; - amount: number; - details: object; -} - -export type EventGroupRecorderCallback = (results: AsyncResults) => void; - -export interface IEventGroupRecorder { - record(event: IncompleteEvent): Promise; - - handlesForGroup(g: G): RecordHandle[]; -} diff --git a/indexer/src/scheduler/common.ts b/indexer/src/scheduler/common.ts deleted file mode 100644 index 29c4a169d..000000000 --- a/indexer/src/scheduler/common.ts +++ /dev/null @@ -1,238 +0,0 @@ -import { FindOptionsWhere, Repository } from "typeorm"; -import { Artifact, Project } from "../db/orm-entities.js"; -import { Range } from "../utils/ranges.js"; -import { - CollectResponse, - IArtifactGroupCommitmentProducer, - IArtifactGroup, - IEventCollector, -} from "./types.js"; -import { TimeSeriesCacheWrapper } from "../cacher/time-series.js"; -import { IEventRecorderClient } from "../recorder/types.js"; - -export class BasicArtifactGroup implements IArtifactGroup { - private _name: string; - private _meta: T; - private _artifacts: Artifact[]; - - constructor(name: string, meta: T, artifacts: Artifact[]) { - this._name = name; - this._meta = meta; - this._artifacts = artifacts; - } - - async name() { - return this._name; - } - - async meta() { - return this._meta; - } - - async artifacts() { - return this._artifacts; - } - - async createMissingGroup(missing: Artifact[]): Promise> { - return new BasicArtifactGroup(this._name, this._meta, missing); - } -} - -export class ProjectArtifactGroup extends BasicArtifactGroup { - static create(project: Project, artifacts: Artifact[]) { - return new ProjectArtifactGroup( - `Project[slug=${project.slug}]`, - project, - artifacts, - ); - } -} - -export abstract class BaseEventCollector - implements IEventCollector -{ - async allArtifacts(): Promise { - throw new Error( - "#allArtifacts not implemented for a base collector artifact", - ); - } - - /* eslint-disable-next-line require-yield */ - async *groupedArtifacts(): AsyncGenerator> { - throw new Error("#groupedArtifacts() not implemented"); - } - - collect( - _group: IArtifactGroup, - _range: Range, - _committer: IArtifactGroupCommitmentProducer, - ): Promise { - throw new Error("#collect not implemented"); - } -} - -export class ProjectArtifactsCollector extends BaseEventCollector { - protected projectRepository: Repository; - protected cache: TimeSeriesCacheWrapper; - protected recorder: IEventRecorderClient; - protected artifactsWhere: FindOptionsWhere; - - constructor( - projectRepository: Repository, - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - artifactsWhere: FindOptionsWhere, - ) { - super(); - this.projectRepository = projectRepository; - this.cache = cache; - this.recorder = recorder; - this.artifactsWhere = artifactsWhere; - } - - async allArtifacts(): Promise { - const projects = await this.allProjectsWithArtifacts(); - const uniqueIds: Record = {}; - return projects.reduce((artifacts, p) => { - p.artifacts.forEach((a) => { - if (uniqueIds[a.id]) { - return; - } - uniqueIds[a.id] = true; - artifacts.push(a); - }); - return artifacts; - }, []); - } - - protected async allProjectsWithArtifacts(): Promise { - return await this.projectRepository.find({ - relations: { - artifacts: true, - }, - where: { - artifacts: this.artifactsWhere, - }, - }); - } - - async *groupedArtifacts(): AsyncGenerator> { - const projects = await this.allProjectsWithArtifacts(); - - // Emit each project's artifacts as a group of artifacts to record - for (const project of projects) { - yield ProjectArtifactGroup.create(project, project.artifacts); - } - } - - collect( - _group: IArtifactGroup, - _range: Range, - _committer: IArtifactGroupCommitmentProducer, - ): Promise { - throw new Error("#collect Not implemented"); - } -} - -export type Batch = { - size: number; - name: string; - totalBatches: number; -}; - -export class BatchArtifactsCollector extends BaseEventCollector { - protected cache: TimeSeriesCacheWrapper; - protected recorder: IEventRecorderClient; - protected batchSize: number; - - constructor( - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - batchSize: number, - ) { - super(); - this.cache = cache; - this.recorder = recorder; - this.batchSize = batchSize; - } - - async allArtifacts(): Promise { - throw new Error("#allArtifacts not implemented for a batch artifact"); - } - - async *groupedArtifacts(): AsyncGenerator> { - const allUnsorted = await this.allArtifacts(); - const all = allUnsorted.sort((a, b) => { - return a.id - b.id; - }); - - let batchNumber = 0; - const batches = Math.ceil(all.length / this.batchSize); - // Emit each project's artifacts as a group of artifacts to record - for (let i = 0; i < all.length; i += this.batchSize) { - batchNumber += 1; - const batchArtifacts = all.slice(i, i + this.batchSize); - const batchName = `Batch[${batchNumber}/${batches}]`; - const batch: Batch = { - totalBatches: batches, - size: batchArtifacts.length, - name: batchName, - }; - - yield new BasicArtifactGroup(batchName, batch, batchArtifacts); - } - } - - collect( - _group: IArtifactGroup, - _range: Range, - _committer: IArtifactGroupCommitmentProducer, - ): Promise { - throw new Error("Not implemented"); - } -} - -export class BatchedProjectArtifactsCollector extends BatchArtifactsCollector { - protected projectRepository: Repository; - protected artifactsWhere: FindOptionsWhere; - - constructor( - projectRepository: Repository, - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - batchSize: number, - artifactsWhere: FindOptionsWhere, - ) { - super(recorder, cache, batchSize); - this.projectRepository = projectRepository; - this.artifactsWhere = artifactsWhere; - this.cache = cache; - this.recorder = recorder; - } - - async allArtifacts(): Promise { - const projects = await this.allProjectsWithArtifacts(); - const uniqueIds: Record = {}; - return projects.reduce((artifacts, p) => { - p.artifacts.forEach((a) => { - if (uniqueIds[a.id]) { - return; - } - uniqueIds[a.id] = true; - artifacts.push(a); - }); - return artifacts; - }, []); - } - - protected async allProjectsWithArtifacts(): Promise { - return await this.projectRepository.find({ - relations: { - artifacts: true, - }, - where: { - artifacts: this.artifactsWhere, - }, - }); - } -} diff --git a/indexer/src/scheduler/github.ts b/indexer/src/scheduler/github.ts deleted file mode 100644 index a326d70ff..000000000 --- a/indexer/src/scheduler/github.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Octokit } from "octokit"; -import { WorkerSpawner } from "./types.js"; - -export interface GithubWorkerSpawnerOptions { - owner: string; - repo: string; - workflowId: string; - ref: string; -} - -export class GithubWorkerSpawner implements WorkerSpawner { - private gh: Octokit; - private options: GithubWorkerSpawnerOptions; - - constructor(gh: Octokit, options: GithubWorkerSpawnerOptions) { - this.gh = gh; - this.options = options; - } - - async spawn(group?: string): Promise { - await this.gh.rest.actions.createWorkflowDispatch({ - owner: this.options.owner, - repo: this.options.repo, - workflow_id: this.options.workflowId, - ref: this.options.ref, - inputs: { - group: group, - }, - }); - } -} diff --git a/indexer/src/scheduler/index.ts b/indexer/src/scheduler/index.ts deleted file mode 100644 index 6fb74ab46..000000000 --- a/indexer/src/scheduler/index.ts +++ /dev/null @@ -1,371 +0,0 @@ -import { BatchEventRecorder } from "../recorder/recorder.js"; -import { BaseScheduler, Config, ExecutionMode } from "./types.js"; -import { - TimeSeriesCacheManager, - TimeSeriesCacheWrapper, -} from "../cacher/time-series.js"; -import { CommonArgs } from "../utils/api.js"; -import { DateTime } from "luxon"; -import { EventPointerManager } from "./pointers.js"; -import { FundingEventsCollector } from "../collectors/dune-funding-events.js"; -import { FundingEventsClient } from "../collectors/dune/funding-events/client.js"; -import { DuneClient } from "@cowprotocol/ts-dune-client"; -import { - DUNE_API_KEY, - DUNE_CSV_DIR_PATH, - REDIS_URL, - GITHUB_TOKEN, - GITHUB_WORKERS_OWNER, - GITHUB_WORKERS_REF, - GITHUB_WORKERS_REPO, - GITHUB_WORKERS_WORKFLOW_ID, -} from "../config.js"; -import { - ArtifactNamespace, - ArtifactType, - CollectionType, - EventType, - Recording, - RepoDependency, -} from "../db/orm-entities.js"; -import { EventPointerRepository } from "../db/events.js"; -import { ArtifactRepository } from "../db/artifacts.js"; -import { AppDataSource } from "../db/data-source.js"; -import { ProjectRepository } from "../db/project.js"; -import { Octokit } from "octokit"; -import { throttling } from "@octokit/plugin-throttling"; -import { GithubCommitCollector } from "../collectors/github-commits.js"; -import { GithubIssueCollector } from "../collectors/github-issues.js"; -import { GithubFollowingCollector } from "../collectors/github-followers.js"; -import { DailyContractUsageCollector } from "../collectors/dune-daily-contract-usage.js"; -import { DailyContractUsageClient } from "../collectors/dune/daily-contract-usage/client.js"; -import path from "path"; -import { GithubWorkerSpawner } from "./github.js"; -import { JobExecutionRepository, JobsRepository } from "../db/jobs.js"; -import { NpmDownloadCollector } from "../collectors/npm-downloads.js"; -import { DependentsPeriodicCollector } from "../collectors/dependents.js"; -import { CollectionRepository } from "../db/collection.js"; -import { BigQuery } from "@google-cloud/bigquery"; -import { Storage } from "@google-cloud/storage"; -import { DuneCSVUploader } from "../collectors/dune/utils/csv-uploader.js"; -import { createClient } from "redis"; - -export type SchedulerArgs = CommonArgs & { - recorderTimeoutMs: number; - overwriteExistingEvents: boolean; - batchSize: number; - recorderConnections: number; -}; - -export type SchedulerManualArgs = SchedulerArgs & { - collector: string; - startDate: DateTime; - endDate: DateTime; - executionMode: ExecutionMode; - reindex: boolean; -}; - -export type SchedulerWorkerArgs = SchedulerArgs & { - group: string; - resumeWithLock: boolean; -}; - -export type SchedulerQueueAllArgs = SchedulerArgs & { - baseDate: DateTime; -}; - -export type SchedulerQueueJobArgs = SchedulerArgs & { - collector: string; - baseDate: DateTime; - startDate: DateTime; - endDate: DateTime; -}; - -export type SchedulerQueueBackfill = SchedulerArgs & { - collector: string; - startDate: DateTime; - endDate: DateTime; - backfillIntervalDays: number; -}; - -// Entrypoint for the scheduler. Currently not where it should be but this is quick. -export async function configure(args: SchedulerArgs) { - const cacheManager = new TimeSeriesCacheManager(args.cacheDir); - const cache = new TimeSeriesCacheWrapper(cacheManager); - - const AppOctoKit = Octokit.plugin(throttling); - const gh = new AppOctoKit({ - auth: GITHUB_TOKEN, - throttle: { - onRateLimit: (retryAfter, options, octokit, retryCount) => { - const opts = options as { - method: string; - url: string; - }; - octokit.log.warn( - `Request quota exhausted for request ${opts.method} ${opts.url}`, - ); - // Retry up to 50 times (that should hopefully be more than enough) - if (retryCount < 50) { - octokit.log.info(`Retrying after ${retryAfter} seconds!`); - return true; - } else { - octokit.log.error("failed too many times waiting for github quota"); - } - }, - onSecondaryRateLimit: (retryAfter, options, octokit, retryCount) => { - const opts = options as { - method: string; - url: string; - }; - octokit.log.warn( - `Secondary rate limit detected for ${opts.method} ${opts.url}`, - ); - if (retryCount < 3) { - octokit.log.info(`Retrying after ${retryAfter} seconds!`); - return true; - } else { - octokit.log.info(`Failing now`); - } - }, - }, - }); - - const config = new Config(); - const eventPointerManager = new EventPointerManager( - AppDataSource, - EventPointerRepository, - { - batchSize: args.batchSize, - }, - ); - - const spawner = new GithubWorkerSpawner(gh, { - owner: GITHUB_WORKERS_OWNER, - repo: GITHUB_WORKERS_REPO, - ref: GITHUB_WORKERS_REF, - workflowId: GITHUB_WORKERS_WORKFLOW_ID, - }); - - const redisClient = createClient({ - url: REDIS_URL, - }); - - // Used to allow for lazy loading of the redis client. This is a hack to - // disable redis when it's not needed for some scheduler commands - const redisFactory = async () => { - if (!redisClient.isReady) { - await redisClient.connect(); - } - return redisClient; - }; - - const scheduler = new BaseScheduler( - args.runDir, - () => { - const recorder = new BatchEventRecorder( - AppDataSource, - [], - AppDataSource.getRepository(Recording), - AppDataSource.getRepository(EventType), - redisFactory, - { - timeoutMs: args.recorderTimeoutMs, - }, - ); - recorder.setOptions({ - overwriteExistingEvents: args.overwriteExistingEvents, - }); - return Promise.resolve(recorder); - }, - config, - eventPointerManager, - cache, - spawner, - JobsRepository, - JobExecutionRepository, - ); - const dune = new DuneClient(DUNE_API_KEY); - - scheduler.registerEventCollector({ - create: async (_config, recorder, cache) => { - const client = new FundingEventsClient(dune); - - const collector = new FundingEventsCollector( - client, - ProjectRepository, - recorder, - cache, - 10000, - ); - return collector; - }, - name: "dune-funding-events", - description: "gathers funding events", - group: "dune", - schedule: "weekly", - artifactScope: [ArtifactNamespace.OPTIMISM, ArtifactNamespace.ETHEREUM], - artifactTypeScope: [ - ArtifactType.EOA_ADDRESS, - ArtifactType.SAFE_ADDRESS, - ArtifactType.CONTRACT_ADDRESS, - ], - }); - - scheduler.registerEventCollector({ - create: async (_config, recorder, cache) => { - const collector = new GithubCommitCollector( - ProjectRepository, - gh, - recorder, - cache, - // Arrived at this batch size through trial and error. 500 was too much. - // Many "Premature close" errors. The less we have the less opportunity - // for HTTP5XX errors it seems. This batch size is fairly arbitrary. - 50, - ); - return collector; - }, - name: "github-commits", - description: "Collects github commits", - group: "github", - schedule: "daily", - dataSetIncludesNow: true, - artifactScope: [ArtifactNamespace.GITHUB], - artifactTypeScope: [ - ArtifactType.GITHUB_ORG, - ArtifactType.GITHUB_USER, - ArtifactType.GIT_EMAIL, - ArtifactType.GIT_NAME, - ArtifactType.GIT_REPOSITORY, - ], - }); - - scheduler.registerEventCollector({ - create: async (_config, recorder, cache) => { - const collector = new GithubIssueCollector( - ProjectRepository, - recorder, - cache, - 100, - ); - return collector; - }, - name: "github-issues", - description: "Collects github pull requests and issues", - group: "github", - schedule: "daily", - dataSetIncludesNow: true, - artifactScope: [ArtifactNamespace.GITHUB], - artifactTypeScope: [ - ArtifactType.GITHUB_ORG, - ArtifactType.GITHUB_USER, - ArtifactType.GIT_EMAIL, - ArtifactType.GIT_NAME, - ArtifactType.GIT_REPOSITORY, - ], - }); - - scheduler.registerEventCollector({ - create: async (_config, recorder, cache) => { - const collector = new GithubFollowingCollector( - ProjectRepository, - recorder, - cache, - 10, - ); - return collector; - }, - name: "github-followers", - description: "Collects github pull requests and issues", - group: "github", - schedule: "daily", - dataSetIncludesNow: true, - artifactScope: [ArtifactNamespace.GITHUB], - artifactTypeScope: [ - ArtifactType.GITHUB_ORG, - ArtifactType.GITHUB_USER, - ArtifactType.GIT_EMAIL, - ArtifactType.GIT_NAME, - ArtifactType.GIT_REPOSITORY, - ], - }); - - scheduler.registerEventCollector({ - create: async (_config, recorder, cache) => { - const client = new DailyContractUsageClient( - dune, - new DuneCSVUploader(DUNE_API_KEY), - { - csvDirPath: DUNE_CSV_DIR_PATH, - }, - ); - const collector = new DailyContractUsageCollector( - client, - ProjectRepository, - recorder, - cache, - { - knownUserAddressesSeedPath: path.join( - args.cacheDir, - "known-user-addresses-seed.json", - ), - mode: "api", - }, - ); - return collector; - }, - name: "dune-daily-contract-usage", - description: "Collects github pull requests and issues", - group: "dune", - schedule: "daily", - artifactScope: [ArtifactNamespace.OPTIMISM], - artifactTypeScope: [ - ArtifactType.CONTRACT_ADDRESS, - ArtifactType.FACTORY_ADDRESS, - ArtifactType.EOA_ADDRESS, - ArtifactType.SAFE_ADDRESS, - ], - }); - - scheduler.registerEventCollector({ - create: async (_config, recorder, cache) => { - const collector = new NpmDownloadCollector( - ProjectRepository, - recorder, - cache, - 200, - ); - return collector; - }, - name: "npm-downloads", - description: "Collects npm download metrics by day", - group: "npm", - schedule: "daily", - artifactScope: [ArtifactNamespace.NPM_REGISTRY], - artifactTypeScope: [ArtifactType.NPM_PACKAGE], - }); - - scheduler.registerPeriodicCollector({ - create: async (_config, _cache) => { - return new DependentsPeriodicCollector( - AppDataSource, - eventPointerManager, - AppDataSource.getRepository(RepoDependency), - ArtifactRepository, - CollectionRepository, - AppDataSource.getRepository(CollectionType), - ProjectRepository, - new BigQuery(), - "opensource_observer", - new Storage(), - "oso-csv-exports", - ); - }, - name: "dependents", - description: "Periodically collect dependencies", - schedule: "monthly", - }); - - return scheduler; -} diff --git a/indexer/src/scheduler/pointers.test.ts b/indexer/src/scheduler/pointers.test.ts deleted file mode 100644 index 60541ceb7..000000000 --- a/indexer/src/scheduler/pointers.test.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { DateTime } from "luxon"; -import { ArtifactRepository } from "../db/artifacts.js"; -import { EventPointerRepository } from "../db/events.js"; -import { withDbDescribe } from "../db/testing.js"; -import { - Artifact, - ArtifactNamespace, - ArtifactType, - EventPointer, -} from "../index.js"; -import { EventPointerManager } from "./pointers.js"; -import { AppDataSource } from "../db/data-source.js"; -import { rangeFromISO, Range } from "../utils/ranges.js"; -import { jest } from "@jest/globals"; - -jest.setTimeout(60000); - -withDbDescribe("EventPointerManager", () => { - let artifact0: Artifact; - let eventPointer0: EventPointer; - let eventPointer1: EventPointer; - let eventPointers: EventPointer[]; - let manager: EventPointerManager; - - beforeEach(async () => { - // Setup the database - const updateArtifacts = ArtifactRepository.create({ - name: "test0", - namespace: ArtifactNamespace.OPTIMISM, - type: ArtifactType.CONTRACT_ADDRESS, - }); - artifact0 = await ArtifactRepository.save(updateArtifacts); - - eventPointer0 = await EventPointerRepository.save( - EventPointerRepository.create({ - artifact: artifact0, - collector: "test", - startDate: DateTime.fromISO("2023-01-01T00:00:00Z").toJSDate(), - endDate: DateTime.fromISO("2023-02-01T00:00:00Z").toJSDate(), - version: 0, - }), - ); - - eventPointer1 = await EventPointerRepository.save( - EventPointerRepository.create({ - artifact: artifact0, - collector: "test", - startDate: DateTime.fromISO("2023-03-01T00:00:00Z").toJSDate(), - endDate: DateTime.fromISO("2023-04-01T00:00:00Z").toJSDate(), - version: 0, - }), - ); - - eventPointers = [eventPointer0, eventPointer1]; - - manager = new EventPointerManager(AppDataSource, EventPointerRepository); - }); - - function itWithEventPointers( - message: string, - range: Range, - expectedPointerIndexes: number[], - ) { - it(message, async () => { - const expectedMatches = expectedPointerIndexes.map( - (n) => eventPointers[n], - ); - const result = await manager.getAllEventPointersForRange("test", range, [ - artifact0, - ]); - expect(result.length).toBe(expectedMatches.length); - const sortedResultIds = result.map((e) => e.id.valueOf()); - const sortedExpectedIds = expectedMatches.map((e) => e.id.valueOf()); - sortedResultIds.sort(); - sortedExpectedIds.sort(); - expect(sortedResultIds).toEqual(sortedExpectedIds); - }); - } - - itWithEventPointers( - "should match exactly with existing pointer", - rangeFromISO("2023-01-01T00:00:00Z", "2023-02-01T00:00:00Z"), - [0], - ); - - itWithEventPointers( - "should match existing pointer that starts before range starts and ends before the range ends", - rangeFromISO("2023-01-05T00:00:00Z", "2023-02-05T00:00:00Z"), - [0], - ); - - itWithEventPointers( - "should match existing pointer that starts after the range starts and ends after the range ends", - rangeFromISO("2023-01-05T00:00:00Z", "2023-02-05T00:00:00Z"), - [0], - ); - - itWithEventPointers( - "should match with larger pointer", - rangeFromISO("2023-01-02T00:00:00Z", "2023-01-10T00:00:00Z"), - [0], - ); - - itWithEventPointers( - "should match with a smaller pointer", - rangeFromISO("2022-12-31T00:00:00Z", "2023-02-02T00:00:00Z"), - [0], - ); - - itWithEventPointers( - "should match with multiple pointers", - rangeFromISO("2022-01-15T00:00:00Z", "2023-03-02T00:00:00Z"), - [0, 1], - ); - - itWithEventPointers( - "should not match with a pointer - range is before any pointers", - rangeFromISO("2022-12-31T00:00:00Z", "2023-01-01T00:00:00Z"), - [], - ); - - it("should merge pointers together", async () => { - const range = rangeFromISO("2022-01-15T00:00:00Z", "2023-03-02T00:00:00Z"); - await manager.commitArtifactForRange("test", range, artifact0); - - // Check that there's only a single pointer left - const result = await manager.getAllEventPointersForRange("test", range, [ - artifact0, - ]); - expect(result.length).toBe(1); - }); - - it("should merge pointers together if the existing is larger", async () => { - const range = rangeFromISO("2022-01-10T00:00:00Z", "2023-01-20T00:00:00Z"); - await manager.commitArtifactForRange("test", range, artifact0); - - const totalRange = rangeFromISO( - "2023-01-01T00:00:00Z", - "2023-02-01T00:00:00Z", - ); - // Check that there's only a single pointer left - const result = await manager.getAllEventPointersForRange( - "test", - totalRange, - [artifact0], - ); - expect(result.length).toBe(1); - }); - - it("should merge pointers together when they do not intersect", async () => { - const range = rangeFromISO("2023-04-01T00:00:00Z", "2023-05-01T00:00:00Z"); - await manager.commitArtifactForRange("test", range, artifact0); - - // Check that there's only a single pointer left - const totalRange = rangeFromISO( - "2023-02-02T00:00:00Z", - "2023-05-01T00:00:00Z", - ); - const result = await manager.getAllEventPointersForRange( - "test", - totalRange, - [artifact0], - ); - expect(result.length).toBe(1); - expect( - DateTime.fromJSDate(result[0].startDate) - .toUTC() - .toISO({ suppressMilliseconds: true }), - ).toEqual("2023-03-01T00:00:00Z"); - }); -}); diff --git a/indexer/src/scheduler/pointers.ts b/indexer/src/scheduler/pointers.ts deleted file mode 100644 index 066ece802..000000000 --- a/indexer/src/scheduler/pointers.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { Artifact, EventPointer } from "../db/orm-entities.js"; -import { Range, findMissingRanges, rangeFromDates } from "../utils/ranges.js"; -import { asyncBatch } from "../utils/array.js"; -import _ from "lodash"; -import { EventPointerRepository } from "../db/events.js"; -import { - DataSource, - In, - LessThan, - LessThanOrEqual, - MoreThanOrEqual, -} from "typeorm"; -import { logger } from "../utils/logger.js"; - -type IEventPointerRepository = typeof EventPointerRepository; - -export interface EventPointerManagerOptions { - batchSize: number; -} - -export const DefaultEventPointerManagerOptions = { - batchSize: 5000, -}; - -export interface IEventPointerManager { - getAllEventPointersForRange( - collectorName: string, - range: Range, - artifacts: Artifact[], - ): Promise; - commitArtifactForRange( - collectorName: string, - range: Range, - artifact: Artifact, - ): Promise; - missingArtifactsForRange( - collectorName: string, - range: Range, - artifacts: Artifact[], - ): Promise; -} - -// Event pointer management -export class EventPointerManager implements IEventPointerManager { - private eventPointerRepository: IEventPointerRepository; - private options: EventPointerManagerOptions; - private dataSource: DataSource; - - constructor( - dataSource: DataSource, - eventPointerRepository: IEventPointerRepository, - options?: EventPointerManagerOptions, - ) { - this.eventPointerRepository = eventPointerRepository; - this.dataSource = dataSource; - this.options = _.merge(DefaultEventPointerManagerOptions, options); - } - - // Find all matching event pointers for the given artifacts and the collector - // name - async getAllEventPointersForRange( - collector: string, - range: Range, - artifacts: Artifact[], - inclusive: boolean = false, - ): Promise { - const startDateWhere = inclusive - ? LessThanOrEqual(range.endDate.toJSDate()) - : LessThan(range.endDate.toJSDate()); - const endDateWhere = MoreThanOrEqual(range.startDate.toJSDate()); - const batches = await asyncBatch( - artifacts, - this.options.batchSize, - async (batch) => { - return this.eventPointerRepository.find({ - // where (range.startDate <= startDate < range.endDate) || (range.startDate < endDate <= range.endDate) - relations: { - artifact: true, - }, - where: { - collector: collector, - artifact: { - id: In(batch.map((a) => a.id)), - }, - startDate: startDateWhere, - endDate: endDateWhere, - }, - }); - }, - ); - return batches.flat(1); - } - - async commitArtifactForRange( - collector: string, - range: Range, - artifact: Artifact, - ) { - logger.debug(`committing this artifact[${artifact.id}]`); - - // Find any old event pointer that's connectable to this one if it exists. Update it. - const intersectingPointers = await this.getAllEventPointersForRange( - collector, - range, - [artifact], - false, - ); - - const rangeStart = range.startDate.toJSDate(); - const rangeEnd = range.endDate.toJSDate(); - - if (intersectingPointers.length === 0) { - // Create a new pointer - const pointer = this.eventPointerRepository.create({ - startDate: rangeStart, - endDate: rangeEnd, - collector: collector, - artifact: { id: artifact.id }, - version: 0, - }); - await this.eventPointerRepository.insert(pointer); - return; - } else { - // Order all of the intersecting pointers by start date and also by end date - // Start date ascending - const byStartDate = _.clone(intersectingPointers).sort( - (a, b) => a.startDate.getTime() - b.startDate.getTime(), - ); - - // End date descending - const byEndDate = _.clone(intersectingPointers).sort( - (a, b) => b.endDate.getTime() - a.endDate.getTime(), - ); - - const startDate = - byStartDate[0].startDate.getTime() < rangeStart.getTime() - ? byStartDate[0].startDate - : rangeStart; - const endDate = - byEndDate[0].endDate.getTime() > rangeEnd.getTime() - ? byEndDate[0].endDate - : rangeEnd; - - // Merge all of the pointers (arbitrarily choose the first and delete the others) - return this.dataSource.transaction(async (manager) => { - const repository = manager.withRepository(this.eventPointerRepository); - - await repository.update( - { - artifact: { - id: artifact.id, - }, - id: byStartDate[0].id, - version: byStartDate[0].version, - }, - { - artifact: { - id: artifact.id, - }, - startDate: startDate, - endDate: endDate, - version: byStartDate[0].version + 1, - }, - ); - - await Promise.all( - byStartDate.slice(1).map((p) => { - return repository.delete({ - id: p.id, - version: p.version, - }); - }), - ); - }); - } - } - - async missingArtifactsForRange( - collectorName: string, - range: Range, - artifacts: Artifact[], - ): Promise { - const eventPtrs = await this.getAllEventPointersForRange( - collectorName, - range, - artifacts, - ); - const existingMap = eventPtrs.reduce>((a, c) => { - const pointers = a[c.artifact.id] || []; - pointers.push(rangeFromDates(c.startDate, c.endDate)); - a[c.artifact.id] = pointers; - return a; - }, {}); - return artifacts.filter((a) => { - const ranges = existingMap[a.id]; - // If there're no ranges then this is missing events - if (!ranges) { - return true; - } - return ( - findMissingRanges(range.startDate, range.endDate, ranges).length > 0 - ); - }); - } -} diff --git a/indexer/src/scheduler/scheduler.ts b/indexer/src/scheduler/scheduler.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/indexer/src/scheduler/types.ts b/indexer/src/scheduler/types.ts deleted file mode 100644 index 5fd418b26..000000000 --- a/indexer/src/scheduler/types.ts +++ /dev/null @@ -1,1180 +0,0 @@ -import { - Artifact, - ArtifactNamespace, - ArtifactType, - EventType, - Job, - JobExecution, - JobExecutionStatus, -} from "../db/orm-entities.js"; -import { - ICommitResult, - IEventGroupRecorder, - IEventRecorder, - IEventRecorderClient, - IEventTypeStrategy, - RecordHandle, - RecordResponse, - RecorderFactory, -} from "../recorder/types.js"; -import { Range, rangeSplit, rangeToString } from "../utils/ranges.js"; -import { IEventPointerManager } from "./pointers.js"; -import { TimeSeriesCacheWrapper } from "../cacher/time-series.js"; -import { logger } from "../utils/logger.js"; -import { - JobAlreadyQueued, - JobExecutionRepository, - JobsRepository, -} from "../db/jobs.js"; -import { DateTime, Duration, DurationLike } from "luxon"; -import _ from "lodash"; -import { INDEXER_SPAWN } from "../config.js"; -import { writeFile, readFile, unlink } from "fs/promises"; -import path from "path"; -import { fileExists } from "../utils/files.js"; -import { mkdirp } from "mkdirp"; -import type { Brand } from "utility-types"; -import { GenericError } from "../common/errors.js"; -import { UniqueArray, asyncBatchFlattened } from "../utils/array.js"; -import { EventEmitter } from "node:events"; -import { AsyncResults, PossiblyError } from "../utils/async-results.js"; -import { assert } from "../utils/common.js"; - -export type IArtifactGroup = { - name(): Promise; - meta(): Promise; - artifacts(): Promise; - - createMissingGroup(missing: Artifact[]): Promise>; -}; - -export type ErrorsList = PossiblyError[]; - -export class ArtifactCommittmentError extends GenericError {} - -export interface IArtifactCommitterProducer { - withResults(results: AsyncResults): void; - withHandles(handles: RecordHandle[]): void; - withNoChanges(): void; -} - -export interface IArtifactCommitter extends IArtifactCommitterProducer { - handles(): RecordHandle[]; - results(): AsyncResults; - isComplete(): boolean; -} - -class ArtifactCommitter implements IArtifactCommitter { - private _handles: RecordHandle[]; - private _results: AsyncResults; - private artifact: Artifact; - private closed: boolean; - - static setup(artifact: Artifact) { - return new ArtifactCommitter(artifact); - } - - constructor(artifact: Artifact) { - this.artifact = artifact; - this._handles = []; - this.closed = false; - this._results = { - success: [], - errors: [], - }; - } - - withHandles(handles: RecordHandle[]): void { - if (this.closed) { - throw new ArtifactCommittmentError( - `committment results attempting to add unresolved handles. Artifact[id=${this.artifact.id}] committer was already resolved.`, - ); - } - handles.forEach((h) => { - this._handles.push(h); - }); - } - - withResults(results: AsyncResults): void { - if (!this.closed) { - this._results = results; - this.closed = true; - } else { - throw new ArtifactCommittmentError( - `committment results attempting override. Artifact[id=${this.artifact.id}] committer was already resolved.`, - ); - } - } - - withNoChanges(): void { - return this.withResults({ - success: ["no changes"], - errors: [], - }); - } - - isComplete(): boolean { - return this.closed; - } - - handles(): RecordHandle[] { - return this._handles; - } - - results(): AsyncResults { - return this._results; - } -} - -export interface IArtifactGroupCommitmentProducer { - commit(artifact: Artifact): IArtifactCommitterProducer; - commitGroup(group: IEventGroupRecorder): void; - failAll(err: PossiblyError): void; -} - -export interface IArtifactGroupCommitmentConsumer { - complete(result: ICommitResult): Promise; - - addListener(listener: "error", cb: (err: unknown) => void): EventEmitter; - removeListener(listener: "error", cb: (err: unknown) => void): EventEmitter; -} - -export type ArtifactRecordingStatus = { - recordings: RecordResponse[]; - errors: PossiblyError[]; -}; - -export type CollectorErrorsOptions = { - unsortedErrors?: ErrorsList; - errorsBySourceId?: Record; -}; - -export class CollectorErrors extends GenericError { - constructor(message: string, options?: CollectorErrorsOptions) { - const details = { - ...options, - }; - super(message, details); - } -} - -export class CollectorRegistrationError extends GenericError {} -export class CollectorDoesNotExistError extends GenericError {} - -type LockData = { - execution: { - id: number; - }; - group: string; -}; - -export type CommitArtifactCallback = ( - artifact: Artifact | Artifact[], -) => Promise; - -/** - * The interface for the scheduler that manages all of the schedules for jobs - */ -export interface IEventCollector { - // Yield arrays of artifacts that this collector monitors. This is an async - // generator so that artifacts can be yielded in groups if needed. - groupedArtifacts(): AsyncGenerator>; - - allArtifacts(): Promise; - - collect( - group: IArtifactGroup, - range: Range, - committer: IArtifactGroupCommitmentProducer, - ): Promise; -} - -export interface IPeriodicCollector { - collect(): Promise; -} - -export interface Errors { - errors: unknown[]; -} - -export type CollectResponse = Errors | void; - -/** - * Rudimentary service locator that uses names to register services. This isn't - * ideal but will work for now to ensure we can do dependency injection. - */ -export interface IConfig { - get(name: string): T; -} - -export interface EventTypeStrategyRegistration { - type: EventType; - strategy: IEventTypeStrategy; -} - -export type Schedule = "monthly" | "weekly" | "daily" | "hourly"; -export type ExecutionMode = "all-at-once" | "progressive"; - -// Collector Types. Currently there are only 2. -// - "event" - Collects events. The execution of which will be controlled by -// `EventPointer` -// - "periodic" - This means the collector is really just a simple job that will -// periodically execute. -export type CollectorType = "event" | "periodic"; - -interface CollectorRegistration { - name: string; - - description: string; - - group?: string; - - schedule: Schedule; -} - -export interface EventCollectorRegistration extends CollectorRegistration { - create( - config: IConfig, - recorder: IEventRecorderClient, - cache: TimeSeriesCacheWrapper, - ): Promise; - - dataSetIncludesNow?: boolean; - - artifactScope: ArtifactNamespace[]; - - artifactTypeScope: ArtifactType[]; - - backfillInterval?: DurationLike; -} - -export interface PeriodicCollectorRegistration extends CollectorRegistration { - create( - config: IConfig, - cache: TimeSeriesCacheWrapper, - ): Promise; -} - -export class Config implements IConfig { - private storage: Record; - - constructor() { - this.storage = {}; - } - - get(name: string): T { - const retVal = this.storage[name]; - if (retVal === undefined || retVal === null) { - throw new Error(`config item ${name} does not exist`); - } - return retVal as T; - } -} - -export interface IExecutionSummary { - group?: string; - errors: PossiblyError[]; - artifactSummaries: ArtifactCommitmentSummary[]; - - hasErrors(): boolean; - successfulArtifacts(): number; - failedArtifacts(): number; - errorCount(): number; -} - -export class ExecutionSummary implements IExecutionSummary { - group?: string; - errors: PossiblyError[]; - artifactSummaries: ArtifactCommitmentSummary[]; - - static empty() { - return new ExecutionSummary(); - } - - constructor() { - this.errors = []; - this.artifactSummaries = []; - } - - failedArtifacts(): number { - return this.artifactSummaries.reduce((a, c) => { - if (c.results.errors.length > 0) { - return a + 1; - } - return a; - }, 0); - } - - successfulArtifacts(): number { - return this.artifactSummaries.length - this.failedArtifacts(); - } - - errorCount(): number { - const artifactErrors = this.artifactSummaries.reduce((a, c) => { - return a + c.results.errors.length; - }, 0); - return artifactErrors + this.errors.length; - } - - hasErrors(): boolean { - if (this.errors.length > 0) { - return true; - } - return this.failedArtifacts() > 0; - } -} - -export interface SchedulerExecuteCollectorOptions { - range?: Range; - mode?: ExecutionMode; - reindex?: boolean; -} - -/** - * Main interface to the indexer. - */ -export interface IScheduler { - registerEventCollector(reg: EventCollectorRegistration): void; - - registerPeriodicCollector(reg: PeriodicCollectorRegistration): void; - - /** - * Should process all register collectors and schedule them - */ - queueAll(baseTime?: DateTime): Promise; - - queueEventJob( - collector: string, - baseTime: DateTime, - range: Range, - ): Promise; - - queuePeriodicJob(collector: string, baseTime: DateTime): Promise; - - queueBackfill( - collectorName: string, - startTime: DateTime, - backfillIntervalDays: number, - ): Promise; - - runWorker(group: string, resumeWithLock: boolean): Promise; - - executeCollector( - collectorName: string, - options?: SchedulerExecuteCollectorOptions, - ): Promise; -} - -export type ArtifactCommitmentSummary = { - results: AsyncResults; - artifact: Artifact; -}; - -export class ArtifactRecordsCommitmentWrapper - implements IArtifactGroupCommitmentProducer, IArtifactGroupCommitmentConsumer -{ - private collectorName: string; - private range: Range; - private promises: Promise[]; - private committers: Record; - private artifacts: Artifact[]; - private emitter: EventEmitter; - private eventPointerManager: IEventPointerManager; - private duplicatesTracker: Record; - - static setup( - collectorName: string, - range: Range, - eventPointerManager: IEventPointerManager, - artifacts: Artifact[], - duplicatesTracker: Record, - ) { - const wrapper = new ArtifactRecordsCommitmentWrapper( - collectorName, - range, - eventPointerManager, - artifacts, - duplicatesTracker, - ); - return wrapper; - } - - private constructor( - collectorName: string, - range: Range, - eventPointerManager: IEventPointerManager, - artifacts: Artifact[], - duplicatesTracker: Record, - ) { - this.collectorName = collectorName; - this.range = range; - this.eventPointerManager = eventPointerManager; - this.artifacts = artifacts; - this.emitter = new EventEmitter(); - this.promises = []; - this.duplicatesTracker = duplicatesTracker; - this.committers = {}; - } - - commit(artifact: Artifact): IArtifactCommitterProducer { - return this.committerForArtifact(artifact); - } - - private committerForArtifact(artifact: Artifact) { - let committer = this.committers[artifact.id.valueOf()]; - if (!committer) { - committer = ArtifactCommitter.setup(artifact); - this.committers[artifact.id.valueOf()] = committer; - } - return committer; - } - - commitGroup(group: IEventGroupRecorder): void { - // Listen for errors in the group - //const groupedHandles = group.groupedHandles() - - // Start waiting for all the artifacts asynchronously - this.artifacts.forEach((artifact) => { - const handles = group.handlesForGroup(artifact); - if (handles.length === 0) { - this.commit(artifact).withNoChanges(); - } else { - this.commit(artifact).withHandles(handles); - } - }); - } - - async complete(commit: ICommitResult): Promise { - return await asyncBatchFlattened(this.artifacts, 1000, async (batch) => { - return await Promise.all( - batch.map(async (artifact) => { - const committer = this.committers[artifact.id]; - const results = committer.isComplete() - ? committer.results() - : commit.collectResultsForHandles(committer.handles()); - if (results.errors.length === 0) { - try { - await this.eventPointerManager.commitArtifactForRange( - this.collectorName, - this.range, - artifact, - ); - logger.info( - `successfully committed Artifact[name=${ - artifact.name - }, namespace=${artifact.namespace}] for ${rangeToString( - this.range, - )}`, - ); - } catch (err) { - return { - artifact: artifact, - results: { - success: [], - errors: [err], - }, - }; - } - } else { - logger.error( - `cannot commit Artifact[name=${artifact.name}, namespace=${artifact.namespace}]. has ${results.errors.length} error(s)`, - ); - } - return { - artifact: artifact, - results: results, - }; - }), - ); - }); - } - - failAll(err: PossiblyError): void { - this.artifacts.forEach((artifact) => { - const committer = this.committerForArtifact(artifact); - if (committer.isComplete()) { - return; - } - committer.withResults({ - errors: [err], - success: [], - }); - }); - } - - successAll(results: AsyncResults): void { - this.artifacts.forEach((artifact) => { - this.commit(artifact).withResults(results); - }); - } - - addListener(listener: "error", cb: (err: unknown) => void): EventEmitter { - return this.emitter.addListener(listener, cb); - } - - removeListener(listener: "error", cb: (err: unknown) => void): EventEmitter { - return this.emitter.removeListener(listener, cb); - } -} - -/** - * Spawns workers (this can be through any means) - * - * This is used by the scheduler to begin spawning workers. - */ -export interface WorkerSpawner { - spawn(group?: string): Promise; -} - -export class BaseScheduler implements IScheduler { - private config: IConfig; - private eventCollectors: Record; - private periodicCollectors: Record; - private eventPointerManager: IEventPointerManager; - private recorderFactory: RecorderFactory; - private cache: TimeSeriesCacheWrapper; - private spawner: WorkerSpawner; - private jobsRepository: typeof JobsRepository; - private jobsExecutionRepository: typeof JobExecutionRepository; - private runDir: string; - private eventTypes: EventTypeStrategyRegistration[]; - - constructor( - runDir: string, - recorderFactory: RecorderFactory, - config: IConfig, - eventPointerManager: IEventPointerManager, - cache: TimeSeriesCacheWrapper, - spawner: WorkerSpawner, - jobsRepository: typeof JobsRepository, - jobsExecutionRepository: typeof JobExecutionRepository, - ) { - this.runDir = runDir; - this.config = config; - this.eventCollectors = {}; - this.periodicCollectors = {}; - this.eventTypes = []; - this.cache = cache; - this.eventPointerManager = eventPointerManager; - this.spawner = spawner; - this.jobsRepository = jobsRepository; - this.jobsExecutionRepository = jobsExecutionRepository; - this.recorderFactory = recorderFactory; - } - - registerEventCollector(reg: EventCollectorRegistration) { - this.assertUniqueCollectorName(reg.name); - this.eventCollectors[reg.name] = reg; - } - - registerPeriodicCollector(reg: PeriodicCollectorRegistration): void { - this.assertUniqueCollectorName(reg.name); - this.periodicCollectors[reg.name] = reg; - } - - private assertUniqueCollectorName(name: string) { - if ( - this.eventCollectors[name] !== undefined || - this.periodicCollectors[name] - ) { - throw new CollectorRegistrationError( - `collector ${name} already registered. names must be unique`, - ); - } - } - - async newRecorder(): Promise { - const recorder = await this.recorderFactory(); - await recorder.setup(); - return recorder; - } - - async queueBackfill( - collectorName: string, - startTime: DateTime, - backfillIntervalDays: number, - ): Promise { - // Only event collectors can do backfill. - const collector = this.eventCollectors[collectorName]; - - // Allow a year to be the maximum default interval for now - let backfillInterval: DurationLike = collector.backfillInterval - ? collector.backfillInterval - : { days: 365 }; - - if ( - backfillIntervalDays !== 0 && - !( - Duration.fromDurationLike({ days: backfillIntervalDays }) > - Duration.fromDurationLike(backfillInterval) - ) - ) { - backfillInterval = { days: backfillIntervalDays }; - } - - let currentStartTime = startTime; - let currentEndTime = currentStartTime.plus(backfillInterval); - const baseTime = DateTime.now().toUTC().startOf("hour"); - - const untilTimes: Record = { - hourly: baseTime.minus({ hour: 1 }), - weekly: baseTime.minus({ days: 2 }), - monthly: baseTime.startOf("month"), - daily: baseTime.startOf("day"), - }; - const until = untilTimes[collector.schedule]; - - while (currentStartTime < until) { - if (currentEndTime > until) { - currentEndTime = until; - } - if (currentEndTime.toMillis() === currentStartTime.toMillis()) { - break; - } - await this.queueEventJob( - collectorName, - currentStartTime, - { startDate: currentStartTime, endDate: currentEndTime }, - true, - ); - - currentStartTime = currentStartTime.plus(backfillInterval); - currentEndTime = currentEndTime.plus(backfillInterval); - } - } - - async queueAll(baseTime?: DateTime): Promise { - // Get the current time. Normalized for hour, month, day, week - baseTime = baseTime - ? baseTime.startOf("hour") - : DateTime.now().toUTC().startOf("hour"); - logger.debug(`queuing jobs for ${baseTime.toISO()}`); - - const scheduleMatches: Schedule[] = ["hourly"]; - - // The ranges have specific arbitrary offsets to hopefully (and cheaply) - // ensure that data collection doesn't miss if a data source is slow. - const scheduleRanges: Record = { - hourly: { - startDate: baseTime.minus({ hour: 2 }), - endDate: baseTime.minus({ hour: 1 }), - }, - weekly: { - startDate: baseTime.minus({ days: 9 }), - endDate: baseTime.minus({ days: 2 }), - }, - monthly: { - startDate: baseTime.minus({ days: 3 }).startOf("month"), - endDate: baseTime.startOf("month"), - }, - daily: { - startDate: baseTime.minus({ hours: 3 }).startOf("day"), - endDate: baseTime.startOf("day"), - }, - }; - - // Find matching collector times. Offsets are intentionally set. - const weeklyMatchDateTime = baseTime.startOf("week"); - logger.debug(`Weekly match is ${weeklyMatchDateTime.toISO()}`); - if (weeklyMatchDateTime.equals(baseTime)) { - scheduleMatches.push("weekly"); - } - - const dailyMatchDateTime = baseTime.startOf("day").plus({ hours: 2 }); - logger.debug(`Daily match is ${dailyMatchDateTime.toISO()}`); - if (dailyMatchDateTime.equals(baseTime)) { - scheduleMatches.push("daily"); - } - - const monthlyMatchDateTime = baseTime.startOf("month").plus({ days: 2 }); - logger.debug(`Monthly match is ${monthlyMatchDateTime.toISO()}`); - if (monthlyMatchDateTime.equals(baseTime)) { - scheduleMatches.push("monthly"); - } - - for (const name in this.eventCollectors) { - const collector = this.eventCollectors[name]; - if (scheduleMatches.indexOf(collector.schedule) !== -1) { - // attempt to schedule this job - await this.queueEventJob( - name, - baseTime, - scheduleRanges[collector.schedule], - ); - } - } - - for (const name in this.periodicCollectors) { - const collector = this.periodicCollectors[name]; - if (scheduleMatches.indexOf(collector.schedule) !== -1) { - // attempt to schedule this job. For now we can use the same input as - await this.queuePeriodicJob(name, baseTime); - } - } - } - - async queueEventJob( - collectorName: string, - baseTime: DateTime, - range: Range, - backfill: boolean = false, - ) { - const options = { - startDate: range.startDate.toISO(), - endDate: range.endDate.toISO(), - }; - return this.queueJob(collectorName, baseTime, backfill, options); - } - - async queuePeriodicJob( - collectorName: string, - baseTime: DateTime, - ): Promise { - return this.queueJob(collectorName, baseTime, false); - } - - private getCollectorTypeByName(name: string): CollectorType { - if (this.eventCollectors[name] !== undefined) { - return "event"; - } - if (this.periodicCollectors[name] !== undefined) { - return "periodic"; - } - throw new CollectorDoesNotExistError(`collector ${name} does not exist.`); - } - - private async queueJob( - collectorName: string, - baseTime: DateTime, - backfill: boolean, - options?: Record, - ) { - const collector = this.eventCollectors[collectorName]; - try { - await this.jobsRepository.queueJob( - collector.name, - collector.group || null, - baseTime, - backfill, - options, - ); - } catch (err) { - if (err instanceof JobAlreadyQueued) { - logger.info( - `job for ${collector.name} already queued at ${baseTime.toISO()}`, - ); - return; - } - throw err; - } - } - - async runWorker( - group: string, - resumeWithLock: boolean, - ): Promise { - let job: Job; - let execution: JobExecution | undefined; - - // Execute from the jobs queue. - if (resumeWithLock && (await this.lockExists())) { - logger.info(`resuming with job lock at ${this.lockJsonPath()}`); - - const lock = await this.loadLock(); - execution = await this.jobsExecutionRepository.findOneOrFail({ - relations: { - job: true, - }, - where: { - id: lock.execution.id as Brand, - }, - }); - job = execution.job; - } else { - const groups = await this.jobsRepository.availableJobGroups(); - - const jobsToChoose = groups.reduce((jobs, grouping) => { - if (grouping.name === "none") { - if (group === "all" || group === "none") { - jobs.push(...grouping.jobs); - return jobs; - } - } else { - if (grouping.name === group) { - jobs.push(...grouping.jobs); - return jobs; - } - } - return jobs; - }, []); - - if (jobsToChoose.length === 0) { - logger.debug("no jobs available for the given group"); - return ExecutionSummary.empty(); - } - - job = _.sample(jobsToChoose)!; - execution = await this.jobsExecutionRepository.createExecutionForJob(job); - - if (!execution) { - throw new Error("could not establish a lock"); - } - } - if (!execution) { - throw new Error("unexpected error. execution not set"); - } - - const collectorType = this.getCollectorTypeByName(job.collector); - - // Write the data for the lock to disk. So that this can be used to kill any - // prematurely killed job. - await this.ensureRunDir(); - const lockJsonPath = this.lockJsonPath(); - await writeFile( - lockJsonPath, - JSON.stringify({ - execution: { - id: execution.id, - }, - group: group, - }), - { encoding: "utf-8" }, - ); - - const execSummary = - collectorType === "event" - ? await this.runWorkerForEventCollector(job) - : await this.runWorkerForPeriodicCollector(job); - - let execStatus = JobExecutionStatus.FAILED; - if (!execSummary.hasErrors()) { - execStatus = JobExecutionStatus.COMPLETE; - } - await this.jobsExecutionRepository.updateExecutionStatus( - execution, - execStatus, - ); - - // Check the available jobs after the run and spawn a worker - logger.info("releasing lock for current worker"); - const afterGroups = await this.jobsRepository.availableJobGroups(); - if (afterGroups.length) { - // Only spawn a single worker with the same group - if (INDEXER_SPAWN) { - await this.spawner.spawn(group); - } else { - logger.debug(`No spawn allowed. would have spawned.`); - } - } - - try { - logger.info("deleting jobs lock file"); - await unlink(lockJsonPath); - } catch (e) { - logger.error("error occurred attempting to delete lock file"); - } - return execSummary; - } - - private async runWorkerForEventCollector( - job: Job, - ): Promise { - const options = job.options as { startDate: string; endDate: string }; - const startDate = DateTime.fromISO(options.startDate); - const endDate = DateTime.fromISO(options.endDate); - - if (!(startDate.isValid && endDate.isValid)) { - throw new Error("irrecoverable error. job description is bad."); - } - - return await this.executeCollector(job.collector, { - range: { - startDate: startDate, - endDate: endDate, - }, - mode: "all-at-once", - }); - } - - private async runWorkerForPeriodicCollector( - job: Job, - ): Promise { - return await this.executeCollector(job.collector); - } - - private lockJsonPath() { - return path.join(this.runDir, "lock.json"); - } - - private async ensureRunDir() { - return mkdirp(this.runDir); - } - - private async lockExists(): Promise { - return await fileExists(this.lockJsonPath()); - } - - private async loadLock() { - const lockData = await readFile(this.lockJsonPath(), { encoding: "utf-8" }); - return JSON.parse(lockData) as LockData; - } - - async cleanLock() { - // If there's an existing lock file then we need to cancel the current job and consider it failed. - const lockJsonPath = this.lockJsonPath(); - if (!(await fileExists(lockJsonPath))) { - logger.info("no lock found. assuming the job exited cleanly"); - return; - } - const lock = await this.loadLock(); - - await this.jobsExecutionRepository.updateExecutionStatusById( - lock.execution.id, - JobExecutionStatus.FAILED, - ); - await unlink(lockJsonPath); - } - - async executeCollector( - collectorName: string, - options?: SchedulerExecuteCollectorOptions, - ) { - const collectorType = this.getCollectorTypeByName(collectorName); - let executionSummary: IExecutionSummary; - - if (collectorType === "event") { - assert(options?.range !== undefined, "Range must be set"); - assert(options?.mode !== undefined, "Mode must be set"); - executionSummary = await this.executeEventCollector( - collectorName, - options.range, - options.mode, - options.reindex || false, - ); - } else if (collectorType === "periodic") { - executionSummary = await this.executePeriodicCollector(collectorName); - } else { - throw new Error("collector type unknown. cannot execute collector"); - } - - return executionSummary; - } - - async executePeriodicCollector(collectorName: string) { - const reg = this.periodicCollectors[collectorName]; - const collector = await reg.create(this.config, this.cache); - - const summary = ExecutionSummary.empty(); - try { - const res = await collector.collect(); - if (res) { - summary.errors.push(...res.errors); - } - } catch (err) { - summary.errors.push(err); - } - - return summary; - } - - async executeEventCollector( - collectorName: string, - range: Range, - mode: ExecutionMode = "all-at-once", - reindex: boolean = false, - ) { - if (range.startDate >= range.endDate) { - throw new Error(`invalid input range ${rangeToString(range)}`); - } - logger.debug( - `starting ${collectorName} in ${mode} for range ${rangeToString(range)}`, - ); - const reg = this.eventCollectors[collectorName]; - if (mode === "all-at-once") { - return this.executeForRange(reg, range, reindex); - } else { - // Execute range by day - const ranges = rangeSplit(range, "day"); - - const summaries: IExecutionSummary[] = []; - for (const pullRange of ranges) { - const result = await this.executeForRange(reg, pullRange, reindex); - summaries.push(result); - } - const summary = summaries.reduce((acc, curr) => { - acc.errors.push(...curr.errors); - acc.artifactSummaries.push(...curr.artifactSummaries); - return acc; - }, ExecutionSummary.empty()); - return summary; - } - } - - async executeForRange( - collectorReg: EventCollectorRegistration, - range: Range, - reindex: boolean, - ) { - const recorder = await this.newRecorder(); - recorder.setActorScope( - collectorReg.artifactScope, - collectorReg.artifactTypeScope, - ); - if (collectorReg.dataSetIncludesNow) { - recorder.setRange({ - startDate: range.startDate, - endDate: DateTime.now().toUTC(), - }); - } else { - recorder.setRange(range); - } - - const executionSummary = ExecutionSummary.empty(); - - recorder.addListener("error", (err) => { - logger.error("caught error on the recorder"); - logger.error(err); - executionSummary.errors.push(err); - }); - - const collector = await collectorReg.create( - this.config, - recorder, - this.cache, - ); - - const seenIds: Record = {}; - - const expectedArtifacts = new UniqueArray((a: number) => a); - - // Get a full count of the remaining work so we can provide a percentage of completion. - const artifacts = await collector.allArtifacts(); - const allMissing = await this.findMissingArtifactsFromEventPointers( - range, - artifacts, - collectorReg.name, - ); - let totalMissing = allMissing.length; - logger.info( - `--------------------------------------------------------------`, - ); - logger.info("Collection summary:"); - logger.info(` Total items: ${artifacts.length}`); - logger.info(` Missing items: ${allMissing.length}`); - logger.info( - `--------------------------------------------------------------`, - ); - if (reindex) { - logger.info( - `-------------------------REINDEXING---------------------------`, - ); - } - - // Get a list of the monitored artifacts - for await (const group of collector.groupedArtifacts()) { - const artifacts = await group.artifacts(); - artifacts.forEach(({ id }) => expectedArtifacts.push(id)); - - // Determine anything missing from this group - const groupName = await group.name(); - const missing = reindex - ? artifacts - : await this.findMissingArtifactsFromEventPointers( - range, - artifacts, - collectorReg.name, - ); - - const committer = ArtifactRecordsCommitmentWrapper.setup( - collectorReg.name, - range, - this.eventPointerManager, - missing, - seenIds, - ); - - // Listen for errors on the committer - const committerErrorListener = (err: unknown) => { - logger.error("caught error on the committer"); - logger.error(err); - executionSummary.errors.push(err); - }; - committer.addListener("error", committerErrorListener); - - // Nothing missing in this group. Skip - if (missing.length === 0) { - logger.debug(`${groupName}: all artifacts already up to date`); - continue; - } - logger.debug( - `${groupName}: missing ${missing.length} artifacts for the group. ${totalMissing} remaining`, - ); - - // Execute the collection for the missing items - try { - // Start the recorder - await recorder.begin(); - - const response = await collector.collect( - await group.createMissingGroup(missing), - range, - committer, - ); - - // This is jank and needs to be fixed. This can be cleaned up. - const commitResult = await recorder.commit(); - logger.debug(`${groupName}: waiting for artifacts to complete commits`); - - const artifactSummaries = await committer.complete(commitResult); - logger.debug(`completed commits for ${artifactSummaries.length}`); - totalMissing = totalMissing - artifactSummaries.length; - executionSummary.artifactSummaries.push(...artifactSummaries); - - if (response) { - const errorsResponse = response as Errors; - if (errorsResponse.errors) { - executionSummary.errors.push(...errorsResponse.errors); - } - } - } catch (err) { - logger.error("Error encountered. Skipping group", err); - await recorder.rollback(); - committer.failAll(err); - executionSummary.errors.push(err); - continue; - } - committer.removeListener("error", committerErrorListener); - } - - logger.debug("collection recorded but waiting for recorder"); - - try { - await recorder.close(); - } catch (err) { - logger.error("error while waiting for the recorder to complete", err); - executionSummary.errors.push(err); - } - - if (executionSummary.hasErrors()) { - logger.info("completed with errors"); - } else { - // TODO collect errors and return here - logger.info( - `completed collector run successfully for ${expectedArtifacts.length} artifacts.`, - ); - } - return executionSummary; - } - - private findMissingArtifactsFromEventPointers( - range: Range, - artifacts: Artifact[], - collectorName: string, - ): Promise { - return this.eventPointerManager.missingArtifactsForRange( - collectorName, - range, - artifacts, - ); - } -} diff --git a/indexer/src/schema.sql b/indexer/src/schema.sql deleted file mode 100644 index 11b04afa8..000000000 --- a/indexer/src/schema.sql +++ /dev/null @@ -1,36 +0,0 @@ -create table - public.projects ( - id uuid not null default gen_random_uuid (), - created_at timestamp with time zone not null default current_timestamp, - name text not null, - github_org text not null, - description text not null, - constraint projects_pkey primary key (id) - ) tablespace pg_default; - - - create table - public.events ( - id uuid not null default gen_random_uuid (), - created_at timestamp with time zone not null default current_timestamp, - project_id uuid not null, - event_time timestamp with time zone null, - event_type text null, - contributor text null, - amount double precision null, - details jsonb null, - constraint events_pkey primary key (id) - ) tablespace pg_default; - - - create table - public.wallets ( - id uuid not null default gen_random_uuid (), - created_at timestamp with time zone not null default current_timestamp, - address text null, - project_id uuid not null, - type text null, - ens text null, - constraint wallets_pkey primary key (id), - constraint wallets_project_id_fkey foreign key (project_id) references projects (id) on update cascade on delete restrict - ) tablespace pg_default; \ No newline at end of file diff --git a/indexer/src/scripts/README.md b/indexer/src/scripts/README.md deleted file mode 100644 index 5d7e2a5ea..000000000 --- a/indexer/src/scripts/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Scripts - -One off scripts that might be useful later. diff --git a/indexer/src/scripts/artifact-management.ts b/indexer/src/scripts/artifact-management.ts deleted file mode 100644 index b42529ed7..000000000 --- a/indexer/src/scripts/artifact-management.ts +++ /dev/null @@ -1,333 +0,0 @@ -/* eslint no-restricted-properties: 0 */ -// Currently just a test file but if it gets committed that wasn't intentional -// but is also just fine -import { handleError } from "../utils/error.js"; -import { Argv } from "yargs"; -import { - ArtifactType, - Project, - Event, - EventPointer, - Artifact, -} from "../index.js"; -import _ from "lodash"; -import { AppDataSource } from "../db/data-source.js"; -import { In } from "typeorm"; -import { ArtifactRepository } from "../db/artifacts.js"; -import { Brand } from "utility-types"; -import inquirer from "inquirer"; -import { ProjectRepository } from "../db/project.js"; -import { CollectionRepository } from "../db/collection.js"; - -export type ArtifactsCommonArgs = { - type?: ArtifactType; -}; - -export function artifactsCommandGroup(topYargs: Argv) { - topYargs.command( - "fix-casing", - "Fixes any duplicate artifact naming errors", - (yargs) => { - yargs.option("type", { - type: "array", - description: "restrict to specific types", - }); - }, - (args) => handleError(fixArtifactCasing(args)), - ); -} - -export async function fixArtifactCasing( - _args: ArtifactsCommonArgs, -): Promise { - const caseInsensitiveTypes: ArtifactType[] = [ - ArtifactType.FACTORY_ADDRESS, - ArtifactType.CONTRACT_ADDRESS, - ArtifactType.EOA_ADDRESS, - ArtifactType.SAFE_ADDRESS, - ArtifactType.GITHUB_ORG, - ArtifactType.GITHUB_USER, - ArtifactType.GIT_EMAIL, - ArtifactType.GIT_REPOSITORY, - ]; - const artifactDupes = - await ArtifactRepository.duplicates(caseInsensitiveTypes); - let artifactsNonCanonical = - await ArtifactRepository.nonCanonical(caseInsensitiveTypes); - const projectsDupes = await ProjectRepository.duplicates(); - const projectsNonCanonical = await ProjectRepository.nonCanonical(); - const collectionsDupes = await CollectionRepository.duplicates(); - const collectionsNonCanonical = await CollectionRepository.nonCanonical(); - console.log(`Found ${collectionsDupes.length} duplicated projects`); - console.log( - `Found ${collectionsNonCanonical.length} collections without proper canonical naming`, - ); - console.log(`Found ${projectsDupes.length} duplicated projects for types:`); - console.log( - `Found ${projectsNonCanonical.length} projects without proper canonical name`, - ); - console.log(`Found ${artifactDupes.length} duplicated artifacts for types:`); - caseInsensitiveTypes.forEach((t) => { - console.log(` - ${t}`); - }); - console.log( - `Found ${artifactsNonCanonical.length} artifacts without proper canonical name`, - ); - - if (projectsNonCanonical.length > 0) { - console.warn( - "There is currently no solution for projects without canonical names", - ); - } - if (collectionsNonCanonical.length > 0) { - console.warn( - "There is currently no solution for collections without canonical names", - ); - } - console.log(artifactsNonCanonical); - - if (artifactDupes.length === 0 && artifactsNonCanonical.length === 0) { - console.log(""); - console.log("Nothing to do"); - console.log(""); - return process.exit(0); - } - - const answer = await inquirer.prompt({ - type: "confirm", - name: "confirm", - message: "Fix these duplicates now?", - }); - if (!answer.confirm) { - console.log(""); - console.log("Not fixing for now."); - console.log(""); - return process.exit(0); - } - - await fixAnyGitEmailsAndNames(); - - // Fix dupes first - if (artifactDupes.length > 0) { - await fixArtifactDuplicates(caseInsensitiveTypes); - artifactsNonCanonical = - await ArtifactRepository.nonCanonical(caseInsensitiveTypes); - } - - if (artifactsNonCanonical.length > 0) { - await fixNonCanonicalArtifacts(caseInsensitiveTypes); - } -} - -async function fixAnyGitEmailsAndNames() { - // Punts on git emails by adding a prefix `unverified:email:` before the name - const r1 = await AppDataSource.query(` - update artifact a - set "name" = format('unverified:git:data:email:%s', a."name") - where a."type" = 'GIT_EMAIL' AND a."name" NOT LIKE 'unverified:git:data:%' - `); - console.log("changed names of GIT_EMAIL (that are not emails)"); - console.log(r1); - - const r2 = await AppDataSource.query(` - update artifact a - set "name" = format('unverified:git:data:name:%s', a."name") - where a."type" = 'GIT_NAME' AND a."name" NOT LIKE 'unverified:git:data:%' - `); - - console.log("changed names of GIT_NAME"); - console.log(r2); -} - -async function fixNonCanonicalArtifacts(types: ArtifactType[]): Promise { - await AppDataSource.createQueryBuilder() - .update(Artifact) - .set({ name: () => 'lower("name")' }) - .where("type IN (:...types)", { types: types }) - //.andWhere('lower("name") NOT IN (:...ignore)', { ignore: ignoreNames }) - .execute(); -} - -async function fixArtifactDuplicates(types: ArtifactType[]): Promise { - const duplicates = await ArtifactRepository.duplicates(types); - - for (const duplicate of duplicates) { - const artifactStr = `Artifact[name=${duplicate.name},ns=${duplicate.namespace},t=${duplicate.type}]`; - console.log("---------------------------"); - console.log(`Fixing ${artifactStr}`); - - // Fix duplicates in a transaction for safety - await AppDataSource.transaction(async (manager) => { - // Choose a canonical version (currently we choose the one with the lowest id) - const ids = duplicate.ids; - ids.sort(); - - // Fix the project associations - const canonicalId = ids[0]; - console.log(`${artifactStr}: canonical id set to ${canonicalId}`); - const nonCanonicalIds = ids.slice(1); - - const projectRepo = manager.getRepository(Project); - - // Find all projects that happen to have one of these (or all of these) - // artifacts - const projects = await projectRepo.find({ - relations: { - artifacts: true, - }, - where: { - artifacts: { - id: In(ids), - }, - }, - }); - console.log( - `${artifactStr}: Found ${projects.length} project(s) with associations`, - ); - - // Fix all the projects - for (const project of projects) { - await projectRepo - .createQueryBuilder() - .relation(Project, "artifacts") - .of(project.id) - .remove(nonCanonicalIds); - - const found = _.findKey(project.artifacts, { id: canonicalId }); - if (!found) { - await projectRepo - .createQueryBuilder() - .relation(Project, "artifacts") - .of(project.id) - .add([ids[0]]); - } - } - - // Move all of the events to the oldest versions of objects (hopefully - // there are no duplicates or this will fail) do queries one by one - const toRes = await manager - .createQueryBuilder() - .update(Event) - .set({ to: { id: canonicalId } }) - .where("toId IN (:...ids)", { ids: nonCanonicalIds }) - .execute(); - console.log( - `${artifactStr}: Updated ${toRes.affected} event(s) using nonCanonicalIds for toId`, - ); - - const forRes = await manager - .createQueryBuilder() - .update(Event) - .set({ from: { id: canonicalId } }) - .where("fromId IN (:...ids)", { ids: nonCanonicalIds }) - .execute(); - console.log( - `${artifactStr}: Updated ${forRes.affected} event(s) using nonCanonicalIds for forId`, - ); - - // Find duplicates in the event pointers. - const epDupesRes = (await manager - .createQueryBuilder() - .select() - .addSelect("ep.collector", "collector") - .addSelect("ep.startDate", "startDate") - .addSelect("ep.endDate", "endDate") - .addSelect('count(ep."id")', "count") - .addSelect('array_agg(ep."id")', "ids") - .addSelect('array_agg(ep."artifactId")', "artifactIds") - .from(EventPointer, "ep") - .where("ep.artifactId IN (:...ids)", { ids: ids }) - .groupBy("1,2,3") - .having('count(ep."id") > 1') - .getRawMany()) as { - collector: string; - startDate: string; - endDate: string; - count: number; - ids: number[]; - artifactIds: number[]; - }[]; - - // Duplicate pointers need to be merged into a single pointer for the current object - // Currently this doesn't do any merging of pointers that intersect. - for (const dupedEp of epDupesRes) { - // If the canonicalId is not in the set. Set that one. - const canonicalIndex = dupedEp.artifactIds.indexOf(canonicalId); - - const epIndexToKeep = canonicalIndex === -1 ? 0 : canonicalIndex; - const epIdsToDelete = dupedEp.ids.filter((d, i) => { - return i != epIndexToKeep; - }); - - // Delete everything that isn't the one we're keeping - const delRes = await manager.getRepository(EventPointer).delete({ - id: In(epIdsToDelete), - }); - console.log( - `${artifactStr}: Deleting ${delRes.affected} duplicate event pointers`, - ); - - // Update the epIdToKeep - const ensureRes = await manager - .createQueryBuilder() - .update(EventPointer) - .set({ artifact: { id: canonicalId } }) - .where("id = :id", { id: dupedEp.ids[epIndexToKeep] }) - .execute(); - if (ensureRes.affected && ensureRes.affected > 0) { - console.log( - `${artifactStr}: updated EventPointer[${dupedEp.ids[epIndexToKeep]}] to point to the canonical artifact`, - ); - } - } - - const epRes = await manager - .createQueryBuilder() - .update(EventPointer) - .set({ artifact: { id: canonicalId } }) - .where("artifactId IN (:...ids)", { ids: nonCanonicalIds }) - .execute(); - console.log(`${artifactStr}: Fixed ${epRes.affected} event pointers`); - - // Rename the artifact if it's not the `lowercase` version of the artifact. - // Ensure there are zero events from anything but the canonical version - const count = await manager - .getRepository(Event) - .createQueryBuilder("event") - .where("event.toId IN (:...toIds)", { toIds: nonCanonicalIds }) - .orWhere("event.fromId IN (:...fromIds)", { fromIds: nonCanonicalIds }) - .getCount(); - if (count !== 0) { - throw new Error("failed to update all events. Failing transaction"); - } - - const artifactRepo = manager.withRepository(ArtifactRepository); - - console.log(`${artifactStr}: Removing nonCanonicalIds`); - // Delete all of the nonCanonical artifacts - const delRes = await artifactRepo.delete({ - id: In(nonCanonicalIds), - }); - if (delRes.affected !== nonCanonicalIds.length) { - throw new Error("deletion did not run as expected"); - } - - // Ensure the name of the canonical version is lowercased - await artifactRepo.update( - { - id: canonicalId as Brand, - }, - { - name: duplicate.name.toLowerCase(), - }, - ); - console.log(`${artifactStr}: changes committed`); - }).catch((err) => { - console.log( - `${artifactStr}: encountered an error. changes not committed`, - ); - console.error(err); - throw err; - }); - } -} diff --git a/indexer/src/scripts/db-utilities.ts b/indexer/src/scripts/db-utilities.ts deleted file mode 100644 index c6fd93ccb..000000000 --- a/indexer/src/scripts/db-utilities.ts +++ /dev/null @@ -1,165 +0,0 @@ -/* eslint no-restricted-properties: 0 */ -import { handleError } from "../utils/error.js"; -import { Argv } from "yargs"; -import { AppDataSource } from "../db/data-source.js"; -import { DateTime } from "luxon"; -import { coerceDateTime, coerceDateTimeOrNow } from "../utils/cli.js"; -import { logger } from "../utils/logger.js"; -import { Recording } from "../db/orm-entities.js"; -import { LessThan } from "typeorm"; -import _ from "lodash"; - -export type UtilitiesRefreshAggregatesArgs = { - startDate: DateTime; - endDate: DateTime; - intervalDays: number; -}; - -export type CleanRecorderTempTableArgs = { - olderThanDate: DateTime | null; -}; - -export function dbUtilitiesCommandGroup(topYargs: Argv) { - topYargs.command( - "refresh-mvs", - "Refreshes materialized views", - (yargs) => { - yargs - .option("start-date", { - type: "string", - describe: "ISO8601 of the start date", - default: "2010-01-01T00:00:00Z", - }) - .coerce("start-date", coerceDateTime) - .option("end-date", { - type: "string", - describe: "ISO8601 of the end date (defaults to now)", - default: "", - }) - .coerce("end-date", coerceDateTimeOrNow) - .option("interval-days", { - type: "number", - describe: "interval", - default: 5000, - }); - }, - (args) => handleError(refreshAggregates(args)), - ); - topYargs.command( - "clean-recorder-temps", - "Cleans the recorder temporary tables", - (yargs) => { - yargs - .option("older-than-date", { - type: "string", - describe: - "ISO8601 of the expiration date to use. defaults to using the stored expiration", - default: "", - }) - .coerce("older-than-date", coerceDateTimeOrNow); - }, - (args) => handleError(cleanRecorderTemps(args)), - ); -} - -export async function cleanRecorderTemps(args: CleanRecorderTempTableArgs) { - const repo = AppDataSource.getRepository(Recording); - const olderThan = args.olderThanDate ? args.olderThanDate : DateTime.now(); - const recordings = await repo.find({ - where: { - expiration: LessThan(olderThan.toJSDate()), - }, - }); - for (const recording of recordings) { - console.log(`Cleaning Recording[${recording.recorderId}]`); - // Find all tables with the recorderId - const recorderId = recording.recorderId; - const recorderIdTableStr = recorderId.replace(/-/g, "_"); - const tablesToDelete = (await AppDataSource.query( - ` - SELECT tablename FROM pg_catalog.pg_tables where schemaname = 'public' AND tablename LIKE $1; - `, - [`%${recorderIdTableStr}%`], - )) as { tablename: string }[]; - if (tablesToDelete.length > 0) { - logger.debug(`deleting tables for Recording[${recorderId}]`); - await AppDataSource.query(` - DROP TABLE ${tablesToDelete.map((a) => a.tablename).join(", ")} - `); - } - logger.debug(`deleting Recording[${recorderId}]`); - await repo.delete(recorderId); - } -} - -export async function refreshAggregates( - args: UtilitiesRefreshAggregatesArgs, -): Promise { - // Query for all of the continous aggregate names. These will be excluded from - // the materialized views list - const rawAggregates = (await AppDataSource.query(` - SELECT view_name - FROM timescaledb_information.continuous_aggregates - WHERE view_schema = 'public' - `)) as { view_name: string }[]; - - const aggregatesList = _.uniq(rawAggregates.map((a) => a.view_name)); - - const entities = AppDataSource.entityMetadatas; - const materialized: string[] = []; - for (const entity of entities) { - if (entity.tableMetadataArgs.type !== "view") { - continue; - } - const name = entity.tableName; - // Don't include views from the continous aggregates. Those are refreshed - // differently - if ( - entity.tableMetadataArgs.materialized && - aggregatesList.indexOf(name) === -1 - ) { - materialized.push(entity.tableName); - } - } - - console.log("Found the following materialized views %j", materialized); - console.log("Found the following continous aggregates %j", aggregatesList); - - const startDate = args.startDate; - const endDate = args.endDate!; - const intervalDays = args.intervalDays; - - for (const mv of materialized) { - logger.info(`Refreshing ${mv}`); - const resp = await AppDataSource.query(` - REFRESH MATERIALIZED VIEW ${mv} - `); - logger.info("Refresh response"); - logger.info(resp); - } - - logger.debug(`refreshing indexes in ${intervalDays} day intervals`); - // Continuous aggregates will start - for (const agg of aggregatesList) { - let currentStart = endDate.minus({ days: intervalDays }); - let currentEnd = endDate; - while (currentEnd >= startDate) { - logger.info( - `Refreshing ${agg} for ${currentStart.toISODate()} to ${currentEnd.toISODate()}`, - ); - const resp = await AppDataSource.query( - ` - CALL refresh_continuous_aggregate($1, $2::timestamptz, $3::timestamptz) - `, - [agg, startDate, endDate], - ); - currentStart = currentStart.minus({ days: intervalDays }); - currentEnd = currentEnd.minus({ days: intervalDays }); - if (currentStart < startDate) { - currentStart = startDate; - } - logger.info("Refresh response"); - logger.info(resp); - } - } -} diff --git a/indexer/src/scripts/manual-csv-import-helper.ts b/indexer/src/scripts/manual-csv-import-helper.ts deleted file mode 100644 index adc51c67b..000000000 --- a/indexer/src/scripts/manual-csv-import-helper.ts +++ /dev/null @@ -1,185 +0,0 @@ -// Currently just a test file but if it gets committed that wasn't intentional -// but is also just fine -import { parse } from "csv"; -import fs from "fs"; -import { DateTime } from "luxon"; -import { TimeSeriesLookupNormalizingUnit } from "../cacher/time-series.js"; -import { handleError } from "../utils/error.js"; -import { Argv } from "yargs"; -import _ from "lodash"; - -export type ImportCsvCommonArgs = { - dateIndex: number; - path: string[]; - normalizingUnit: TimeSeriesLookupNormalizingUnit; - direction: "asc" | "dsc"; -}; - -export function csvCommandGroup(topYargs: Argv) { - topYargs.command( - "download-assistant", - "Download assistant that tells you what to query for the next query", - (yargs) => { - yargs - .option("date-index", { default: 0, type: "number" }) - .option("path", { type: "array" }) - .option("normalizing-unit", { - type: "string", - choices: ["hour", "day", "month"], - default: "day", - }); - }, - (args) => handleError(csvDownloadAssistant(args)), - ); -} - -/** - * Used to backfill data from any csv. Tells you where to query next (if that's - * needed). Also tells you how many rows you're getting. It checks if the dates - * are ordered. If they're not this will error. The data must be ordered and a - * `time` field must be the first field. It will normalize on days by default - * (this means the date it will give for you to continue from will be normalized - * to the day/hour/month _before or on_ the second to last available date). This - * command can be fed multiple paths and it will ensure that these csv files - * maintain a continous set of results. - */ -export async function csvDownloadAssistant( - args: ImportCsvCommonArgs, -): Promise { - let lastScanResult: ScanResult | undefined = undefined; - - if (args.path.length === 0) { - throw new Error("no input"); - } - - let fullyContinuous = true; - - for (const path of args.path) { - const scanResult = await csvDownloadScan( - path, - args.dateIndex, - args.normalizingUnit, - lastScanResult, - ); - if (!scanResult.isContinuous) { - if (!lastScanResult) { - throw new Error("unexpectedly non-continuous from the first csv"); - } - console.log( - `Rows between ${scanResult.path} and ${ - lastScanResult!.path - } are not continuous`, - ); - fullyContinuous = false; - } - lastScanResult = scanResult; - } - - if (!lastScanResult) { - throw new Error("there should be a scan result or an error"); - } - console.log("Scan Result:"); - console.log(` Final checked path: ${lastScanResult.path}`); - console.log(` Are files continuous?: ${fullyContinuous ? "yes" : "no"}`); - console.log( - ` Next point date to query: ${lastScanResult.lastPeriodDateTime - .toUTC() - .toISO()}`, - ); - console.log( - ` Last period rows length: ${lastScanResult.lastPeriodRows.length}`, - ); -} - -type ScanResult = { - path: string; - isContinuous: boolean; - lastPeriodRows: string[][]; - lastPeriodDateTime: DateTime; -}; - -export async function csvDownloadScan( - path: string, - dateIndex: number, - normalizatingUnit: TimeSeriesLookupNormalizingUnit, - previousScanResult?: ScanResult, -): Promise { - let trackingDateTime: DateTime | null = null; - let rows: string[][] = []; - - let isCheckingContinuity = previousScanResult !== undefined; - let isContinuous = true; - - return new Promise((resolve, reject) => { - fs.createReadStream(path) - .pipe(parse({ delimiter: ",", fromLine: 2 })) - .on("data", (row) => { - const rowTime = DateTime.fromSQL(row[dateIndex]); - if (trackingDateTime === null) { - trackingDateTime = rowTime.startOf(normalizatingUnit); - - if (isCheckingContinuity) { - if ( - trackingDateTime.toMillis() !== - previousScanResult!.lastPeriodDateTime.toMillis() - ) { - isContinuous = false; - } - } - } - if ( - trackingDateTime.startOf(normalizatingUnit) < - rowTime.startOf(normalizatingUnit) - ) { - if (isCheckingContinuity) { - isCheckingContinuity = false; - if (!_.isEqual(previousScanResult!.lastPeriodRows, rows)) { - // Check each of the last scan results rows - const lastPeriodRows = previousScanResult!.lastPeriodRows; - if (lastPeriodRows.length < 1) { - console.warn( - `continuity of file is ambiguous between ${ - previousScanResult!.path - } and ${path}`, - ); - isContinuous = false; - } else { - for (let i = 0; i < lastPeriodRows.length; i++) { - if (!_.isEqual(lastPeriodRows[i], rows[i])) { - isContinuous = false; - break; - } - } - } - } - } - rows = []; - trackingDateTime = rowTime; - } - if (trackingDateTime > rowTime) { - return reject( - new Error( - "invalid data it must be date time ordered (ascending at the moment)", - ), - ); - } - - // Keep the current set of rows until we hit the end or we change the - // trackingDateTime - rows.push(row); - }) - .on("end", () => { - if (!trackingDateTime) { - return reject(new Error("there are no rows in this csv")); - } - - resolve({ - path: path, - // If there wasn't a previous file then we're ok - isContinuous: previousScanResult === undefined ? true : isContinuous, - lastPeriodDateTime: trackingDateTime, - lastPeriodRows: rows, - }); - }); - }); -} diff --git a/indexer/src/scripts/manual-dune-tools.ts b/indexer/src/scripts/manual-dune-tools.ts deleted file mode 100644 index 88fe76b47..000000000 --- a/indexer/src/scripts/manual-dune-tools.ts +++ /dev/null @@ -1,475 +0,0 @@ -// Currently just a test file but if it gets committed that wasn't intentional -// but is also just fine -import { parse } from "csv"; -import fs, { WriteStream } from "fs"; -import { DateTime } from "luxon"; -import { TimeSeriesLookupNormalizingUnit } from "../cacher/time-series.js"; -import { handleError } from "../utils/error.js"; -import { Argv } from "yargs"; -import { - Transform, - TransformCallback, - TransformOptions, - Writable, -} from "stream"; -import { - parseDuneContractUsageCSVRow, - DuneRawRow, - transformDuneRawRowToUsageRows, - DailyContractUsageRow, - DailyContractUsageClient, - SafeAggregate, -} from "../collectors/dune/daily-contract-usage/client.js"; -import nodePath from "path"; -import { DuneClient } from "@cowprotocol/ts-dune-client"; -import { DuneCSVUploader } from "../collectors/dune/utils/csv-uploader.js"; -import { DUNE_API_KEY, DUNE_CSV_DIR_PATH } from "../config.js"; -import { ProjectRepository } from "../db/project.js"; -import { Artifact, ArtifactNamespace, ArtifactType } from "../index.js"; -import { UniqueArray } from "../utils/array.js"; -import { In } from "typeorm"; -import { Queue } from "@datastructures-js/queue"; - -export type DuneUploadArgs = object; - -export type DuneSplitUsageArgs = { - path: string[]; - outputDir: string; -}; - -function rowToString(row: DailyContractUsageRow) { - return `${row.date},${row.contractAddress},${row.userAddress || ""},${ - row.safeAddress || "" - },${row.l2GasUsed},${row.l1GasUsed},${row.txCount}\n`; -} - -export class DuneDayExporter extends Writable { - currentDay: DateTime | null; - stream: WriteStream | null; - outputDir: string; - writtenLines: number; - totalLines: number; - - constructor(outputDir: string, opts: TransformOptions) { - super({ ...{ objectMode: true, writableObjectMode: true }, ...opts }); - this.currentDay = null; - this.stream = null; - this.outputDir = outputDir; - this.writtenLines = 0; - this.totalLines = 0; - } - - _write(chunk: any, encoding: BufferEncoding, done: TransformCallback): void { - const row = chunk as DailyContractUsageRow; - const rowTime = DateTime.fromISO(row.date); - if (!this.currentDay) { - this.currentDay = rowTime; - this.startNewStream(this.currentDay); - } - if (this.currentDay.toMillis() !== rowTime.toMillis()) { - if (this.currentDay > rowTime) { - console.log(row); - console.log( - `OUT OF ORDER ${this.currentDay.toISO()} > ${rowTime.toISO()}`, - ); - throw new Error("data is not in chronological order"); - } - - if (this.stream) { - const closingStream = this.stream; - setImmediate(() => { - closingStream.end(() => { - this.currentDay = rowTime; - this.startNewStream(this.currentDay); - this.writeToStream(row); - done(); - }); - }); - } - } else { - if (this.stream) { - const tryWrite = () => { - if (!this.writeToStream(row)) { - return setImmediate(done); - } - done(); - }; - tryWrite(); - } else { - done(); - } - } - } - - writeToStream(row: DailyContractUsageRow) { - this.writtenLines += 1; - if (this.writtenLines % 1000 == 0) { - console.log( - `File[${this.currentDay?.toISODate()}]: ${this.writtenLines}`, - ); - } - return this.stream!.write(rowToString(row)); - } - - startNewStream(date: DateTime) { - const pathToWrite = nodePath.join( - this.outputDir, - `${date.toISODate()}.csv`, - ); - this.writtenLines = 0; - console.log(`Starting a write to file ${pathToWrite}`); - this.stream = fs.createWriteStream(pathToWrite); - } - - _final(done: (error?: Error | null | undefined) => void): void { - console.debug("Closing the day writer"); - if (this.stream) { - const closingStream = this.stream; - setImmediate(() => { - closingStream.end(() => { - done(); - }); - }); - } else { - done(); - } - } -} - -export class DuneSplitRow extends Transform { - queue: { - callback: TransformCallback; - rows: DailyContractUsageRow[]; - }[]; - contractsMap: Record; - - constructor(contractsMap: Record, opts: TransformOptions) { - super({ - ...{ - objectMode: true, - readableObjectMode: true, - writableObjectMode: true, - }, - ...opts, - }); - this.queue = []; - this.contractsMap = contractsMap; - } - - _write( - chunk: any, - _encoding: BufferEncoding, - callback: TransformCallback, - ): void { - // Queue things up - const row = chunk as DuneRawRow; - const rows = transformDuneRawRowToUsageRows(row, this.contractsMap); - this.queue.push({ callback: callback, rows: rows }); - this.pushRows(); - } - - pushRows() { - while (this.queue.length > 0) { - // Get top most queue - const innerQueue = this.queue[0]; - const next = innerQueue.rows.pop(); - - if (!next) { - this.queue.pop(); - return innerQueue.callback(); - } - - if (!this.push(next)) { - return; - } - } - } - - _read(_size: number): void { - this.pushRows(); - } -} - -export class DuneSafeAggregate extends Transform { - incomingDay: DailyContractUsageRow[]; - outgoingRows: Queue; - currentDay: DateTime | null; - safes: Record; - - constructor(opts: TransformOptions) { - super({ - ...{ - objectMode: true, - readableObjectMode: true, - writableObjectMode: true, - }, - ...opts, - }); - this.incomingDay = []; - this.outgoingRows = new Queue(); - this.currentDay = null; - this.safes = {}; - } - - _write( - chunk: any, - encoding: BufferEncoding, - callback: TransformCallback, - ): void { - // Queue things up - const row = chunk as DailyContractUsageRow; - const rowDay = DateTime.fromISO(row.date); - - if (!this.currentDay) { - this.currentDay = rowDay; - } - - if (this.currentDay.toMillis() !== rowDay.toMillis()) { - //console.log('new day triggered'); - if (this.currentDay > rowDay) { - console.log(row); - console.log("this is happening"); - throw new Error("the data being passed is out of order"); - } - for (const r of this.incomingDay) { - this.outgoingRows.enqueue(r); - } - - const safeRows = this.popSafes(); - for (const r of safeRows) { - this.outgoingRows.enqueue(r); - } - this.incomingDay = []; - this.currentDay = rowDay; - } - - // for some reason safes weren't aggregated properly on dune. let's do that here - if (row.safeAddress) { - const address = row.safeAddress.toLowerCase(); - const agg = this.safes[address]; - //console.log(Object.keys(this.safes)); - if (agg) { - agg.add(row); - } else { - this.safes[address] = new SafeAggregate(row); - } - } else { - this.incomingDay.push(row); - } - this.pushRows(); - callback(); - } - - popSafes(): DailyContractUsageRow[] { - const safes: DailyContractUsageRow[] = []; - for (const addr in this.safes) { - safes.push(this.safes[addr].aggregate()); - } - this.safes = {}; - return safes; - } - - pushRows() { - while (!this.outgoingRows.isEmpty()) { - // Get top most queue - const next = this.outgoingRows.dequeue(); - - if (!next) { - return; - } - const row = next; - - if (!this.push(row)) { - return; - } - } - } - - _read(_size: number): void { - this.pushRows(); - } -} - -export function duneCommandGroup(topYargs: Argv) { - topYargs.command( - "split-contract-usage", - "Split the contract usage rows", - (yargs) => { - yargs - .option("path", { type: "array", description: "the paths" }) - .option("output-dir", { type: "string" }); - }, - (args) => handleError(splitContractUsage(args)), - ); - topYargs.command( - "upload-latest-contracts-table", - "A way to manually upload the contracts table", - (_yargs) => {}, - (args) => handleError(uploadLatestContractTable(args)), - ); -} - -export async function uploadLatestContractTable(_args: DuneUploadArgs) { - const dune = new DuneClient(DUNE_API_KEY); - const client = new DailyContractUsageClient( - dune, - new DuneCSVUploader(DUNE_API_KEY), - { - csvDirPath: DUNE_CSV_DIR_PATH, - }, - ); - const contracts = await currentContractsTableArtifacts(); - await client.uploadContractTable( - contracts.map((c) => { - return { - id: c.id, - address: c.name, - }; - }), - ); -} - -export async function currentContractsTableArtifacts() { - const projects = await ProjectRepository.find({ - relations: { - artifacts: true, - }, - where: { - artifacts: { - type: In([ArtifactType.CONTRACT_ADDRESS, ArtifactType.FACTORY_ADDRESS]), - namespace: ArtifactNamespace.OPTIMISM, - }, - }, - }); - const allArtifacts = projects.flatMap((p) => p.artifacts); - - const uniqueArtifacts = new UniqueArray((a: Artifact) => a.id); - allArtifacts.forEach((a) => uniqueArtifacts.push(a)); - return uniqueArtifacts.items(); -} - -/** - * Used to backfill data from any csv. Tells you where to query next (if that's - * needed). Also tells you how many rows you're getting. It checks if the dates - * are ordered. If they're not this will error. The data must be ordered and a - * `time` field must be the first field. It will normalize on days by default - * (this means the date it will give for you to continue from will be normalized - * to the day/hour/month _before or on_ the second to last available date). This - * command can be fed multiple paths and it will ensure that these csv files - * maintain a continous set of results. - */ -export async function splitContractUsage( - args: DuneSplitUsageArgs, -): Promise { - await csvTransformAndSplit(args.path[0], args.outputDir, 0, "day"); -} - -type ScanResult = { - path: string; - isContinuous: boolean; - lastPeriodRows: string[][]; - lastPeriodDateTime: DateTime; -}; - -export async function csvTransformAndSplit( - path: string, - outputDir: string, - _dateIndex: number, - _normalizatingUnit: TimeSeriesLookupNormalizingUnit, - _previousScanResult?: ScanResult, -): Promise { - //const writeStream = fs.createWriteStream(, { encoding: 'utf-8' }); - - // const contractsMap = await new Promise>( - // (resolve, reject) => { - // const map: Record = {}; - // fs.createReadStream( - // "/home/raven/contracts-da1aae77b853fc7c74038ee08eec441b10b89570-90-503188.csv", - // ) - // .pipe(parse({ delimiter: ",", fromLine: 2 })) - // .on("data", (row) => { - // const id = parseInt(row[0], 10); - // const address = row[1]; - // map[id] = address; - // }) - // .on("end", () => { - // resolve(map); - // }) - // .on("error", (err) => { - // reject(err); - // }); - // }, - // ); - - const contracts = await currentContractsTableArtifacts(); - const contractsMap = contracts.reduce>((a, c) => { - a[c.id] = c.name; - return a; - }, {}); - - console.log(contracts.length); - - console.log("here"); - console.log(path); - - let expectedRows = 0; - - return new Promise((resolve, reject) => { - fs.createReadStream(path) - .pipe(parse({ delimiter: ",", fromLine: 2 })) - .pipe( - new Transform({ - objectMode: true, - readableObjectMode: true, - writableObjectMode: true, - highWaterMark: 1, - transform: (chunk: string[], _encoding, callback) => { - const rawRow = parseDuneContractUsageCSVRow(chunk); - expectedRows += rawRow.usage.length; - callback(null, rawRow); - }, - }), - ) - .pipe(new DuneSplitRow(contractsMap, { highWaterMark: 1 })) - .pipe(new DuneSafeAggregate({ highWaterMark: 1000000 })) - .pipe(new DuneDayExporter(outputDir, { highWaterMark: 1000 })) - .on("data", (t) => { - console.log(t); - }) - .on("error", (err) => { - /* eslint-disable no-restricted-properties */ - console.error(err); - reject(err); - }) - .on("finish", () => { - console.log(`EXPECTED ROWS: ${expectedRows}`); - resolve({ - path: "", - isContinuous: false, - lastPeriodRows: [], - lastPeriodDateTime: DateTime.now(), - }); - }); - // .on('data', (rows: DailyContractUsageRow[]) => { - // if (rows.length === 0) { - // return; - // } - // const rowsTime = DateTime.fromSQL(rows[0].date); - // if (!currentTime) { - // currentTime = rowsTime; - // } - - // if (rowsTime > currentTime!) { - // // Write this out to a file - // const filePath = nodePath.join(outputDir, `${currentTime.toISODate()}.csv`) - // const rowsToWrite = currentDayBuffer; - // const stream = fs.createWriteStream(filePath, { encoding: 'utf-8' }); - // // Be a bad steward and write a lot. We will make one file a day - // currentDayBuffer = []; - // } - // currentDayBuffer.push(...rows) - // }) - // .on('finish', () => { - // }) - }); -} diff --git a/indexer/src/scripts/upload-contracts-table-to-dune.ts b/indexer/src/scripts/upload-contracts-table-to-dune.ts deleted file mode 100644 index cb7c248d3..000000000 --- a/indexer/src/scripts/upload-contracts-table-to-dune.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { In } from "typeorm"; -import { AppDataSource } from "../db/data-source.js"; -import { - Artifact, - ArtifactNamespace, - ArtifactType, -} from "../db/orm-entities.js"; -import { ProjectRepository } from "../db/project.js"; -import fetch from "node-fetch"; -import { DUNE_API_KEY } from "../config.js"; -import { writeFile } from "fs/promises"; -import { UniqueArray } from "../utils/array.js"; -import { sha1FromArray } from "../utils/source-ids.js"; - -export async function main() { - await AppDataSource.initialize(); - - const projects = await ProjectRepository.find({ - relations: { - artifacts: true, - }, - where: { - artifacts: { - type: In([ArtifactType.CONTRACT_ADDRESS]), - namespace: ArtifactNamespace.OPTIMISM, - }, - }, - }); - const allArtifacts = projects.flatMap((p) => p.artifacts); - - const uniqueArtifacts = new UniqueArray((a: Artifact) => a.id); - allArtifacts.forEach((a) => uniqueArtifacts.push(a)); - const sortedUniqueArtifacts = uniqueArtifacts.items(); - - console.log(`SORTED UNIQUE COUNT: ${sortedUniqueArtifacts.length}`); - // Sort by creation - sortedUniqueArtifacts.sort((a, b) => a.id - b.id); - - const rows = ["id,address"]; - rows.push( - ...sortedUniqueArtifacts.map((a) => { - return `${a.id},${a.name}`; - }), - ); - const artifactsCsv = rows.join("\n"); - - if (sortedUniqueArtifacts.length === 0) { - throw new Error("expecting artifacts. have none"); - } - - const contractsCsvSha1 = sha1FromArray([artifactsCsv]); - console.log(`sha1=${contractsCsvSha1}`); - - await writeFile( - `contracts-${contractsCsvSha1}-${sortedUniqueArtifacts[0].id}-${ - sortedUniqueArtifacts.slice(-1)[0].id - }.csv`, - artifactsCsv, - { encoding: "utf-8" }, - ); - - await new Promise((resolve) => { - console.log("about to upload"); - console.log(`sha1=${contractsCsvSha1}`); - console.log(`count=${sortedUniqueArtifacts.length}`); - setTimeout(() => { - console.log("uploading...."); - resolve(); - }, 30000); - }); - - const tableName = `oso_optimism_contracts_${contractsCsvSha1}`; - - const uploader = new DuneCSVUploader(DUNE_API_KEY); - const response = await uploader.upload( - tableName, - `OSO monitored optimism contracts: ${contractsCsvSha1}.`, - rows, - ); - if (response.status !== 200) { - console.log("failed to upload to the contracts"); - process.exit(1); - } - console.log(`uploaded to ${tableName}`); -} - -class DuneCSVUploader { - private apiKey: string; - - constructor(apiKey: string) { - this.apiKey = apiKey; - } - - async upload(tableName: string, description: string, rows: string[]) { - return await fetch("https://api.dune.com/api/v1/table/upload/csv", { - method: "POST", - body: JSON.stringify({ - table_name: tableName, - description: description, - data: rows.join("\n"), - }), - headers: { - "Content-Type": "application/json", - "X-Dune-Api-Key": this.apiKey, - }, - }); - } -} - -main().catch((err) => { - console.log(err); -}); diff --git a/indexer/src/utils/api.ts b/indexer/src/utils/api.ts deleted file mode 100644 index bd2733ed2..000000000 --- a/indexer/src/utils/api.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ADT } from "ts-adt"; - -export interface CommonArgs { - // Auto-respond yes to any user-prompts (useful for CI) - yes?: boolean; - // Mark the query for auto-crawling - // Note: we currently ignore this when false, only updating the column when true - autocrawl?: boolean; - - // The cache directory to use for load/writing cache data. - cacheDir: string; - - runDir: string; -} - -export interface ApiInterface { - command: string; - func: EventSourceFunction; -} - -export interface EventSourceFunction { - (args: Args): Promise; -} - -export type ApiReturnType = ADT<{ - // Signals that there was nothing to retrieve - upToDate: { cached: true }; - // Signals that we had to retrieve `count` events the the source - success: { count: number }; - // Signals that we partially retrieved some data, but go stuck at a rate-limit - rateLimited: { count: number }; - // Signals that we encountered an error - error: { e: Error }; -}>; diff --git a/indexer/src/utils/array.ts b/indexer/src/utils/array.ts deleted file mode 100644 index 4592d8697..000000000 --- a/indexer/src/utils/array.ts +++ /dev/null @@ -1,97 +0,0 @@ -import _ from "lodash"; - -type Addressable = number | string; - -export class UniqueArray { - private uniqueMap: { [key: Addressable]: boolean }; - private arr: T[]; - private idFunc: (value: T) => Addressable; - - constructor(idFunc: (value: T) => Addressable) { - this.uniqueMap = {}; - this.arr = []; - this.idFunc = idFunc; - } - - push(obj: T) { - const id = this.idFunc(obj); - if (this.uniqueMap[id] !== undefined) { - return this.arr.length; - } - this.uniqueMap[id] = true; - this.arr.push(obj); - } - - pop(): T | undefined { - const obj = this.arr.pop(); - if (obj) { - const id = this.idFunc(obj); - delete this.uniqueMap[id]; - } - return obj; - } - - popAll(): T[] { - const all = this.arr; - this.arr = []; - this.uniqueMap = {}; - return all; - } - - items(): T[] { - return _.cloneDeep(this.arr); - } - - clear() { - this.arr = []; - this.uniqueMap = {}; - } - - get length() { - return this.arr.length; - } -} - -/** - * asyncBatch creates batches of a given array for processing. This function - * awaits the callback for every batch. - * - * @param arr - The array to process in batches - * @param batchSize - The batch size - * @param cb - The async callback - * @returns - */ -export async function asyncBatch( - arr: T[], - batchSize: number, - cb: (batch: T[], batchLength: number, batchNumber: number) => Promise, -): Promise { - let batch = []; - let batchNumber = 0; - const results: R[] = []; - for (const item of arr) { - batch.push(item); - - if (batch.length >= batchSize) { - const batchResult = await cb(batch, batch.length, batchNumber); - results.push(batchResult); - batchNumber += 1; - batch = []; - } - } - if (batch.length > 0) { - const batchResult = await cb(batch, batch.length, batchNumber); - results.push(batchResult); - batch = []; - } - return results; -} - -export async function asyncBatchFlattened( - arr: T[], - batchSize: number, - cb: (batch: T[], batchLength: number, batchNumber: number) => Promise, -): Promise { - const batches = await asyncBatch(arr, batchSize, cb); - return batches.flat(1); -} diff --git a/indexer/src/utils/async-results.test.ts b/indexer/src/utils/async-results.test.ts deleted file mode 100644 index da8cf68b9..000000000 --- a/indexer/src/utils/async-results.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { collectAsyncResults } from "./async-results.js"; - -describe("reducePromises", () => { - it("should reduce promises", async () => { - const err2 = new Error("two"); - const err4 = new Error("four"); - const results = await collectAsyncResults([ - Promise.resolve("one"), - Promise.reject(err2), - Promise.resolve("three"), - Promise.reject(err4), - ]); - - expect(results.errors.length).toEqual(2); - expect(results.success.length).toEqual(2); - expect(results.success).toEqual(["one", "three"]); - expect(results.errors).toEqual([err2, err4]); - }); -}); diff --git a/indexer/src/utils/async-results.ts b/indexer/src/utils/async-results.ts deleted file mode 100644 index 6c1deffb6..000000000 --- a/indexer/src/utils/async-results.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Tools for dealing with async coordination - -export type PossiblyError = unknown | undefined | null; -export type AsyncResults = { - success: T[]; - errors: PossiblyError[]; -}; - -export function collectAsyncResults( - promises: Promise[], -): Promise> { - const caughtPromises: Promise>[] = promises.map((p) => { - return p - .then((s: T) => { - return { - success: [s], - errors: [], - }; - }) - .catch((err) => { - return { - success: [], - errors: [err], - }; - }); - }); - - return Promise.all(caughtPromises).then((results) => { - return results.reduce>( - (acc, result) => { - acc.errors.push(...result.errors); - acc.success.push(...result.success); - return acc; - }, - { - errors: [], - success: [], - }, - ); - }); -} diff --git a/indexer/src/utils/cli.ts b/indexer/src/utils/cli.ts deleted file mode 100644 index 07d467843..000000000 --- a/indexer/src/utils/cli.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { DateTime } from "luxon"; - -export function coerceDateTime(input: string) { - const date = DateTime.fromISO(input).toUTC(); - if (!date.isValid) { - throw new Error(`input "${input}" is not a valid date`); - } - return date; -} - -export function coerceDateTimeOrNull(input: string) { - if (input) { - return coerceDateTime(input); - } - return null; -} - -export function coerceDateTimeOrNow(input: string) { - if (input) { - return coerceDateTime(input); - } - return DateTime.now(); -} diff --git a/indexer/src/utils/common.ts b/indexer/src/utils/common.ts deleted file mode 100644 index ee7063f24..000000000 --- a/indexer/src/utils/common.ts +++ /dev/null @@ -1,130 +0,0 @@ -import _ from "lodash"; -import { AssertionError, NullOrUndefinedValueError } from "./error.js"; - -/** - * Explicitly marks a promise as something we won't await - * @param _promise - */ -export function spawn(_promise: Promise) {} // eslint-disable-line - -/** - * Asserts that a condition is true. - * @param cond - * @param msg - */ -export function assert(cond: T, msg: string): asserts cond { - if (!cond) { - // eslint-disable-next-line no-debugger - debugger; - throw new AssertionError(msg || "Assertion failed"); - } -} - -/** - * Asserts that a branch is never taken. - * Useful for exhaustiveness checking. - * @param _x - */ -export function assertNever(_x: never): never { - throw new Error("unexpected branch taken"); -} - -/** - * Asserts that a value is not null or undefined. - * @param x - * @param msg - * @returns - */ -export function ensure(x: T | null | undefined, msg: string): T { - if (x === null || x === undefined) { - // eslint-disable-next-line no-debugger - debugger; - throw new NullOrUndefinedValueError( - `Value must not be undefined or null${msg ? `- ${msg}` : ""}`, - ); - } else { - return x; - } -} - -/** - * Asserts that a value is a string. - * @param x - * @returns - */ -export function ensureString(x: any) { - if (_.isString(x)) { - return x; - } else { - throw new Error(`Expected string, but got ${typeof x}`); - } -} - -/** - * Asserts that a value is a number - * @param x - * @returns - */ -export function ensureNumber(x: any) { - if (_.isNumber(x)) { - return x; - } else { - throw new Error(`Expected number, but got ${typeof x}`); - } -} - -/** - * Asserts that a value is an array. - * @param x - * @returns - */ -export function ensureArray(x: T | T[] | undefined): T[] { - if (x == null) { - return []; - } else if (Array.isArray(x)) { - return x; - } else { - return [x]; - } -} - -/** - * Return an object type that can be used for comparisons - * @param record - * @returns - */ -export const normalizeToObject = (record?: any) => - safeCast(_.toPlainObject(record) as Partial); - -/** - * Explicitly mark that a cast is safe. - * e.g. `safeCast(x as string[])`. - */ -export function safeCast(x: T): T { - return x; -} - -/** - * Mark that a cast is tech debt that needs to be cleaned up. - */ -export function hackyCast(x: any): T { - return x; -} - -/** - * Marks that a cast should be checked at runtime. - * Usually this is at some system boundary, e.g. a message received over the network. - */ -export function uncheckedCast(x: any): T { - return x; -} - -export function isTruthy(value: T): value is _.Truthy { - return !!value; -} - -export function filterFalsy( - xs: ReadonlyArray, -): T[] { - return xs.filter(isTruthy); -} diff --git a/indexer/src/utils/debug.ts b/indexer/src/utils/debug.ts deleted file mode 100644 index f4473e060..000000000 --- a/indexer/src/utils/debug.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { DateTime } from "luxon"; -import { logger } from "./logger.js"; - -class Timer { - private start: DateTime; - - constructor() { - this.start = DateTime.now(); - } - - finish(message: string) { - logger.info(`${this.start.diffNow().milliseconds * -1}ms: ${message}`); - } -} - -export function timer(label: string) { - const timer = new Timer(); - return () => { - timer.finish(`${label} completed`); - }; -} diff --git a/indexer/src/utils/dune/type.ts b/indexer/src/utils/dune/type.ts deleted file mode 100644 index 28ce5137c..000000000 --- a/indexer/src/utils/dune/type.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { - ExecutionResponse, - ExecutionState, - QueryParameter, - ResultsResponse, -} from "@cowprotocol/ts-dune-client"; - -// Generic dune client this will be useful for testing -export interface IDuneClient { - refresh( - queryId: number, - parameters?: QueryParameter[], - pingFrequency?: number, - ): Promise; - execute( - queryId: number, - parameters?: QueryParameter[], - ): Promise; -} - -export class NoopDuneClient implements IDuneClient { - constructor() {} - - async refresh( - _queryId: number, - _parameters?: QueryParameter[] | undefined, - _pingFrequency?: number | undefined, - ): Promise { - return { - execution_id: "", - state: ExecutionState.COMPLETED, - submitted_at: new Date(), - query_id: 123, - }; - } - - async execute( - _queryId: number, - _parameters?: QueryParameter[] | undefined, - ): Promise { - return { - execution_id: "", - state: ExecutionState.COMPLETED, - }; - } -} diff --git a/indexer/src/utils/error.ts b/indexer/src/utils/error.ts deleted file mode 100644 index 8a4fd876d..000000000 --- a/indexer/src/utils/error.ts +++ /dev/null @@ -1,61 +0,0 @@ -import chalk from "chalk"; -import { logger } from "./logger.js"; - -/** - * Used with assert statements (See common.ts) - */ -export class AssertionError extends Error { - constructor(msg = "Assertion failed") { - super(msg); - } -} - -/** - * Something is `null` or `undefined` when we don't expect it - */ -export class NullOrUndefinedValueError extends Error {} - -/** - * Some value is out of an expected bound - */ -export class OutOfBoundsError extends Error {} - -/** - * Data is malformed - */ -export class InvalidDataError extends Error {} - -/** - * Invalid inputs to a function - */ -export class InvalidInputError extends Error {} - -/** - * Data is malformed - */ -export class MalformedDataError extends Error {} - -/** - * Represents an error that doesn't need to be forwarded to Sentry. - * These are usually errors that are the user's fault - */ -export class HandledError extends Error {} - -/** - * Catches HandledErrors and just exits - * Forwards all other errors along. - * @param p - * @returns - */ -export const handleError = (p: Promise) => { - return p.catch((e) => { - if (e.message) { - logger.error(chalk.bold(chalk.redBright("\nError: ")) + e.message); - } - if (e instanceof HandledError) { - process.exit(1); - } else { - throw e; - } - }); -}; diff --git a/indexer/src/utils/files.ts b/indexer/src/utils/files.ts deleted file mode 100644 index 16bfe8bce..000000000 --- a/indexer/src/utils/files.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as fs from "fs/promises"; - -export async function fileExists(filePath: string): Promise { - try { - // Use fs.promises.access() with the fs.constants.F_OK flag to check if the file exists - await fs.access(filePath, fs.constants.F_OK); - return true; - } catch (error) { - // If an error is thrown, the file does not exist - return false; - } -} diff --git a/indexer/src/utils/format.ts b/indexer/src/utils/format.ts deleted file mode 100644 index 36e50e2d3..000000000 --- a/indexer/src/utils/format.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Dayjs } from "dayjs"; - -// human-readable URL for a package -const getNpmUrl = (slug: string) => `https://www.npmjs.com/package/${slug}`; -// date format used by NPM APIs -const formatDate = (date: Dayjs) => date.format("YYYY-MM-DD"); -// combine owner/name into a slug -const getSlug = (owner: string, name: string) => `${owner}/${name}`; - -export { formatDate, getNpmUrl, getSlug }; diff --git a/indexer/src/utils/getPath.ts b/indexer/src/utils/getPath.ts deleted file mode 100644 index e8fedf9f8..000000000 --- a/indexer/src/utils/getPath.ts +++ /dev/null @@ -1,48 +0,0 @@ -// ex: getPath({ user: { name: 'u1' }}, "user.name") -export function getPath< - T extends Record, - K extends Path, ->(obj: T, path: K): Choose { - // https://stackoverflow.com/a/6394168 - return path.split(".").reduce((o, i) => o[i], obj) as any; -} - -// Here there be dragons -// https://www.calebpitan.com/blog/dot-notation-type-accessor-in-typescript - -type ExcludeArrayKeys = - T extends ArrayLike ? Exclude : keyof T; -export type IsAny = unknown extends T - ? [keyof T] extends [never] - ? false - : true - : false; - -type PathImpl2 = PathImpl | keyof T; - -type PathImpl = Key extends string - ? IsAny extends true - ? never - : T[Key] extends Record - ? - | `${Key}.${PathImpl> & string}` - | `${Key}.${ExcludeArrayKeys & string}` - : never - : never; - -export type Path = keyof T extends string - ? PathImpl2 extends infer P - ? P extends string | keyof T - ? P - : keyof T - : keyof T - : never; - -export type Choose< - T extends Record, - K extends Path, -> = K extends `${infer U}.${infer Rest}` - ? Rest extends Path - ? Choose - : never - : T[K]; diff --git a/indexer/src/utils/logger.ts b/indexer/src/utils/logger.ts deleted file mode 100644 index b20d5afd6..000000000 --- a/indexer/src/utils/logger.ts +++ /dev/null @@ -1,19 +0,0 @@ -import winston from "winston"; - -export const logger = winston.createLogger({ - //level: "info", - level: "debug", - format: winston.format.json(), - defaultMeta: {}, - transports: [ - new winston.transports.Console({ - format: winston.format.printf( - (info) => `${info.message}`, - //info => `${moment().format("HH:mm:ss")}:${info.level}\t${info.message}` - ), - stderrLevels: ["error"], - }), - //new winston.transports.File({ filename: "error.log", level: "error" }), - //new winston.transports.File({ filename: "combined.log", level: "info" }), - ], -}); diff --git a/indexer/src/utils/lookup.ts b/indexer/src/utils/lookup.ts deleted file mode 100644 index db2301c66..000000000 --- a/indexer/src/utils/lookup.ts +++ /dev/null @@ -1,24 +0,0 @@ -export class EntityLookup { - private storage: Array; - lookup: Record; - private idFunc: (value: T) => string; - - constructor(idFunc: (value: T) => string) { - this.storage = []; - this.lookup = {}; - this.idFunc = idFunc; - } - - push(item: T) { - this.storage.push(item); - this.lookup[this.idFunc(item)] = item; - } - - has(other: T) { - return this.hasId(this.idFunc(other)); - } - - hasId(id: string) { - return this.lookup[id] !== undefined; - } -} diff --git a/indexer/src/utils/parsing.ts b/indexer/src/utils/parsing.ts deleted file mode 100644 index 45f9d6eab..000000000 --- a/indexer/src/utils/parsing.ts +++ /dev/null @@ -1,134 +0,0 @@ -import path from "path"; - -type ParseGitHubUrlResult = { - // e.g. https://github.com/hypercerts-org/oso - url: string; - // e.g. 'hypercerts-org/oso' - slug: string; - // e.g. 'hypercerts-org' - owner: string; - // e.g. 'oso' - repo?: string; -}; - -/** - * Parse a GitHub URL into its owner and name - * @param url - * @returns null if invalid input, otherwise the results - */ -function parseGitHubUrl(urlStr: any): ParseGitHubUrlResult | null { - if (typeof urlStr !== "string") { - return null; - } - - try { - const url = new URL(urlStr); - const pathParts = url.pathname.split("/").filter((x) => !!x); - if (!["github.com", "www.github.com"].includes(url.host)) { - // Check the host - return null; - } else if (pathParts.length < 1) { - // Check if there is a path - return null; - } else if (pathParts.length === 1) { - // If only 1 path part, then it is the owner with no repo - return { - url: urlStr, - slug: pathParts[0], - owner: pathParts[0], - }; - } else if (pathParts.length > 1) { - // Otherwise, take the first 2 parts as the owner and repo - return { - url: urlStr, - slug: `${pathParts[0]}/${pathParts[1]}`, - owner: pathParts[0], - repo: path.basename(pathParts[1], ".git"), - }; - } else { - // This should never happen - return null; - } - } catch (_e) { - return null; - } -} - -function isGitHubOrg(urlStr: string) { - const { owner, repo } = parseGitHubUrl(urlStr) ?? {}; - return !!owner && !repo; -} - -function isGitHubRepo(urlStr: string) { - const { owner, repo } = parseGitHubUrl(urlStr) ?? {}; - return !!owner && !!repo; -} - -type ParseNpmUrlResult = { - // e.g. 'https://www.npmjs.com/package/@hypercerts-org/oso' - url: string; - // e.g. '@hypercerts-org/oso' - slug: string; - // e.g. '@hypercerts-org' - scope?: string; -}; - -/** - * - * Parse an npm URL into its scope and name - * @param url - * @returns null if invalid input, otherwise the results - */ -function parseNpmUrl(urlStr: any): ParseNpmUrlResult | null { - if (typeof urlStr !== "string") { - return null; - } - - try { - const url = new URL(urlStr); - const pathParts = url.pathname.split("/").filter((x) => !!x); - if (!["npmjs.com", "www.npmjs.com"].includes(url.host)) { - // Check the host - return null; - } else if (pathParts.length < 1) { - // Check for a valid path - return null; - } else if (pathParts.length === 1) { - // If only 1 path part, then it is the slug - return { - url: urlStr, - slug: pathParts[0], - }; - } else if (pathParts[0] !== "package") { - // If there are more than 2 parts, the first part must be "package" - return null; - } else if (pathParts.length === 2) { - // If there are 2 parts, then the slug is the second part - return { - url: urlStr, - slug: pathParts[1], - }; - } else if (pathParts.length > 2) { - // Otherwise, the scope is the first part and the slug is the first 2 - return { - url: urlStr, - slug: `${pathParts[1]}/${pathParts[2]}`, - scope: pathParts[1], - }; - } else { - // This should never happen - return null; - } - } catch (_e) { - return null; - } -} - -export { - ParseGitHubUrlResult, - parseGitHubUrl, - isGitHubOrg, - isGitHubRepo, - ParseNpmUrlResult, - parseNpmUrl, -}; diff --git a/indexer/src/utils/pubsub.test.ts b/indexer/src/utils/pubsub.test.ts deleted file mode 100644 index 49e22d030..000000000 --- a/indexer/src/utils/pubsub.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { PromisePubSub, PromisePubSubTimeoutError } from "./pubsub.js"; - -describe("PromisePubSub", () => { - it("should pub sub correctly", async () => { - const ps = new PromisePubSub(); - const sub0 = ps.sub("test"); - const sub1 = ps.sub("test"); - ps.pub("test", null, 1); - expect(await Promise.all([sub0, sub1])).toEqual([1, 1]); - }); - - it("should timeout", async () => { - const ps = new PromisePubSub({ - timeoutMs: 100, - }); - const sub0 = ps.sub("test"); - await expect(sub0).rejects.toThrow(PromisePubSubTimeoutError); - }); -}); diff --git a/indexer/src/utils/pubsub.ts b/indexer/src/utils/pubsub.ts deleted file mode 100644 index e60bf98b3..000000000 --- a/indexer/src/utils/pubsub.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { EventEmitter } from "node:events"; -import { logger } from "./logger.js"; -import _ from "lodash"; -import { GenericError } from "../common/errors.js"; - -type PromiseResolvers = { - resolve: (o: T) => void; - reject: (err: E) => void; - timeout: NodeJS.Timeout | null; -}; - -export interface PromisePubSubOptions { - timeoutMs: number; -} - -export class PromisePubSubTimeoutError extends GenericError {} - -/** - * Allows a simple internal pub sub that can be resolved with promises - */ -export class PromisePubSub { - private topicResolvers: Record[]>; - private emitter: EventEmitter; - private options: PromisePubSubOptions; - - constructor(options?: PromisePubSubOptions) { - this.topicResolvers = {}; - this.emitter = new EventEmitter(); - this.options = _.merge( - { - timeoutMs: 0, - }, - options, - ); - this.emitter.on("message", (topic: string, err: E | null, res: T) => { - const resolvers = this.topicResolvers[topic] || []; - this.topicResolvers[topic] = []; - - for (const resolver of resolvers) { - try { - if (err !== null) { - resolver.reject(err); - } - resolver.resolve(res); - - if (resolver.timeout) { - clearTimeout(resolver.timeout); - } - } catch (err) { - logger.error("Unexpectedly errored resolving pub sub promises"); - } - } - }); - } - - sub(topic: string): Promise { - return new Promise((resolve, reject) => { - const resolvers = this.topicResolvers[topic] || []; - const timeout = - this.options.timeoutMs <= 0 - ? null - : setTimeout(() => { - reject(new PromisePubSubTimeoutError(`timeout for ${topic}`)); - }, this.options.timeoutMs); - - resolvers.push({ - resolve: resolve, - reject: reject, - timeout: timeout, - }); - this.topicResolvers[topic] = resolvers; - }); - } - - pub(topic: string, err: E | null, res?: T) { - this.emitter.emit("message", topic, err, res); - } -} diff --git a/indexer/src/utils/ranges.test.ts b/indexer/src/utils/ranges.test.ts deleted file mode 100644 index 0695e5abe..000000000 --- a/indexer/src/utils/ranges.test.ts +++ /dev/null @@ -1,336 +0,0 @@ -import { DateTime } from "luxon"; -import { - findMissingRanges, - Range, - removeOverlappingRanges, - doRangesIntersect, - rangesEqual, - rangeFromISO, - rangeUnion, -} from "./ranges.js"; - -describe("findMissingRanges", () => { - it("should find missing ranges correctly", () => { - const ranges: Range[] = [ - { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T04:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T08:00:00"), - endDate: DateTime.fromISO("2023-01-01T10:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T08:00:00"), - endDate: DateTime.fromISO("2023-01-01T11:00:00"), - }, - ]; - - const startDate: DateTime = DateTime.fromISO("2023-01-01T00:00:00"); - const endDate: DateTime = DateTime.fromISO("2023-01-01T12:00:00"); - - const missingRanges: Range[] = findMissingRanges( - startDate, - endDate, - ranges, - ); - - expect(missingRanges).toEqual([ - { - startDate: DateTime.fromISO("2023-01-01T04:00:00"), - endDate: DateTime.fromISO("2023-01-01T08:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T11:00:00"), - endDate: DateTime.fromISO("2023-01-01T12:00:00"), - }, - ]); - }); - - it("should find missing ranges correctly if ranges are out of order", () => { - const ranges: Range[] = [ - { - startDate: DateTime.fromISO("2023-01-01T08:00:00"), - endDate: DateTime.fromISO("2023-01-01T10:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T08:00:00"), - endDate: DateTime.fromISO("2023-01-01T11:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T04:00:00"), - }, - ]; - - const startDate: DateTime = DateTime.fromISO("2023-01-01T00:00:00"); - const endDate: DateTime = DateTime.fromISO("2023-01-01T12:00:00"); - - const missingRanges: Range[] = findMissingRanges( - startDate, - endDate, - ranges, - ); - - expect(missingRanges).toEqual([ - { - startDate: DateTime.fromISO("2023-01-01T04:00:00"), - endDate: DateTime.fromISO("2023-01-01T08:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T11:00:00"), - endDate: DateTime.fromISO("2023-01-01T12:00:00"), - }, - ]); - }); - - it("should handle ranges missing entirely", () => { - const beginDate: DateTime = DateTime.fromISO("2023-01-01T00:00:00"); - const endDate: DateTime = DateTime.fromISO("2023-01-01T12:00:00"); - - const missingRanges: Range[] = findMissingRanges(beginDate, endDate, []); - - const expectedRange = rangeFromISO( - "2023-01-01T00:00:00", - "2023-01-01T12:00:00", - ); - - expect(missingRanges.length).toEqual(1); - expect(rangesEqual(missingRanges[0], expectedRange)).toBe(true); - }); - - it("should handle no missing ranges", () => { - const ranges: Range[] = [ - { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T04:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T04:00:00"), - endDate: DateTime.fromISO("2023-01-01T12:00:00"), - }, - ]; - - const beginDate: DateTime = DateTime.fromISO("2023-01-01T00:00:00"); - const endDate: DateTime = DateTime.fromISO("2023-01-01T12:00:00"); - - const missingRanges: Range[] = findMissingRanges( - beginDate, - endDate, - ranges, - ); - - expect(missingRanges).toEqual([]); - }); -}); - -describe("removeOverlappingRanges", () => { - it("should remove overlapping ranges and choose the largest range", () => { - const inputRanges: Range[] = [ - { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T04:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T03:00:00"), - endDate: DateTime.fromISO("2023-01-01T07:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T08:00:00"), - endDate: DateTime.fromISO("2023-01-01T10:00:00"), - }, - ]; - - const expectedRanges: Range[] = [ - { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T04:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T08:00:00"), - endDate: DateTime.fromISO("2023-01-01T10:00:00"), - }, - ]; - - const nonOverlappingRanges: Range[] = removeOverlappingRanges(inputRanges); - - expect(nonOverlappingRanges).toEqual(expectedRanges); - }); - - it("should handle non-overlapping ranges", () => { - const inputRanges: Range[] = [ - { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T02:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T03:00:00"), - endDate: DateTime.fromISO("2023-01-01T05:00:00"), - }, - { - startDate: DateTime.fromISO("2023-01-01T08:00:00"), - endDate: DateTime.fromISO("2023-01-01T10:00:00"), - }, - ]; - - const expectedRanges: Range[] = [...inputRanges]; - - const nonOverlappingRanges: Range[] = removeOverlappingRanges(inputRanges); - - expect(nonOverlappingRanges).toEqual(expectedRanges); - }); - - it("should handle empty input", () => { - const inputRanges: Range[] = []; - - const expectedRanges: Range[] = []; - - const nonOverlappingRanges: Range[] = removeOverlappingRanges(inputRanges); - - expect(nonOverlappingRanges).toEqual(expectedRanges); - }); - - it("should handle single range", () => { - const inputRanges: Range[] = [ - { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T04:00:00"), - }, - ]; - - const expectedRanges: Range[] = [...inputRanges]; - - const nonOverlappingRanges: Range[] = removeOverlappingRanges(inputRanges); - - expect(nonOverlappingRanges).toEqual(expectedRanges); - }); -}); - -describe("doRangesIntersect", () => { - it("should return true for intersecting ranges", () => { - const range1: Range = { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T04:00:00"), - }; - - const range2: Range = { - startDate: DateTime.fromISO("2023-01-01T03:00:00"), - endDate: DateTime.fromISO("2023-01-01T07:00:00"), - }; - - const intersect = doRangesIntersect(range1, range2); - - expect(intersect).toBe(true); - }); - - it("should return true for ranges that are equal", () => { - const range1: Range = { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-02T00:00:00"), - }; - - const range2: Range = { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-02T00:00:00"), - }; - - const intersect = doRangesIntersect(range1, range2); - - expect(intersect).toBe(true); - }); - - it("should return false for non-intersecting ranges", () => { - const range1: Range = { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T04:00:00"), - }; - - const range2: Range = { - startDate: DateTime.fromISO("2023-01-01T04:00:00"), - endDate: DateTime.fromISO("2023-01-01T07:00:00"), - }; - - const intersect = doRangesIntersect(range1, range2); - - expect(intersect).toBe(false); - }); - - it("should return true for partially overlapping ranges", () => { - const range1: Range = { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T04:00:00"), - }; - - const range2: Range = { - startDate: DateTime.fromISO("2023-01-01T03:00:00"), - endDate: DateTime.fromISO("2023-01-01T05:00:00"), - }; - - const intersect = doRangesIntersect(range1, range2); - - expect(intersect).toBe(true); - }); - - it("should throw errors for invalid ranges", () => { - const range1: Range = { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T00:00:00"), - }; - - const range2: Range = { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T00:00:00"), - }; - - expect(() => { - doRangesIntersect(range1, range2); - }).toThrowError(); - }); -}); - -describe("rangesEqual", () => { - it("should return true for equal ranges", () => { - const range1: Range = { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T04:00:00"), - }; - - const range2: Range = { - startDate: DateTime.fromISO("2023-01-01T00:00:00"), - endDate: DateTime.fromISO("2023-01-01T04:00:00"), - }; - - const equal = rangesEqual(range1, range2); - - expect(equal).toBe(true); - }); - - it("should return true for equal ranges with different timezones", () => { - const range1 = rangeFromISO("2023-01-01T00:00:00Z", "2023-01-02T00:00:00"); - - const range2 = rangeFromISO( - "2023-01-01T00:00:00Z", - "2023-01-01T16:00:00-08:00", - ); - - const equal = rangesEqual(range1, range2); - - expect(equal).toBe(true); - }); -}); - -describe("rangeUnion", () => { - it("should get the union of two ranges", () => { - const a = rangeFromISO("2023-01-01T00:00:00Z", "2023-01-31T00:00:00Z"); - const b = rangeFromISO("2023-12-01T00:00:00Z", "2023-12-31T00:00:00Z"); - const expected = rangeFromISO( - "2023-01-01T00:00:00Z", - "2023-12-31T00:00:00Z", - ); - const unions = rangeUnion(a, b); - - const equal = rangesEqual(unions, expected); - - expect(equal).toBe(true); - }); -}); diff --git a/indexer/src/utils/ranges.ts b/indexer/src/utils/ranges.ts deleted file mode 100644 index b1edcbe0b..000000000 --- a/indexer/src/utils/ranges.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { DateTime, DurationLikeObject, DateTimeUnit } from "luxon"; - -export interface Range { - startDate: DateTime; - endDate: DateTime; -} - -export function rangeFromDates(startDate: Date, endDate: Date): Range { - return { - startDate: DateTime.fromJSDate(startDate), - endDate: DateTime.fromJSDate(endDate), - }; -} - -// Splits a range -export function rangeSplit(range: Range, by: DateTimeUnit): Range[] { - if (range.startDate > range.endDate) { - throw new Error(`invalid range ${rangeToString(range)}`); - } - if (range.startDate.equals(range.endDate)) { - throw new Error(`invalid range ${rangeToString(range)}`); - } - let currentDate = range.startDate.startOf(by); - const step: DurationLikeObject = {}; - step[by] = 1; - const ranges: Range[] = []; - while (currentDate < range.endDate) { - ranges.push({ - startDate: currentDate, - endDate: currentDate.plus(step), - }); - currentDate = currentDate.plus(step); - } - return ranges; -} - -export function isWithinRange(range: Range, dt: DateTime): boolean { - const dtMillis = dt.toMillis(); - return ( - range.startDate.toMillis() <= dtMillis && - range.endDate.toMillis() > dtMillis - ); -} - -export function rangeFromISO(startDateISO: string, endDateISO: string): Range { - const startDate = DateTime.fromISO(startDateISO, { zone: "utc" }); - const endDate = DateTime.fromISO(endDateISO, { zone: "utc" }); - if (!(startDate.isValid && endDate.isValid)) { - throw new Error("range is invalid"); - } - return { - startDate: startDate, - endDate: endDate, - }; -} - -export function rangeFromObj(obj: { - startDate: string; - endDate: string; -}): Range { - return rangeFromISO(obj.startDate, obj.endDate); -} - -export function rangesEqual(a: Range, b: Range): boolean { - // DateTime#equals doesn't always behave as expected even when timezones are - // seemingly the same GMT+00:00 or Z for example don't have equality (which - // they should) - return ( - a.startDate.toUnixInteger() === b.startDate.toUnixInteger() && - a.endDate.toUnixInteger() === b.endDate.toUnixInteger() - ); -} - -export function rangeUnion(a: Range, b: Range): Range { - const startDate = - a.startDate.toUnixInteger() < b.startDate.toUnixInteger() - ? a.startDate - : b.startDate; - const endDate = - a.endDate.toUnixInteger() > b.endDate.toUnixInteger() - ? a.endDate - : b.endDate; - return { - startDate: startDate, - endDate: endDate, - }; -} - -export function rangeToString(r: Range) { - return `${r.startDate.setZone("utc").toISO()}-${r.endDate - .setZone("utc") - .toISO()}`; -} - -export function doRangesIntersect( - a: Range, - b: Range, - allowZeroRange: boolean = false, -): boolean { - if (!allowZeroRange) { - if (a.endDate.toUnixInteger() == a.startDate.toUnixInteger()) { - throw new Error("a is not a valid range"); - } - if (b.endDate.toUnixInteger() == b.startDate.toUnixInteger()) { - throw new Error("b is not a valid range"); - } - } - return a.startDate < b.endDate && a.endDate > b.startDate; -} - -export function findMissingRanges( - startDate: DateTime, - endDate: DateTime, - ranges: Range[], -): Range[] { - const sortedRanges = ranges.sort( - (a, b) => a.startDate.toUnixInteger() - b.startDate.toUnixInteger(), - ); - - const missingRanges: Range[] = []; - let currentStartDate: DateTime = startDate; - let currentEndDate: DateTime | null = null; - - for (const range of sortedRanges) { - const rangeStartDate = range.startDate; - const rangeEndDate = range.endDate; - - if (currentStartDate < rangeStartDate) { - currentEndDate = rangeStartDate; - missingRanges.push({ - startDate: currentStartDate, - endDate: currentEndDate, - }); - } - currentStartDate = rangeEndDate; - } - - if (currentStartDate < endDate) { - missingRanges.push({ startDate: currentStartDate, endDate: endDate }); - } - - return missingRanges; -} - -export function getRangeDuration(range: Range): number { - // Calculate the duration of the range in milliseconds - return range.endDate.toUnixInteger() - range.startDate.toUnixInteger(); -} - -export function removeOverlappingRanges(ranges: Range[]): Range[] { - // Sort the ranges by their start dates - const sortedRanges = ranges.sort( - (a, b) => a.startDate.toUnixInteger() - b.startDate.toUnixInteger(), - ); - - const nonOverlappingRanges: Range[] = []; - let currentRange: Range | null = null; - - for (const range of sortedRanges) { - if (currentRange === null) { - currentRange = range; - } else { - // Check if the current range overlaps with the next range - if (currentRange.endDate >= range.startDate) { - // If overlapping, choose the larger time range by duration - if (getRangeDuration(range) > getRangeDuration(currentRange)) { - currentRange = range; - } - } else { - // If not overlapping, add the current range to the result and update the current range - nonOverlappingRanges.push(currentRange); - currentRange = range; - } - } - } - - // Add the last remaining range, if any - if (currentRange !== null) { - nonOverlappingRanges.push(currentRange); - } - - return nonOverlappingRanges; -} diff --git a/indexer/src/utils/source-ids.ts b/indexer/src/utils/source-ids.ts deleted file mode 100644 index 85c6d6a98..000000000 --- a/indexer/src/utils/source-ids.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createHash } from "crypto"; - -export function sha1FromArray(a: string[]) { - return hashFromArray(a, "sha1"); -} - -export function hashFromArray(a: string[], algo = "sha256") { - const hash = createHash(algo); - a.forEach((key) => { - hash.update(key); - }); - return hash.digest("hex"); -} diff --git a/indexer/src/validate_eth_address.py b/indexer/src/validate_eth_address.py deleted file mode 100644 index 0772c9323..000000000 --- a/indexer/src/validate_eth_address.py +++ /dev/null @@ -1,125 +0,0 @@ -from dotenv import load_dotenv -import os -import requests - -from ens import ENS -from web3 import Web3 - - -load_dotenv() -ALCHEMY_KEY = os.environ['ALCHEMY_KEY'] -ALCHEMY_KEY_OP = os.environ['ALCHEMY_KEY_OP'] -ETHERSCAN_OP = os.environ['ETHERSCAN_KEY_OP'] - -# create a web3 connection -mainnet_url = f"https://eth-mainnet.g.alchemy.com/v2/{ALCHEMY_KEY}" -w3 = Web3(Web3.HTTPProvider(mainnet_url)) -ns = ENS.fromWeb3(w3) - -optimism_url = f"https://opt-mainnet.g.alchemy.com/v2/{ALCHEMY_KEY_OP}" -op = Web3(Web3.HTTPProvider(optimism_url)) - -# get hex codes for checking address/wallet types - -# TODO: map Optimism contracts - -SPLITS_CODE = w3.eth.get_code("0xD2584c1CF7E3fF11957195732d380DC886F5f05b") -EOA_CODE = w3.eth.get_code("0xEAF9830bB7a38A3CEbcaCa3Ff9F626C424F3fB55") -SAFES = [ - w3.eth.get_code(Web3.toChecksumAddress(x.lower())) - for x in [ - "0x4D9339dd97db55e3B9bCBE65dE39fF9c04d1C2cd", - "0x7DAC9Fc15C1Db4379D75A6E3f330aE849dFfcE18", - "0x70CCBE10F980d80b7eBaab7D2E3A73e87D67B775" - ] -] - -def get_ens(addr): - try: - return ns.name(addr) - except: - return None - - -def get_address_type(addr): - - try: - code = w3.eth.get_code(addr) - except: - return "Unknown" - if code == EOA_CODE: - return "EOA" - if code == SPLITS_CODE: - return "0xSplits" - if code in SAFES: - return "Safe" - return "Unknown" - - -def get_transaction_count(client, addr): - try: - return client.eth.get_transaction_count(addr) - except: - return None - - -def lookup_op_contract(addr): - headers = {'Accept': 'application/json'} - url = "&".join([ - "https://api-optimistic.etherscan.io/api?module=contract&action=getcontractcreation", - f"contractaddresses={addr}", - f"apikey={ETHERSCAN_OP}" - ]) - response = requests.get(url, headers=headers).json() - if response['status'] == '1': - return response['result'][0]['contractAddress'] - else: - return None - - -def get_address_data(address, tx_count=False): - - def checksum(a): - try: - return Web3.toChecksumAddress(a.lower()) - except Exception as e: - print(e) - return None - - - result = { - 'address': None, - 'type': None, - 'ens': None - } - - if not isinstance(address, str): - result.update({ - 'type': "Missing" - }) - elif "oeth:" in address: - address = address.replace("oeth:","") - result.update({ - 'address': address, - 'type': "Safe (OP)" - }) - elif len(address) == 42: - address = checksum(address) - result.update({ - 'address': address, - 'type': get_address_type(address), - 'ens': get_ens(address) - }) - else: - result.update({ - 'type': "Needs Review" - }) - - if tx_count: - result.update(dict( - eth_count=get_transaction_count(w3, address), - op_count=get_transaction_count(op, address) - )) - - return result - diff --git a/indexer/src/validate_github_org.py b/indexer/src/validate_github_org.py deleted file mode 100644 index f14f19150..000000000 --- a/indexer/src/validate_github_org.py +++ /dev/null @@ -1,44 +0,0 @@ -from dotenv import load_dotenv -import os -import requests - - -def validate_github_org(owner): - - load_dotenv() - token = os.getenv('GITHUB_TOKEN') - - query = """ - query ($owner: String!) { - organization(login: $owner) { - repositories(first: 10) { - nodes { - name - url - } - } - } - } - """ - headers = {'Authorization': f'token {token}'} - variables = {'owner': owner} - response = requests.post( - 'https://api.github.com/graphql', - json={'query': query, 'variables': variables}, - headers=headers - ) - try: - response_json = response.json() - response.raise_for_status() - data = response_json['data'] - except: - print(f"Encountered error retrieving repos for https://github.com/{owner}.") - print(response_json) - return False - - org = data.get('organization') - if org: - return True - - print(f"No valid organization / repositories found for https://github.com/{owner}") - return False diff --git a/indexer/supabase/.gitignore b/indexer/supabase/.gitignore deleted file mode 100644 index 439de01e0..000000000 --- a/indexer/supabase/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Supabase -.branches -.temp -.ipynb_checkpoints -.ipynb \ No newline at end of file diff --git a/indexer/supabase/config.toml b/indexer/supabase/config.toml deleted file mode 100644 index 23036f124..000000000 --- a/indexer/supabase/config.toml +++ /dev/null @@ -1,82 +0,0 @@ -# A string used to distinguish different Supabase projects on the same host. Defaults to the working -# directory name when running `supabase init`. -project_id = "os-observer" - -[api] -# Port to use for the API URL. -port = 54321 -# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API -# endpoints. public and storage are always included. -schemas = ["public", "storage", "graphql_public"] -# Extra schemas to add to the search_path of every request. public is always included. -extra_search_path = ["public", "extensions"] -# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size -# for accidental or malicious requests. -max_rows = 1000 - -[db] -# Port to use for the local database URL. -port = 54322 -# The database major version to use. This has to be the same as your remote database's. Run `SHOW -# server_version;` on the remote database to check. -major_version = 15 - -[studio] -# Port to use for Supabase Studio. -port = 54323 - -# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they -# are monitored, and you can view the emails that would have been sent from the web interface. -[inbucket] -# Port to use for the email testing server web interface. -port = 54324 -smtp_port = 54325 -pop3_port = 54326 - -[storage] -# The maximum file size allowed (e.g. "5MB", "500KB"). -file_size_limit = "50MiB" - -[auth] -# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used -# in emails. -site_url = "http://localhost:3000" -# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. -additional_redirect_urls = ["https://localhost:3000"] -# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 seconds (one -# week). -jwt_expiry = 3600 -# Allow/disallow new user signups to your project. -enable_signup = true - -[auth.email] -# Allow/disallow new user signups via email to your project. -enable_signup = true -# If enabled, a user will be required to confirm any email change on both the old, and new email -# addresses. If disabled, only the new email is required to confirm. -double_confirm_changes = true -# If enabled, users need to confirm their email address before signing in. -enable_confirmations = false - -# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, -# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin`, `notion`, `twitch`, -# `twitter`, `slack`, `spotify`, `workos`, `zoom`. -[auth.external.apple] -enabled = false -client_id = "" -secret = "" -# Overrides the default auth redirectUrl. -redirect_uri = "" -# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, -# or any other third-party OIDC providers. -url = "" - -[analytics] -enabled = false -port = 54327 -vector_port = 54328 -# Setup BigQuery project to enable log viewer on local development stack. -# See: https://supabase.com/docs/guides/getting-started/local-development#enabling-local-logging -gcp_project_id = "" -gcp_project_number = "" -gcp_jwt_path = "supabase/gcloud.json" diff --git a/indexer/supabase/queries/get_contributors_by_project.sql b/indexer/supabase/queries/get_contributors_by_project.sql deleted file mode 100644 index 628b4f133..000000000 --- a/indexer/supabase/queries/get_contributors_by_project.sql +++ /dev/null @@ -1,40 +0,0 @@ --- This query counts incremental and cumulative commits by contributor --- includes a simple bot filter - -WITH FilteredData AS ( - SELECT - p.name AS project_name, - c.name AS contributor_name, - e."eventType" AS event_type, - e."eventTime" AS event_time, - TO_CHAR(e."eventTime", 'YYYY-MM') AS month - FROM - "Project" p - JOIN - "ProjectsOnArtifacts" poa ON p.id = poa."projectId" - JOIN - "Artifact" a ON poa."artifactId" = a.id - LEFT JOIN - "Event" e ON a.id = e."artifactId" - LEFT JOIN - "Contributor" c ON e."contributorId" = c.id - WHERE - p.name = 'Uniswap' -- insert your project name here - AND e."eventType" = 'COMMIT_CODE' -- insert GitHub event type here -) -SELECT - contributor_name, - month, - COUNT(event_time) AS commits, - CAST(SUM(COUNT(*)) OVER (PARTITION BY contributor_name ORDER BY month) AS integer) AS cumulative_commits, - CASE WHEN - contributor_name LIKE '%-bot%' - OR contributor_name = 'web-flow' - THEN True ELSE False END AS is_bot -FROM - FilteredData -GROUP BY - contributor_name, - month -ORDER BY - contributor_name, month DESC; diff --git a/indexer/supabase/queries/get_project_events.sql b/indexer/supabase/queries/get_project_events.sql deleted file mode 100644 index d8051223a..000000000 --- a/indexer/supabase/queries/get_project_events.sql +++ /dev/null @@ -1,21 +0,0 @@ --- This query retrieves all events associated with a specific project. -SELECT - p.id AS project_id, - p.name AS project_name, - a.namespace as artifact_namespace, - a.name AS artifact_name, - c.name AS contributor_name, - e."eventType" AS event_type, - e."eventTime" AS event_time -FROM - "Project" p -JOIN - "ProjectsOnArtifacts" poa ON p.id = poa."projectId" -JOIN - "Artifact" a ON poa."artifactId" = a.id -LEFT JOIN - "Event" e ON a.id = e."artifactId" -LEFT JOIN - "Contributor" c ON e."contributorId" = c.id -WHERE - p.name = 'Uniswap'; -- insert your project name here \ No newline at end of file diff --git a/indexer/supabase/queries/get_project_users.sql b/indexer/supabase/queries/get_project_users.sql deleted file mode 100644 index 5e2ed2548..000000000 --- a/indexer/supabase/queries/get_project_users.sql +++ /dev/null @@ -1,30 +0,0 @@ --- This query identifies active blockchain users of a project -WITH UserInteractions AS ( - SELECT - e."contributorId" AS contributor_id, - COUNT(*) AS interaction_count - FROM - "Event" e - JOIN - "Artifact" a ON e."artifactId" = a.id - JOIN - "ProjectsOnArtifacts" poa ON a.id = poa."artifactId" - JOIN - "Project" p ON poa."projectId" = p.id - WHERE - e."eventTime" >= CURRENT_DATE - INTERVAL '30 days' AND - e."eventTime" < CURRENT_DATE AND - e."eventType" = 'CONTRACT_INVOKED' AND - p.name = 'Uniswap' -- insert your project name here - GROUP BY - e."contributorId" -) -SELECT - c.id AS contributor_id, - c.name AS contributor_name, - ui.interaction_count -FROM - "Contributor" c -JOIN - UserInteractions ui ON c.id = ui.contributor_id -ORDER BY 3 DESC; \ No newline at end of file diff --git a/indexer/supabase/queries/get_stars_by_project.sql b/indexer/supabase/queries/get_stars_by_project.sql deleted file mode 100644 index 7870dad10..000000000 --- a/indexer/supabase/queries/get_stars_by_project.sql +++ /dev/null @@ -1,30 +0,0 @@ --- This query counts incremental and cumulative stars by artifact (repo) - -WITH StarredEvents AS ( - SELECT - a.name AS artifact_name, - e."eventTime" AS event_time, - TO_CHAR(e."eventTime", 'YYYY-MM') AS month - FROM - "Project" p - JOIN - "ProjectsOnArtifacts" poa ON p.id = poa."projectId" - JOIN - "Artifact" a ON poa."artifactId" = a.id - LEFT JOIN - "Event" e ON a.id = e."artifactId" - WHERE - e."eventType" = 'STARRED' AND - p.name = 'Uniswap' -- insert your project name here -) -SELECT - artifact_name, - month, - COUNT(*) AS star_count, - CAST (SUM(COUNT(*)) OVER (PARTITION BY artifact_name ORDER BY month) AS integer) AS cumulative_stars -FROM - StarredEvents -GROUP BY - artifact_name, month -ORDER BY - artifact_name, month; diff --git a/indexer/supabase/seed.sql b/indexer/supabase/seed.sql deleted file mode 100644 index e69de29bb..000000000 diff --git a/indexer/test/utils/parsing.test.ts b/indexer/test/utils/parsing.test.ts deleted file mode 100644 index c19b69e8b..000000000 --- a/indexer/test/utils/parsing.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { parseGitHubUrl } from "../../src/utils/parsing.js"; - -describe("parsing", () => { - it("parses github git+https urls", () => { - const url = "git+https://github.com/hypercerts-org/hypercerts.git"; - const result = parseGitHubUrl(url); - expect(result).not.toBeNull(); - expect(result?.owner).toEqual("hypercerts-org"); - expect(result?.repo).toEqual("hypercerts"); - }); - - it("parses github ssh urls", () => { - const url = "ssh://github.com/hypercerts-org/hypercerts.git"; - const result = parseGitHubUrl(url); - expect(result).not.toBeNull(); - expect(result?.owner).toEqual("hypercerts-org"); - expect(result?.repo).toEqual("hypercerts"); - }); - - it("parses multi-part github ssh urls", () => { - const url = "ssh://github.com/hypercerts-org/hypercerts/sdk"; - const result = parseGitHubUrl(url); - expect(result).not.toBeNull(); - expect(result?.owner).toEqual("hypercerts-org"); - expect(result?.repo).toEqual("hypercerts"); - }); - - it("parses just a github org - no trailing slash", () => { - const url = "ssh://github.com/hypercerts-org"; - const result = parseGitHubUrl(url); - expect(result).not.toBeNull(); - expect(result?.owner).toEqual("hypercerts-org"); - expect(result?.repo).toBeUndefined(); - }); - - it("parses just a github org - trailing slash", () => { - const url = "ssh://github.com/hypercerts-org/"; - const result = parseGitHubUrl(url); - expect(result).not.toBeNull(); - expect(result?.owner).toEqual("hypercerts-org"); - expect(result?.repo).toBeUndefined(); - }); - - it("doesn't parse gitlab ssh urls", () => { - const url = "ssh://gitlab.com/hypercerts-org/hypercerts.git"; - const result = parseGitHubUrl(url); - expect(result).toBeNull(); - }); - - it("doesn't parse malformed urls", () => { - const url = "hypercerts-org/hypercerts.git"; - const result = parseGitHubUrl(url); - expect(result).toBeNull(); - }); - - it("doesn't parse falsey values", () => { - const result = parseGitHubUrl(false); - expect(result).toBeNull(); - }); -}); diff --git a/indexer/tsconfig.json b/indexer/tsconfig.json deleted file mode 100644 index 19f9cd4ea..000000000 --- a/indexer/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "outDir": "dist", - "target": "ES6", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "declaration": true, - "declarationMap": true, - "esModuleInterop": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "noImplicitAny": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "strictNullChecks": true, - "strictPropertyInitialization": false, - }, - "exclude": ["node_modules"], - "include": ["./src/*.ts", "./src/**/*.ts", "./test"], -} diff --git a/indexer/utilities/address-indexers/.env.example b/indexer/utilities/address-indexers/.env.example deleted file mode 100644 index 1bd359386..000000000 --- a/indexer/utilities/address-indexers/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -ALCHEMY_API_KEY= -ETHERSCAN_API_KEY= -OP_ETHERSCAN_API_KEY= -LOCAL_PATH_TO_OSSD="./oss-directory/data/projects" \ No newline at end of file diff --git a/indexer/utilities/address-indexers/.gitignore b/indexer/utilities/address-indexers/.gitignore deleted file mode 100644 index 784337930..000000000 --- a/indexer/utilities/address-indexers/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# Logs -logs -*.log - -# Data exports -data/dune/ - -# Local artifacts -.DS_Store -.ipynb_checkpoints -*.ipynb -pycache \ No newline at end of file diff --git a/indexer/utilities/address-indexers/src/add_projects.py b/indexer/utilities/address-indexers/src/add_projects.py deleted file mode 100644 index ee9852469..000000000 --- a/indexer/utilities/address-indexers/src/add_projects.py +++ /dev/null @@ -1,222 +0,0 @@ -import json -import yaml - -from ossd import get_yaml_data_from_path, update_yaml_data, make_template - - -def load_data(json_path): - """Load YAML and JSON data.""" - yaml_data = get_yaml_data_from_path() - with open(json_path) as f: - new_data = json.load(f) - return yaml_data, new_data - - -def create_slug_mapping(yaml_data): - """Create a mapping from slug to project data and from GitHub URLs to slugs.""" - mapping = {} - github_to_slug = {} - - for project in yaml_data: - slug = project['slug'] - mapping[slug] = project - - github_urls = project.get('github', []) - for url in github_urls: - github_to_slug[url['url'].lower()] = slug - - return mapping, github_to_slug - - -def update_network_based_on_tags(addr): - """Update the network for an address based on its tags.""" - unsupported_networks = ['pgn', 'fantom'] - - for network in unsupported_networks: - if network in addr['networks']: - addr['networks'].remove(network) - if 'eoa' in addr['tags']: - addr['networks'].append('mainnet') - - if len(addr['networks']) == 0: - return False # Indicate that the address should not be added - return True # Indicate that the address should be added - - -def update_project_blockchain_data(mapping, slug, project): - """Update the blockchain data for a specific project.""" - - new_address = [key for key in project.keys() if key not in ['name', 'github']][0] - info = project[new_address] - new_address = new_address.lower() - - updated = False - for index, addr in enumerate(mapping[slug]['blockchain']): - if addr['address'].lower() == new_address.lower(): - should_add = update_network_based_on_tags(addr) - if should_add: - # Update tags and networks only if they are different - addr['tags'] = list(set(addr['tags']).union(set(info['tags']))) - addr['networks'] = list(set(addr['networks']).union(set(info['networks']))) - updated = True - else: - # Remove the address from the blockchain data - del mapping[slug]['blockchain'][index] - break - - if not updated: - new_address_data = { - "address": new_address, - "tags": info['tags'], - "networks": info['networks'] - } - if update_network_based_on_tags(new_address_data): - mapping[slug]['blockchain'].append(new_address_data) - updated = True - - if updated: - print(f"Updating {slug}") - update_yaml_data([mapping[slug]]) - - - -def update_existing_projects(mapping, github_to_slug, new_data): - """Check for new projects and update existing projects.""" - new_projects = [] - - for project in new_data: - github_info = project.get('github', {}) - if github_info: - url = github_info.get('url', '').lower() - slug = github_to_slug.get(url, None) - - if not slug: - new_projects.append(project) - continue - - if slug not in mapping or 'blockchain' not in mapping[slug]: - continue - - update_project_blockchain_data(mapping, slug, project) - - return new_projects - - -def create_new_project(project, template): - new_project = template.copy() - addresses = [ - {"address": k, **v} - for k,v in project.items() - if k not in ['name', 'github'] - ] - new_project['name'] = project['name'] - new_project['github'] = [project['github']] - new_project['slug'] = project['github']['url'].split('/')[-1].lower() - new_project['blockchain'] = addresses - return new_project - - -def interactive_update(project_dict, template): - - project = create_new_project(project_dict, template) - print("\nNew project:") - print(project) - print("\nDo you want to update this project? (y/n/q)") - status = input() - if status.lower() == 'q': - return False - elif status.lower() == 'n': - return True - elif status.lower() == 'yy': - update_yaml_data([project]) - return True - - print("Enter a new name for the project:") - name = input() - if name: - project['name'] = name - - print("Enter a new slug for the project:") - slug = input() - if slug: - project['slug'] = slug - - print("\nDo you want to update this project? (y/n/q)") - status = input() - if status.lower() == 'q': - return False - elif status.lower() == 'y': - update_yaml_data([project]) - return True - else: - return True - - -def make_collection_from_addresses(yaml_data, new_data, yaml_path, collection_name): - - address_to_slug_mapping = {} - for project in yaml_data: - addresses = project.get('blockchain', []) - if not addresses: - continue - for addr in addresses: - a = addr['address'].lower() - address_to_slug_mapping[a] = project['slug'] - - collection = [] - for project in new_data: - for key in project.keys(): - if key not in ['name', 'github']: - a = key.lower() - slug = address_to_slug_mapping.get(a, None) - if slug: - collection.append(slug) - - collection = sorted(list(set(collection))) - - collection_data = { - "version": 3, - "slug": collection_name, - "name": collection_name, - "projects": collection, - } - - with open(yaml_path, 'w') as f: - yaml.dump(collection_data, f) - print("Dumped collection to", yaml_path) - - -def main(): - - # Define file paths and collection slug here - #json_path = "data/allo/oss-projects.json" - #yaml_path = "data/allo/gitcoin-allo.yaml" - #collection_name = "gitcoin-allo" - - json_path = "data/rpgf2/rpgf2.json" - yaml_path = "data/rpgf2/rpf2.yaml" - collection_name = "rpgf2" - - # Load the data - yaml_data, new_data = load_data(json_path) - - # Create slug to project mapping - mapping, github_to_slug = create_slug_mapping(yaml_data) - - # Create a template for new projects - template = make_template(yaml_data) - - # Update existing projects and find new projects - new_projects = update_existing_projects(mapping, github_to_slug, new_data) - - # Interactively update new projects - for project in new_projects: - status = interactive_update(project, template) - if status == False: - break - - # Make and save the collection - make_collection_from_addresses(yaml_data, new_data, yaml_path, collection_name) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/indexer/utilities/address-indexers/src/add_rpgf2.py b/indexer/utilities/address-indexers/src/add_rpgf2.py deleted file mode 100644 index 87740563a..000000000 --- a/indexer/utilities/address-indexers/src/add_rpgf2.py +++ /dev/null @@ -1,97 +0,0 @@ -import json -import pandas as pd -from address_tagging import is_eoa, fetch_contract_name - - -def load_data(csv_path): - """Load the CSV data into a DataFrame and filter the required columns.""" - df = pd.read_csv(csv_path) - cols = [ - 'Project', - 'profile_github', - 'address_cleaned', - 'Category', - 'Entity', - 'Address Type', - 'address_EthTxCount', - 'address_OpTxCount' - ] - return df[cols] - - -def get_networks_and_chain(row): - """Determine the networks and chain based on the row data.""" - networks = [] - if row['address_EthTxCount'] > 0: - networks.append('mainnet') - if row['address_OpTxCount'] > 0: - networks.append('optimism') - - # Default to 'optimism' if no networks are found - if not networks: - networks.append('optimism') - - return networks, networks[0] - - -def get_tags(row, chain): - """Determine the tags for a blockchain address.""" - tags = ['wallet'] - - if 'EOA' in row['Address Type']: - if is_eoa(chain, row['address_cleaned']): - tags.append('eoa') - else: - print(f"Error with EOA tag for project {row['Project']} and address {row['address_cleaned']}") - else: - contract_name = fetch_contract_name(chain, row['address_cleaned']) - if isinstance(contract_name, str) and 'proxy' in contract_name.lower(): - tags.append('safe') - else: - print(f"Unknown contract for project {row['Project']} and address {row['address_cleaned']}") - - return tags - - -def create_json(df): - """Create a JSON object from the DataFrame.""" - data = [] - - for _, row in df.iterrows(): - github = row['profile_github'] - if not isinstance(github, str): - continue - - networks, chain = get_networks_and_chain(row) - tags = get_tags(row, chain) - - project = { - "name": row['Project'], - "github": {"url": github}, - row['address_cleaned']: { - "tags": tags, - "networks": networks - } - } - data.append(project) - - return data - - -def dump_json(data, json_path): - """Dump the data into a JSON file.""" - with open(json_path, 'w') as f: - json.dump(data, f, indent=4) - - -def main(): - csv_path = 'data/rpgf2/RPGF2 Tagged Projects - clean.csv' - json_path = 'data/rpgf2/rpgf2.json' - - df = load_data(csv_path) - json_data = create_json(df) - dump_json(json_data, json_path) - - -if __name__ == "__main__": - main() diff --git a/indexer/utilities/address-indexers/src/address_tagging.py b/indexer/utilities/address-indexers/src/address_tagging.py deleted file mode 100644 index a4bf54f81..000000000 --- a/indexer/utilities/address-indexers/src/address_tagging.py +++ /dev/null @@ -1,127 +0,0 @@ -import argparse -import csv -from dotenv import load_dotenv -import os -import requests -import time -from web3 import Web3 - - -load_dotenv() -ALCHEMY_API_KEY = os.environ['ALCHEMY_API_KEY'] -APIS = { - 'optimism': { - 'etherscan': f'https://api-optimistic.etherscan.io/api', - 'etherscan_api_key': os.getenv("OP_ETHERSCAN_API_KEY"), - 'alchemy': f'https://opt-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}' - }, - 'mainnet': { - 'etherscan': 'https://api.etherscan.io/api', - 'etherscan_api_key': os.getenv("ETHERSCAN_API_KEY"), - 'alchemy': f'https://eth-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}' - } -} -DEFAULT_API = APIS['mainnet'] - -def is_eoa(chain, address, sleep=.5): - - url = APIS.get(chain, DEFAULT_API)['alchemy'] - payload = { - "id": 1, - "jsonrpc": "2.0", - "params": [address, "latest"], - "method": "eth_getCode" - } - headers = { - "accept": "application/json", - "content-type": "application/json" - } - response = requests.post(url, json=payload, headers=headers) - if response.status_code != 200: - print(f"Error looking up address {address} on {chain}") - return None - result = response.json()['result'] - time.sleep(sleep) - return result == '0x' - - -def fetch_contract_name(chain, address, sleep=.5): - - try: - # TODO: this won't work for unmapped chains unless the project uses a deterministic deployer - api = APIS.get(chain, DEFAULT_API) - url = api['etherscan'] - api_key = api['etherscan_api_key'] - params = { - 'module': 'contract', - 'action': 'getsourcecode', - 'address': address, - 'apikey': api_key - } - response = requests.get(url, params=params) - if response.json()['status'] != '1': - print(f"Error looking up a contract at address {address} on {chain}") - print(response.text) - return None - - contract_name = response.json()['result'][0]['ContractName'] - if not contract_name: - print(f"No contract/name associated with address {address}") - return None - - print(f"{chain}: {address} -> {contract_name}") - time.sleep(sleep) - return contract_name - except: - print(f"\n\n** Fatal error looking up a contract at address {address}\n\n") - return None - - -def parse_csv(csv_path, address_col, label_col, chain): - - with open(csv_path, 'r') as f: - reader = csv.DictReader(f) - rows = [row for row in reader] - - for row in rows: - address = row[address_col] - label = row[label_col] - if address[:2] != '0x': - continue - - address_type = "eoa" if is_eoa(chain, address, sleep=0) else "contract" - contract_name = fetch_contract_name(chain, address, sleep=0) if address_type == "contract" else None - if isinstance(contract_name, str): - contract_name = contract_name.lower() - - if 'wallet' in label: - if address_type == 'eoa': - row[label_col] = 'eoa wallet' - elif isinstance(contract_name, str) and 'safe' in contract_name: - row[label_col] = 'safe wallet' - else: - row[label_col] = 'unknown wallet' - elif 'contract' in label: - if address_type == 'contract': - if isinstance(contract_name,str) and 'factory' in contract_name: - row[label_col] = 'factory contract' - else: - row[label_col] = 'contract' - elif address_type == 'eoa': - row[label_col] = 'eoa' - - with open(csv_path, 'w') as f: - writer = csv.DictWriter(f, fieldnames=reader.fieldnames) - writer.writeheader() - writer.writerows(rows) - -if __name__ == "__main__": - - parser = argparse.ArgumentParser(description='Parse a csv file.') - parser.add_argument('--csv', type=str, required=True, help='The path to the csv file') - parser.add_argument('--address_col', type=str, required=True, help='The column name of the addresses') - parser.add_argument('--label_col', type=str, required=True, help='The column name of the labels') - parser.add_argument('--chain', type=str, required=True, help='The chain to use for lookups') - args = parser.parse_args() - - parse_csv(args.csv, args.address_col, args.label_col, args.chain) \ No newline at end of file diff --git a/indexer/utilities/address-indexers/src/dune_ossd_mapper.py b/indexer/utilities/address-indexers/src/dune_ossd_mapper.py deleted file mode 100644 index 5bfbffff2..000000000 --- a/indexer/utilities/address-indexers/src/dune_ossd_mapper.py +++ /dev/null @@ -1,97 +0,0 @@ -import json -from collections import defaultdict - -from ossd import get_yaml_data_from_path, update_yaml_data, map_addresses_to_slugs - -DUNE_ADDRESS_DATA_PATH = "data/dune_exports/addresses.json" -MAPPING_FILE_PATH = "data/dune_exports/dune_ossd_mapping.json" - -def load_dune_data(): - with open(DUNE_ADDRESS_DATA_PATH) as f: - return json.load(f) - - -def load_yaml_data(): - return get_yaml_data_from_path() - - -def parse_dune_data(dune_data): - addresses = {} - duplicates = {} - for address, info in dune_data.items(): - if len(info['project_list']) == 1: - addresses[address] = info['project_list'][0] - else: - duplicates[address] = { - 'name': info.get('name', None), - 'project_list': info['project_list'] - } - return addresses, duplicates - - - -def map_dune_names_to_yaml_slugs(): - - yaml_data = get_yaml_data_from_path() - dune_data = load_dune_data() - - # Assign slugs to addresses - yaml_addresses = map_addresses_to_slugs(yaml_data, chain='optimism') - - # Parse Dune data - dune_addresses, dune_duplicates = parse_dune_data(dune_data) - - dune_spells_to_slugs = {} - unmapped_addresses = {} - mapped_projects = {} - unmapped_projects = {} - for address, dune_name in dune_addresses.items(): - yaml_slug = yaml_addresses.get(address, None) - if not yaml_slug: - unmapped_addresses[address] = dune_name - continue - - # Case 1: Addressed has been mapped to a YAML - if dune_name not in dune_spells_to_slugs: - dune_spells_to_slugs[dune_name] = yaml_slug - mapped_projects[yaml_slug] = { - "dune_names": [dune_name], - "mapped_addresses": 1, - "unmapped_addresses": [] - } - else: - if dune_name not in mapped_projects[yaml_slug]["dune_names"]: - mapped_projects[yaml_slug]["dune_names"].append(dune_name) - mapped_projects[yaml_slug]["mapped_addresses"] += 1 - - for address, dune_name in unmapped_addresses.items(): - # Case 2: Address is used by multiple projects; skip it - if address in dune_duplicates: - continue - # Case 3: Address belongs to a mapped project but has been mapped yet - if dune_name in dune_spells_to_slugs: - mapped_projects[dune_spells_to_slugs[dune_name]]["unmapped_addresses"].append(address) - continue - # Case 4: Address belongs to a project that has not been mapped to a YAML - if dune_name not in unmapped_projects: - unmapped_projects[dune_name] = [address] - else: - unmapped_projects[dune_name].append(address) - - mapping = { - "dune_spells_to_slugs": dune_spells_to_slugs, - "mapped_projects": mapped_projects, - "unmapped_projects": unmapped_projects, - "dune_duplicates": dune_duplicates - } - - mapping = {key: dict(sorted(value.items())) for key, value in mapping.items()} - - with open(MAPPING_FILE_PATH, "w") as f: - json.dump(mapping, f, indent=4) - - -map_dune_names_to_yaml_slugs() - - - diff --git a/indexer/utilities/address-indexers/src/dune_ossd_reindex.py b/indexer/utilities/address-indexers/src/dune_ossd_reindex.py deleted file mode 100644 index 2552983f0..000000000 --- a/indexer/utilities/address-indexers/src/dune_ossd_reindex.py +++ /dev/null @@ -1,139 +0,0 @@ -import json - -from ossd import get_yaml_data_from_path, update_yaml_data - - -YAML_DATA = get_yaml_data_from_path() - -MAPPING_FILE_PATH = "data/dune_exports/mapping.json" -with open(MAPPING_FILE_PATH) as f: - MAPPINGS = json.load(f) - -DUNE_ADDRESS_DATA_PATH = "data/dune_exports/addresses.json" -with open(DUNE_ADDRESS_DATA_PATH) as f: - DUNE_DATA = json.load(f) - -PROGRESS_FILE = "data/dune_exports/progress.txt" -def read_progress_from_file(): - try: - with open(PROGRESS_FILE, "r") as f: - return set(line.strip() for line in f.readlines()) - except FileNotFoundError: - return set() - -def save_progress_to_file(slug): - with open(PROGRESS_FILE, "a") as f: - f.write(f"{slug}\n") - - -def update_tags(addr): - print(f"Current tags: {addr['tags']}") - valid_tags = ['eoa', 'contract', 'factory', 'creator'] - print("Enter new tags separated by a comma. Options include: eoa, contract, factory, creator") - new_tags = input().split(',') - - while any(tag not in valid_tags for tag in new_tags): - print("Invalid tags. Please try again.") - new_tags = input().split(',') - - addr['tags'] = new_tags - - -def update_project_info(project, chain): - for address, info in DUNE_DATA.items(): - if len(info['project_list']) > 1: - continue - - project_name = info['project_list'][0] - slug = MAPPINGS.get(project_name, None) - if slug != project['slug']: - continue - - address = address.lower() - entry = { - "address": address, - "tags": info['tags'], - "updated": True, - "networks": [chain] - } - if info.get('name'): - entry['name'] = info.get('name') - - found = False - for addr in project.get('blockchain', []): - if addr['address'] == address and chain in addr.get('networks', []): - addr.update(entry) - found = True - break - if not found: - if 'blockchain' not in project: - project['blockchain'] = [] - project['blockchain'].append(entry) - - -def interactive_update(project, chain): - if 'blockchain' not in project: - return False - - relevant_addresses = [addr for addr in project['blockchain'] if chain in addr.get('networks', [])] - updated_addresses = [addr for addr in relevant_addresses if 'updated' in addr] - missed_addresses = [addr for addr in relevant_addresses if 'updated' not in addr] - - if updated_addresses: - print(f"{project['slug']} updated {len(updated_addresses)} addresses.") - - if missed_addresses: - print(f"{project['slug']} missed {len(missed_addresses)} addresses.") - print("Do you want to update this project? (y/n/q)") - status = input() - if status.lower() == 'q': - return 'quit' - if status.lower() == 'n': - return False - for addr in missed_addresses: - print(f"Delete {addr['address']}? (y/n)") - status = input() - if status.lower() == 'y': - project[chain].remove(addr) - elif status.lower() == 'n': - print(f"Update tags for {addr['address']}? (y/n)") - status = input() - if status.lower() == 'y': - update_tags(addr) - - status = input("Do you want to update the project data? (y/n/q) ") - if status == 'y': - for addr in updated_addresses: - del addr['updated'] - return True - - if status == 'q': - return 'quit' - - return False - - -def main(): - - chain = 'optimism' # or mainnet, etc - completed_slugs = read_progress_from_file() - - for project in YAML_DATA: - update_project_info(project, chain) - - for project in YAML_DATA: - if project['slug'] in completed_slugs: - continue - if project['slug'] not in MAPPINGS.values(): - continue - - status = interactive_update(project, chain) - if status == 'quit': - break - elif status: - update_yaml_data([project]) - save_progress_to_file(project['slug']) - - -if __name__ == "__main__": - main() diff --git a/indexer/utilities/address-indexers/src/dune_process_updates.py b/indexer/utilities/address-indexers/src/dune_process_updates.py deleted file mode 100644 index c7fff8ac4..000000000 --- a/indexer/utilities/address-indexers/src/dune_process_updates.py +++ /dev/null @@ -1,285 +0,0 @@ -""" -This script takes in a JSON of project:creator addresses and two CSV files from Dune Analytics. - -The project:creator spellbook is maintained here: -https://github.com/duneanalytics/spellbook/blob/main/models/contracts/optimism/contracts_optimism_contract_creator_address_list.sql - -The CSVs are maintained here: -- https://dune.com/queries/3015427 [EOA deployed contracts] -- https://dune.com/queries/3015464 [Factory deployed contracts] - -Once the data is ingested, it updates the OSS Directory YAML files. -""" - -from collections import Counter -import json -import os -import pandas as pd - -from address_tagging import is_eoa, fetch_contract_name -from ossd import get_yaml_data_from_path, map_addresses_to_slugs, map_slugs_to_project_data, update_yaml_data - - -def get_datestamp(): - return pd.to_datetime('today').strftime("%Y-%m-%d") - - -def sql_to_json_spellbook(directory): - - sql_file = [f for f in os.listdir(directory) if '.sql' in f][0] - with open(os.path.join(directory, sql_file), 'r') as file: - sql = file.read() - - addresses = {} - found_values = False - for line in sql.split('\n'): - if 'values' in line: - found_values = True - if '(creator_address, contract_project)' in line: - break - if found_values and '0x' in line: - line = line.replace(',(','').replace('),','').replace(')','').replace('(','') - address = line.split(',')[0].strip().lower() - project = line.split(',')[1].split('--')[0].strip().replace("'",'') - addresses[address] = project - - addresses = {k: v for k, v in sorted(addresses.items(), key=lambda item: item[1].lower())} - - datestamp = get_datestamp() - with open(os.path.join(directory, f'{datestamp}_spellbook.json'), 'w') as outfile: - json.dump(addresses, outfile, indent=4) - - -def get_latest_file(directory, filename): - latest_file = max([f for f in os.listdir(directory) if filename in f]) - return os.path.join(directory, latest_file) - - -def consolidate_addresses(directory): - - mapping_path = get_latest_file(directory, 'mapping') - creators_path = get_latest_file(directory, 'spellbook') - contracts_path = get_latest_file(directory, 'contracts') - factories_path = get_latest_file(directory, 'factories') - - with open(mapping_path, 'r') as file: - mapping = json.load(file) - - - with open(creators_path, 'r') as file: - creators = json.load(file) - - contracts = pd.read_csv(contracts_path) - factories = pd.read_csv(factories_path) - - addresses = {} - for project, slug in mapping.items(): - if slug not in addresses: - addresses[slug] = { - "creator_addresses": [], - "contract_addresses": [], - "factory_addresses": [], - "duplicate_addresses": [] - } - - creators_list = [k for k, v in creators.items() if v.lower() == project.lower()] - factories_list = list(factories[factories['contract_project'].str.lower() == project.lower()]['contract_creator_if_factory']) - contracts_list = list(contracts[contracts['contract_project'].str.lower() == project.lower()]['contract_address']) - - addresses[slug]['creator_addresses'].extend(creators_list) - addresses[slug]['contract_addresses'].extend(contracts_list) - addresses[slug]['factory_addresses'].extend(factories_list) - - addresses = {k: {k2: list(set(v2)) for k2, v2 in v.items()} for k, v in addresses.items()} - - for slug, info in addresses.items(): - for address in info['factory_addresses']: - if address in info['contract_addresses']: - addresses[slug]['contract_addresses'].remove(address) - if address in info['creator_addresses']: - addresses[slug]['creator_addresses'].remove(address) - - address_counter = Counter() - for slug, info in addresses.items(): - for k, v in info.items(): - address_counter.update(v) - - for address, count in address_counter.items(): - if count > 1: - for slug, info in addresses.items(): - if address in info['creator_addresses']: - addresses[slug]['creator_addresses'].remove(address) - addresses[slug]['duplicate_addresses'].append(address) - if address in info['factory_addresses']: - addresses[slug]['factory_addresses'].remove(address) - addresses[slug]['duplicate_addresses'].append(address) - if address in info['contract_addresses']: - addresses[slug]['contract_addresses'].remove(address) - addresses[slug]['duplicate_addresses'].append(address) - - return addresses - - -def diff_against_previous_run(current_run_path, previous_run_path): - - new_spells = json.load(open(get_latest_file(current_run_path, 'spellbook'), 'r')) - old_spells = json.load(open(get_latest_file(previous_run_path, 'spellbook'), 'r')) - - mapping_path = get_latest_file(current_run_path, 'mapping') - with open(mapping_path, 'r') as file: - mapping = json.load(file) - mapping = {k.lower(): v.lower() for k, v in mapping.items()} - - new_projects = list(set(new_spells.values()) - set(old_spells.values())) - deleted_projects = list(set(old_spells.values()) - set(new_spells.values())) - moved_creator_addresses = [] - new_creator_addresses = [] - deleted_creator_addresses = [] - - for address, project in new_spells.items(): - if address not in old_spells: - new_creator_addresses.append({ - "address": address, - "to": project, - "toSlug": mapping.get(project.lower(), None), - }) - elif old_spells[address] != project: - moved_creator_addresses.append({ - "address": address, - "from": old_spells[address], - "to": project, - "fromSlug": mapping.get(old_spells[address].lower(), None), - "toSlug": mapping.get(project.lower(), None), - }) - for address, project in old_spells.items(): - if address not in new_spells: - deleted_creator_addresses.append({ - "address": address, - "from": project, - "fromSlug": mapping.get(project.lower(), None), - }) - - unmapped_projects = {} - mapped_projects = list(k.lower() for k in mapping.keys()) - for project in set(new_spells.values()): - if project.lower() not in mapped_projects: - unmapped_projects[project] = len([v for v in new_spells.values() if v == project]) - unmapped_projects = {k: v for k, v in sorted(unmapped_projects.items(), key=lambda item: item[1], reverse=True)} - - datestamp = get_datestamp() - diff = { - "date": datestamp, - "new_projects": sorted(new_projects), - "deleted_projects": sorted(deleted_projects), - "moved_creator_addresses": moved_creator_addresses, - "new_creator_addresses": new_creator_addresses, - "deleted_creator_addresses": deleted_creator_addresses, - "unmapped_projects": unmapped_projects - } - - diff_path = os.path.join(current_run_path, f'{datestamp}_diff.json') - with open(os.path.join(diff_path), 'w') as outfile: - json.dump(diff, outfile, indent=4) - - return diff_path - - -def process_updates(directory): - - yaml_data = get_yaml_data_from_path() - addresses_to_slug = map_addresses_to_slugs(yaml_data, 'optimism') - slugs_to_projects = map_slugs_to_project_data(yaml_data) - - dune_address_data = consolidate_addresses(directory) - - for slug, info in dune_address_data.items(): - - if slug in ['safe-global', 'ethereum-attestation-service']: - continue - project_yaml = slugs_to_projects.get(slug, None) - if not project_yaml: - print(f"Skipping {slug} because it's not in the YAML.") - continue - - for address in info['creator_addresses']: - if address not in addresses_to_slug: - addresses_to_slug[address] = slug - print(f"Adding {address} to {slug} because it's not in the YAML.") - if not is_eoa('optimism', address): - print("Warning: address is not an EOA.") - tags = ["contract", "creator"] - else: - tags = ["eoa", "creator"] - project_yaml['blockchain'].append({ - "address": address, - "networks": ["optimism"], - "tags": tags - }) - for address in info['contract_addresses']: - if address not in addresses_to_slug: - addresses_to_slug[address] = slug - print(f"Adding {address} to {slug} because it's not in the YAML.") - name = fetch_contract_name('optimism', address) - project_yaml['blockchain'].append({ - "address": address, - "name": name, - "networks": ["optimism"], - "tags": ["contract"] - }) - for address in info['factory_addresses']: - if address not in addresses_to_slug: - addresses_to_slug[address] = slug - print(f"Adding FACTORY {address} to {slug} because it's not in the YAML.") - name = fetch_contract_name('optimism', address) - project_yaml['blockchain'].append({ - "address": address, - "name": name, - "networks": ["optimism"], - "tags": ["contract", "factory"] - }) - - approved_entries = [] - for address_entry in project_yaml['blockchain']: - address = address_entry['address'] - if 'optimism' not in address_entry['networks']: - approved_entries.append(address_entry) - continue - if 'wallet' in address_entry['tags'] or 'safe' in address_entry['tags']: - approved_entries.append(address_entry) - continue - if not (address in info['creator_addresses'] or address in info['contract_addresses'] or address in info['factory_addresses']): - print(f"Removing {address} from {slug} because it's not in Dune.") - continue - if address in info['creator_addresses'] and 'creator' in address_entry['tags']: - approved_entries.append(address_entry) - continue - if address in info['factory_addresses'] and 'factory' in address_entry['tags']: - approved_entries.append(address_entry) - continue - if address in info['contract_addresses'] and 'contract' in address_entry['tags']: - if 'factory' in address_entry['tags']: - address_entry['tags'].remove('factory') - approved_entries.append(address_entry) - continue - print(f"Removing {address} from {slug} because it's not in Dune.") - - for entry in approved_entries: - if entry.get('name', None) is None: - entry.pop('name', None) - - project_yaml['blockchain'] = approved_entries - update_yaml_data([project_yaml]) - - -if __name__ == "__main__": - - # uncomment these lines to perform preprocessing - #sql_to_json_spellbook('data/dune_exports/') - #diff_against_previous_run('data/dune_exports/', 'data/dune_exports/archive/') - # addresses = consolidate_addresses('data/dune_exports/') - # with open('data/dune_exports/addresses.json', 'w') as outfile: - # json.dump(addresses, outfile, indent=4) - - # update the YAML files - process_updates('data/dune_exports/') - diff --git a/indexer/utilities/address-indexers/src/etherscan_deployer_trace.py b/indexer/utilities/address-indexers/src/etherscan_deployer_trace.py deleted file mode 100644 index 765a2375f..000000000 --- a/indexer/utilities/address-indexers/src/etherscan_deployer_trace.py +++ /dev/null @@ -1,127 +0,0 @@ -# experimental: etherscan-based approach of grabbing all contracts deployed by a project's address - -import json -import os -import requests -from dotenv import load_dotenv -from web3 import Web3 -from address_tagging import is_eoa, fetch_contract_name -from ossd import get_yaml_data_from_path, map_addresses_to_slugs -import time - -load_dotenv() -ALCHEMY_API_KEY = os.environ['ALCHEMY_API_KEY'] - -APIS = { - 'optimism': { - 'etherscan': f'https://api-optimistic.etherscan.io/api', - 'etherscan_api_key': os.getenv("OP_ETHERSCAN_API_KEY"), - 'alchemy': f'https://opt-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}' - }, - 'mainnet': { - 'etherscan': 'https://api.etherscan.io/api', - 'etherscan_api_key': os.getenv("ETHERSCAN_API_KEY"), - 'alchemy': f'https://eth-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}' - } -} -DEFAULT_API = APIS['mainnet'] -CONTRACT_CREATORS_DATA = "data/contract_creators/optimism.json" -PROJECT_SLUGS_DATA = "data/contract_creators/project_slugs.json" - - -def load_contract_creators_data(): - with open(CONTRACT_CREATORS_DATA, "r") as json_file: - data = json.load(json_file) - return data - - -def get_deploys_from_address(address, chain="optimism"): - try: - api = APIS.get(chain, DEFAULT_API) - url = api['etherscan'] - api_key = api['etherscan_api_key'] - params = { - 'module': 'account', - 'action': 'txlist', - 'address': address, - 'apikey': api_key - } - response = requests.get(url, params=params) - response_json = response.json() - if response_json.get('status') != '1': - print(f"Error looking up transactions at address {address} on {chain}") - print(response.text) - return None - - deployments = [tx['contractAddress'] for tx in response_json['result'] if not tx['to'] and tx['input'] and tx['isError'] == '0'] - return deployments - except Exception as e: - print(f"\n\n** Fatal error looking up transactions at address {address} on {chain}\n\n") - print(e) - return None - - -def load_project_mapping(): - with open(PROJECT_SLUGS_DATA, "r") as json_file: - data = json.load(json_file) - return data - - -def main(): - contract_creators_data = load_contract_creators_data() - project_mapping = load_project_mapping() - - address_mapping = {} - try: - for addr, project in contract_creators_data.items(): - print(f"\nProcessing {addr} for {project}...") - slug = project_mapping.get(project) - if not slug: - print(f"Skipping {addr} for {project} because no slug was found.") - continue - - if project == "Gnosis Safe": - continue - - if slug not in address_mapping: - address_mapping[slug] = {"slug": slug, "blockchain": []} - - if addr in [a['address'] for a in address_mapping[slug]["blockchain"]]: - print(f"Skipping {addr} for {project} because it's already been added.") - continue - - is_eoa_addr = is_eoa("optimism", addr) - creator = { - "address": addr, - "tags": ["eoa" if is_eoa_addr else "contract"], - "name": fetch_contract_name("optimism", addr) if not is_eoa_addr else None - } - - blockchain_data = [creator] - deploys = get_deploys_from_address(addr) - time.sleep(1) - if deploys: - creator["tags"].append("deployer") - for deploy_address in deploys: - time.sleep(0.5) - contract_name = fetch_contract_name("optimism", deploy_address) - contract_deploys = get_deploys_from_address(deploy_address) - blockchain_data.append({ - "address": deploy_address, - "tags": ["contract", "deployer"] if contract_deploys else ["contract"], - "name": contract_name - }) - - address_mapping[slug]["blockchain"].extend(blockchain_data) - with open("data/contract_creators/optimism_address_mapping.json", "w") as json_file: - json.dump(address_mapping, json_file, indent=4) - - except: - print(f"Error at project {project} with address {addr}") - - with open("data/contract_creators/optimism_address_mapping.json", "w") as json_file: - json.dump(address_mapping, json_file, indent=4) - - -if __name__ == "__main__": - main() diff --git a/indexer/utilities/address-indexers/src/get_allo_projects.py b/indexer/utilities/address-indexers/src/get_allo_projects.py deleted file mode 100644 index 51b864d8d..000000000 --- a/indexer/utilities/address-indexers/src/get_allo_projects.py +++ /dev/null @@ -1,151 +0,0 @@ -import argparse -import json -import pandas as pd -import requests - -from address_tagging import is_eoa, fetch_contract_name - - -# https://github.com/gitcoinco/allo-indexer -CHAINSAUCE_URL = "https://indexer-grants-stack.gitcoin.co/data/" -CHAIN_IDS = { - '1': 'mainnet', - '10': 'optimism', - '250': 'fantom', - '42161': 'arbitrum one', - '424': 'pgn' -} - -def get_rounds(chain_num): - - round_url = "/".join([CHAINSAUCE_URL, chain_num, "rounds.json"]) - r = requests.get(round_url) - round_data = r.json() - - funding_round_data = [] - for r in round_data: - if r.get('metadata') and r['metadata'].get('name'): - funding_round_data.append({ - "roundId": r['id'], - "roundName": r['metadata']['name'], - }) - else: - print(f"Skipping round {r['id']} on chain {chain_num} because it has no metadata.") - return funding_round_data - - -def get_projects(chain_num, funding_round_data): - - projects_data = [] - for funding_round in funding_round_data: - round_id = funding_round['roundId'] - round_name = funding_round['roundName'] - print(f"Gathering projects data for round: {round_name}...") - - url = "/".join([CHAINSAUCE_URL, chain_num, "rounds", round_id, "applications.json"]) - projects_json = requests.get(url).json() - - for project in projects_json: - - if project['status'] != "APPROVED": - continue - - if not project.get('metadata'): - continue - - name = project['metadata']['application']['project']['title'] - address = project['metadata']['application']['recipient'] - github = project['metadata']['application']['project'].get('projectGithub', None) - - projects_data.append({ - 'name': name, - 'address': address, - 'projectGithub': github, - 'fundingRounds': round_name, - 'chain': chain_num - }) - print(f"Normalized data for project: {name}") - - print(f"Obtained {len(projects_data)} projects on Chain {chain_num}.") - - return projects_data - - -def get_all_projects_in_round(chain_nums): - - all_projects = [] - for chain_id in chain_nums: - funding_round_data = get_rounds(chain_id) - all_projects.extend(get_projects(chain_id, funding_round_data)) - - df = pd.DataFrame(all_projects) - return df - - -def github_handle_to_url(handle): - if not handle or not isinstance(handle, str) or 'gitcoinco' in handle: - return None - handle = handle.strip() - if len(handle) < 3: - return None - if "https://github.com" in handle: - return handle - return f"https://github.com/{handle}" - - -def get_oss_projects(df_path): - - df = pd.read_csv(df_path) - df['projectGithub'] = df['projectGithub'].apply(github_handle_to_url) - df = df[df['projectGithub'].notnull()] - df['chainAddress'] = df.apply(lambda x: f"{x['chain']}: {x['address']}", axis=1) - df.drop_duplicates(subset=['projectGithub', 'chainAddress'], inplace=True) - df['chain'] = df['chain'].apply(lambda x: CHAIN_IDS[str(x)]) - - githubs = df['projectGithub'].unique().tolist() - project_data = [] - for gh in githubs: - dff = df[df['projectGithub'] == gh] - addresses = dff.groupby('chain')['address'].apply(list) - addresses_dict = {} - for chain, address_list in addresses.items(): - for addr in address_list: - tags = ['wallet'] - eoa = is_eoa(chain, addr) - if eoa: - tags.append('eoa') - else: - contract = fetch_contract_name(chain, addr) - if contract and ("Safe" in contract or contract == "Proxy"): - tags.append('safe') - if addr not in addresses_dict: - addresses_dict[addr] = { - "tags": tags, - "networks": [chain] - } - else: - addresses_dict[addr]['networks'].append(chain) - p = {'name': dff['name'].iloc[0], 'github': {"url": gh}, **addresses_dict} - project_data.append(p) - return project_data - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Get all OSS projects.') - parser.add_argument('--reindex', dest='reindex', action='store_true', help='Whether to reindex data') - parser.add_argument('--csv_output', dest='csv_output', required=True, help='Path to save the CSV output') - parser.add_argument('--json_output', dest='json_output', required=True, help='Path to save the JSON output') - - args = parser.parse_args() - - if args.reindex: - df = get_all_projects_in_round(CHAIN_IDS.keys()) - df.to_csv(args.csv_output, index=False) - - project_data = get_oss_projects(args.csv_output) - with open(args.json_output, 'w') as outfile: - json.dump(project_data, outfile, indent=4) - - # test - # python src/get_allo_projects.py --reindex --csv_output=data/allo/projects.csv --json_output=data/allo/oss-projects.json - # python src/get_allo_projects.py --csv_output=data/allo/projects.csv --json_output=data/allo/oss-projects.json \ No newline at end of file diff --git a/indexer/utilities/address-indexers/src/op_govgrants_parser.py b/indexer/utilities/address-indexers/src/op_govgrants_parser.py deleted file mode 100644 index 7a4fe3e56..000000000 --- a/indexer/utilities/address-indexers/src/op_govgrants_parser.py +++ /dev/null @@ -1,264 +0,0 @@ -import json -from fuzzywuzzy import fuzz, process -import yaml - -from address_tagging import is_eoa, fetch_contract_name -from ossd import ( - get_yaml_data_from_path, - map_addresses_to_slugs, - map_repos_to_slugs, - map_slugs_to_names, - map_slugs_to_project_data, - update_yaml_data, - make_template -) -from yaml_formatter import dump - - -GOVGRANTS_DATA_PATH = "data/govgrants/Optimism_GovFund_PublicTracker.json" - -def load_grants_data(): - with open(GOVGRANTS_DATA_PATH) as f: - grants_data = json.load(f) - - if not any("Address Tags" in grant for grant in grants_data): - tag_grant_addresses(grants_data) - - return grants_data - - -def tag_grant_addresses(grants): - - for grant in grants: - if "Address Tags" in grant: - continue - - # handle some special cases - if address[0] == "x": - address = "0" + address - elif address == "noahlitvin.eth": - address = "0x07Aeeb7E544A070a2553e142828fb30c214a1F86" - - address = grant['Address'].lower().strip() - if address[:2] != "0x": - continue - if " " in address: - print(f"Splitting address for {grant['Project Name']} to {address}") - address = address.split(" ")[0] - grant.update({"Address": address}) - - if is_eoa('optimism', address): - grant.update({"Address Tags": ["eoa", "wallet"]}) - else: - contract_name = fetch_contract_name('optimism', address) - if not isinstance(contract_name, str): - print(f"Found unknown contract at {address}.") - grant.update({"Address Tags": ["wallet"]}) - elif "Proxy" in contract_name or "MultiSig" in contract_name: - grant.update({"Address Tags": ["safe", "wallet"]}) - else: - print(f"Found other contract type at {address}: {contract_name}") - grant.update({"Address Tags": ["wallet"]}) - - with open(GOVGRANTS_DATA_PATH, "w") as f: - json.dump(grants, f, indent=4) - - -def find_closest_match(project_name, names_dict, max_matches=10, fuzz_threshold=80): - - settings = dict(scorer=fuzz.token_set_ratio, limit=max_matches) - names = list(names_dict.values()) - matches = process.extract(project_name, names, **settings) - filtered_matches = [(name, score) for name, score in matches if score >= fuzz_threshold] - matches_dict = {name: [k for k, v in names_dict.items() if v == name] for name, _ in filtered_matches} - return matches_dict - - -def parse_grants_data(last_slug=None): - - grants_data = load_grants_data() - yaml_data = get_yaml_data_from_path() - template = make_template(yaml_data) - - addresses = map_addresses_to_slugs(yaml_data, chain='optimism') - repos = map_repos_to_slugs(yaml_data) - names = map_slugs_to_names(yaml_data) - mapping = map_slugs_to_project_data(yaml_data) - - slug_found = (last_slug is None) - for project in grants_data: - - name = project['Project Name'] - address = project['Address'] - - if last_slug and addresses.get(address, None) == last_slug: - slug_found = True - elif not slug_found: - continue - - # case 1: grant address is already in YAML, confirm it has the right tags - slug = addresses.get(address, None) - if slug: - print(f"Found {name}'s address at {slug} in OSSD.") - update = False - data = mapping[slug] - for addr in data['blockchain']: - if addr['address'] == address: - if 'optimism' not in addr['networks']: - addr['networks'].append('optimism') - update = True - if set(project['Address Tags']) != set(addr['tags']): - addr['tags'] = list(set(addr['tags'] + project['Address Tags'])) - update = True - break - if update: - update_yaml_data([data]) - continue - - print(f"\nFound no existing project for {name} at {address}.") - print(f"Here is the proposal link, FYI: {project['Link']}") - - # case 2: grant address is not in YAML, but there appears to be an existing project already - potential_names = find_closest_match(name, names) - if potential_names: - print(f"Here are some similar-named projects and their slugs: {potential_names}") - else: - print("There aren't any projects with similar names.") - print("Enter a slug for the project if there's a good match:") - slug = input() - data = mapping.get(slug, None) - if data: - data['blockchain'].append( - { - "address": address, - "networks": ["optimism"], - "tags": project['Address Tags'] - } - ) - update_yaml_data([data]) - continue - - # case 3: grant address is not in YAML, check if a new project needs to be created - print("Do you want to create a new project? (y/n/q)") - status = input() - if status.lower() == 'q': - return False - elif status.lower() != 'y': - continue - - print("Enter a GitHub repo url for the project:") - repo = input() - if repos.get(repo, None): - print(f"Found {repo} in OSSD.") - data = mapping[repos[repo]] - data['blockchain'].append( - { - "address": address, - "networks": ["optimism"], - "tags": project['Address Tags'] - } - ) - update_yaml_data([data]) - continue - - if repo: - data = template.copy() - data['github'] = [{"url": repo}] - data['blockchain'] = [{ - "address": address, - "networks": ["optimism"], - "tags": project['Address Tags'] - }] - slug = repo.split('/')[-1].lower() - print(f"Would you like to use the slug {slug}? (y/n)") - status = input() - if status.lower() == 'y': - data['slug'] = slug - else: - print("Enter a slug for the project:") - slug = input() - data['slug'] = slug - - print(f"Would you like to use the name {name}? (y/n)") - status = input() - if status.lower() == 'y': - data['name'] = name - else: - print("Enter a name for the project:") - name = input() - data['name'] = name - - print(f"Would you like to export to a YAML file? (y/n/q)") - status = input() - if status.lower() == 'q': - return False - if status.lower() == 'y': - update_yaml_data([data]) - continue - else: - continue - - -def create_collection_yaml(collection_name, list_of_projects): - - collection_slug = collection_name.lower().replace(" ", "-") - collection_data = { - "version": 3, - "slug": collection_slug, - "name": collection_name, - "projects": list_of_projects - } - - yaml_path = f"data/govgrants/{collection_slug}.yaml" - dump(collection_data, yaml_path) - print("Dumped collection to", yaml_path) - - -def make_collection_from_addresses(address_mapping, grants_data, collection_name): - - collection = [] - for project in grants_data: - name = project['Project Name'] - address = project['Address'].lower() - tags = project['Tags'] - if collection_name in tags: - slug = address_mapping.get(address, None) - if slug: - collection.append(slug) - else: - print(f"Could not find {name} at {address} in OSSD.") - - collection = sorted(list(set(collection))) - collection_name = "OP GovGrants " + collection_name - create_collection_yaml(collection_name, collection) - - -def generate_collection_files(): - - yaml_data = get_yaml_data_from_path() - grants_data = load_grants_data() - addresses = map_addresses_to_slugs(yaml_data, chain='optimism') - - collection_tags = [] - all_projects = [] - for project in grants_data: - name = project['Project Name'] - address = project['Address'].lower() - slug = addresses.get(address, None) - if slug: - all_projects.append(slug) - - tags = project['Tags'] - if tags: - collection_tags.extend(tags) - - collection_tags = sorted(list(set(collection_tags))) - for tag in collection_tags: - make_collection_from_addresses(addresses, grants_data, tag) - - all_projects = sorted(list(set(all_projects))) - create_collection_yaml("OP GovGrants", all_projects) - - -parse_grants_data() -generate_collection_files() \ No newline at end of file diff --git a/indexer/utilities/address-indexers/src/op_govgrants_reader.py b/indexer/utilities/address-indexers/src/op_govgrants_reader.py deleted file mode 100644 index 3fc340619..000000000 --- a/indexer/utilities/address-indexers/src/op_govgrants_reader.py +++ /dev/null @@ -1,195 +0,0 @@ -""" -Run this script on an excel file to generate a json with all grant data -""" - -import openpyxl -import pandas as pd - -# download from the source Google Sheet -inputExcelFile = "data/govgrants/Optimism GovFund Grants_ Public Distribution Tracking.xlsx" -outputJsonFile = "data/govgrants/Optimism_GovFund_PublicTracker.json" - -# assignments for normalizing the data -mappings = [ - { - "sheet": "Status Key", - "ignore": True - }, - { - "sheet": "Grants Season 1", - "mapping": { - "Project Name": "Project Name", - "Status": "Status", - "Distribution Date": "Date", - "Amount (OP)": "Amount", - "L2 Address": "Address", - "Proposal Link": "Link" - }, - "fills": ["Cycle #"], - "tags": { - "Cycle #": { - "Cycle 1 (Phase 0)": "Cycle 1 (Phase 0)", - "Cycle 2 (Phase 1)": "Cycle 2 (Phase 1)", - "Cycle 3 (Phase 1)": "Cycle 3 (Phase 1)", - "Cycle 4 (Phase 1)": "Cycle 4 (Phase 1)" - }, - "Incentive Program Launched?": {"Yes": "Incentive Program Launched"} - } - }, - { - "sheet": "Grants Season 2", - "mapping": { - "Project Name": "Project Name", - "Status": "Status", - "Distribution Date": "Date", - "Amount (OP)": "Amount", - "L2 Address": "Address", - "Proposal Link": "Link" - }, - "fills": ["Cycle #"], - "tags": { - "Cycle #": { - "Cycle 6 (Phase 1)": "Cycle 6 (Phase 1)", - "Cycle 7 (Phase 1)": "Cycle 7 (Phase 1)", - "Cycle 8 (Phase 1)": "Cycle 8 (Phase 1)", - }, - "Incentive Program Launched?": {"Yes": "Incentive Program Launched"} - } - }, - { - "sheet": "Grants Season 3", - "skipRows": 1, - "mapping": { - "Project Name": "Project Name", - "Status": "Status", - "Initial Distribution Date": "Date", - "Total Amount (OP)": "Amount", - "L2 Address": "Address", - "Proposal Link": "Link" - }, - "fills": ["Cycle #"], - "tags": { - "Cycle #": { - "Cycle 10": "Cycle 10", - "Cycle 11": "Cycle 11" - }, - "Incentive Program Launched?": {"Builders": "Builders Grant"} - } - }, - { - "sheet": "Grants Season 4", - "skipRows": 1, - "mapping": { - "Project Name": "Project Name", - "Status": "Status", - "Initial Distribution Date": "Date", - "Total Amount (OP)": "Amount", - "L2 Address": "Address", - "Proposal Link": "Link" - }, - "fills": ["Cycle #"], - "tags": { - "Cycle #": { - "Cycle 13": "Cycle 13", - "Cycle 14": "Cycle 14", - "Cycle 15": "Cycle 15" - }, - "Incentive Program Launched?": { - "Builders": "Builders Grant", - "Growth": "Growth Grant", - "RFG1": "RFG1", - "RFG2": "RFG2", - "RFG3": "RFG3", - "RFG4": "RFG4", - "RFG5": "RFG5", - "RFG6": "RFG6", - "RFG7": "RFG7", - "RFG8": "RFG8" - } - } - }, - { - "sheet": "Missions Season 4", - "mapping": { - "Project Name": "Project Name", - "Status": "Status", - "Distribution Date": "Date", - "Amount (OP)": "Amount", - "L2 Address": "Address", - "Proposal Link": "Link" - }, - "fills": ["Cycle #"], - "tags": { - "Cycle #": { - "Cycle 13": "Cycle 13" - }, - "Intent": { - "Intent 1": "Intent 1", - "Intent 2": "Intent 2", - "Intent 3": "Intent 3", - "Intent 4": "Intent 4" - }, - "Trust Tier": { - "Fledgling": "Fledgling", - "Phoenix": "Phoenix", - "Ember": "Ember" - }, - "Incentive Program Launched?": { - "Builders": "Builders Grant" - } - } - } -] - -# load the excel file -newWorkbook = openpyxl.load_workbook(inputExcelFile) -sheetNames = newWorkbook.sheetnames - -# processing script for reading each sheet -def process(sheet_num): - - sheetData = mappings[sheet_num] - sheetName = sheetData.get('sheet') - skipRows = sheetData.get('skipRows', 0) - mapping = sheetData.get('mapping', {}) - fills = sheetData.get('fills', []) - tags = sheetData.get('tags', {}) - - df = pd.read_excel(inputExcelFile, sheet_name=sheetName, skiprows=skipRows) - df.columns = [c.strip() for c in df.columns] - - for fillCol in fills: - df[fillCol].fillna(method='ffill', inplace=True) - - tagValues = [] - for _, row in df.iterrows(): - thisRowTags = [sheetName] - for tagCol, tapMapping in tags.items(): - for key, val in tapMapping.items(): - if row[tagCol] == key: - thisRowTags.append(val) - #tagValues.append(",".join(thisRowTags)) - tagValues.append(thisRowTags) - - df = df[mapping.keys()].rename(columns=mapping) - df['Tags'] = tagValues - - df['Date'] = pd.to_datetime(df['Date'], errors='coerce').fillna(0) - df['Amount'] = pd.to_numeric(df['Amount'], errors='coerce') - df['Status'].fillna("Unknown", inplace=True) - - df = df[(~df['Amount'].isna()) & (~df['Address'].isna())] - - return df - -# process each sheet -dfs = [] -for i in range(len(sheetNames)): - if mappings[i].get('ignore'): - continue - print(mappings[i]['sheet']) - dfs.append(process(i)) - -# concat and dump to json -df = pd.concat(dfs, ignore_index=True, axis=0) -df.to_json(outputJsonFile, indent=4, orient='records') \ No newline at end of file diff --git a/indexer/utilities/address-indexers/src/ossd.py b/indexer/utilities/address-indexers/src/ossd.py deleted file mode 100644 index f1f7166cc..000000000 --- a/indexer/utilities/address-indexers/src/ossd.py +++ /dev/null @@ -1,177 +0,0 @@ -from dotenv import load_dotenv -import json -import os -import yaml -from yaml_formatter import dump - - -load_dotenv() -LOCAL_PATH = os.getenv("LOCAL_PATH_TO_OSSD") - - -def get_yaml_files(path): - yaml_files = [] - for root, dirs, files in os.walk(path): - for file in files: - if file.endswith(".yaml"): - yaml_files.append(os.path.join(root, file)) - return yaml_files - - -def get_yaml_data(yaml_files): - yaml_data = [] - for file in yaml_files: - with open(file, 'r') as stream: - try: - data = yaml.safe_load(stream) - if data: - yaml_data.append(data) - except yaml.YAMLError as exc: - print(f"Error in {file}: {exc}") - return yaml_data - - -def serialize_yaml_data(outpath): - yaml_data = get_yaml_data_from_path() - data = map_slugs_to_project_data(yaml_data) - with open(outpath, 'w') as f: - json.dump(data, f, indent=4) - print("Dumped yaml data to", outpath) - - -def get_yaml_data_from_path(): - yaml_files = get_yaml_files(LOCAL_PATH) - if not yaml_files: - print("No YAML files found.") - return [] - - print(f"Found {len(yaml_files)} yaml files.") - yaml_data = get_yaml_data(yaml_files) - print(f"Ingested {len(yaml_data)} yaml records.") - return yaml_data - - -def update_yaml_data(yaml_data): - print(f"Exporting {len(yaml_data)} yaml records to {LOCAL_PATH}.") - for data in yaml_data: - if not data: - continue - slug = data['slug'] - path = os.path.join(LOCAL_PATH, slug[0], slug + ".yaml") - dump(data, path) - - -def map_addresses_to_slugs(yaml_data, chain, lowercase=True): - """ - Returns a mapping of addresses to slugs. - Example: - { - "0x1234...": "project-slug" - } - """ - addresses = {} - for data in yaml_data: - if not data: - continue - slug = data['slug'] - blockchain_entries = data.get('blockchain', []) - if not blockchain_entries: - continue - for entry in blockchain_entries: - if chain not in entry.get('networks', []): - continue - address = entry.get('address', None) - if address: - if lowercase: - address = address.lower() - addresses[address] = slug - return addresses - - -def map_repos_to_slugs(yaml_data, lowercase=True): - """ - Returns a mapping of github repo urls to slugs. - Example: - { - "https://github.com/my-repo": "project-slug" - } - """ - repos = {} - for data in yaml_data: - if not data: - continue - slug = data['slug'] - repo_entries = data.get('github', []) - if not repo_entries: - continue - for entry in repo_entries: - url = entry.get('url', None) - if url: - if lowercase: - url = url.lower() - repos[url] = slug - return repos - - -def map_slugs_to_names(yaml_data): - """ - Returns a mapping of slugs to names. - Example: - { - "project-slug": "My Project" - } - """ - mapping = {} - for data in yaml_data: - if not data: - continue - slug = data['slug'] - name = data['name'] - mapping[slug] = name - return mapping - - -def map_slugs_to_project_data(yaml_data): - """ - Returns a mapping of slugs to project data. - Example: - { - "project-slug": { - "name": "My Project", - "github": "https://github.com/my-repo" - } - } - """ - mapping = {} - for data in yaml_data: - if not data: - continue - slug = data['slug'] - mapping[slug] = data - return mapping - - -def make_template(yaml_data): - temp = yaml_data[0] - template = { - k: [] if isinstance(v, list) else None - for k,v in temp.items() - } - template['version'] = temp.get('version', None) - return template - - -def test(): - yaml_data = get_yaml_data_from_path() - #update_yaml_data(yaml_data) - addresses = map_addresses_to_slugs(yaml_data, chain='optimism') - repos = map_repos_to_slugs(yaml_data) - names = map_slugs_to_names(yaml_data) - mapping = map_slugs_to_project_data(yaml_data) - template = make_template(yaml_data) - serialize_yaml_data('data/ossd.json') - print("YAML template:") - print(template) - - -#test() \ No newline at end of file diff --git a/indexer/utilities/address-indexers/src/yaml_formatter.py b/indexer/utilities/address-indexers/src/yaml_formatter.py deleted file mode 100644 index 7009362ba..000000000 --- a/indexer/utilities/address-indexers/src/yaml_formatter.py +++ /dev/null @@ -1,41 +0,0 @@ -import yaml - - -class MyDumper(yaml.Dumper): - - def __init__(self, *args, **kwargs): - super(MyDumper, self).__init__(*args, **kwargs) - self.add_representer(QuotedString, quoted_string_representer) - - def ignore_aliases(self, data): - return True - - def increase_indent(self, flow=False, indentless=False): - return super(MyDumper, self).increase_indent(flow, False) - - -class QuotedString(str): - pass - - -def quoted_string_representer(dumper, data): - return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='"') - - -def traverse_and_quote(data, key_to_quote): - if isinstance(data, dict): - for key, value in data.items(): - if key == key_to_quote and isinstance(value, str): - data[key] = QuotedString(value) - else: - traverse_and_quote(value, key_to_quote) - elif isinstance(data, list): - for item in data: - traverse_and_quote(item, key_to_quote) - - -def dump(yaml_dict, outpath): - traverse_and_quote(yaml_dict, 'address') - formatters = dict(default_flow_style=False, sort_keys=False, indent=2) - with open(outpath, 'w') as outfile: - yaml.dump(yaml_dict, outfile, Dumper=MyDumper, **formatters) diff --git a/indexer/utilities/contract_mapper/README.md b/indexer/utilities/contract_mapper/README.md deleted file mode 100644 index 0a50ecc1c..000000000 --- a/indexer/utilities/contract_mapper/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Smart Contract Mapper - -- App to simplify linking smart contracts to github repos -- Runs locally on your machine - -## Data - -- Contract mappings - - - [Optimism](https://github.com/duneanalytics/spellbook/blob/main/models/contracts/optimism/contracts_optimism_contract_creator_address_list.sql) - -- Github repos - - [Electric Capital](https://github.com/electric-capital/crypto-ecosystems) diff --git a/indexer/utilities/contract_mapper/app.py b/indexer/utilities/contract_mapper/app.py deleted file mode 100644 index 991abc194..000000000 --- a/indexer/utilities/contract_mapper/app.py +++ /dev/null @@ -1,103 +0,0 @@ -from flask import Flask, render_template, request, redirect, url_for -import json -from fuzzywuzzy import fuzz, process -from collections import defaultdict -import os - -JSON_PATH = 'data/protocol_mapping.json' -GITHUB_DATA_PATH = 'data/crypto_ecosystems.json' - -app = Flask(__name__) - - -def flatten_github_data(projects): - flattened_orgs = [] - for project in projects: - github_orgs = project.get('github_organizations', []) - flattened_orgs.extend(github_orgs) - flattened_orgs = set(flattened_orgs) - orgs = [x.replace('https://github.com/','').lower() for x in flattened_orgs] - return orgs - - -def find_closest_matches(name, orgs, num_matches=10): - matches = [(org,100) for org in orgs if name.lower() in org.lower()] - closest_matches = process.extract(name, orgs, limit=num_matches, scorer=fuzz.token_sort_ratio) - matches.extend(closest_matches) - matches.sort(key=lambda x: x[1], reverse=True) - repo_names = [] - for repo,_ in matches: - if repo not in repo_names: - repo_names.append(repo) - return repo_names - - -@app.route('/', methods=['GET', 'POST']) -def home(): - with open(JSON_PATH) as f: - data = json.load(f) - - with open(GITHUB_DATA_PATH) as f: - github_data = json.load(f) - - github_orgs = flatten_github_data(github_data) - - # Compute summary stats - total_projects = len(data) - projects_with_actual_repo = sum('actual_repo' in entry for entry in data) - projects_without_actual_repo = total_projects - projects_with_actual_repo - - # Filter out projects that already have an 'actual_repo' - projects = sorted([entry['project'] for entry in data if 'actual_repo' not in entry], key=str.lower) - - if request.method == 'POST': - project_name = request.form.get('project') - repo_name = request.form.get('repo') - if project_name and repo_name and repo_name != 'unknown': - for project in data: - if project['project'] == project_name: - project['actual_repo'] = repo_name - break - elif repo_name == 'unknown': - repo_name = request.form.get('custom_repo') - if repo_name: # if custom_repo is not blank - for project in data: - if project['project'] == project_name: - project['actual_repo'] = repo_name - break - with open(JSON_PATH, 'w') as f: - json.dump(data, f, indent=4) - - # Redirect to the next project without 'actual_repo' - if project_name in projects: - current_index = projects.index(project_name) - try: - next_project = projects[current_index + 1] - except IndexError: - next_project = None # if we're at the end of the list - else: - next_project = None # if the current project isn't in the list - return redirect(url_for('home', project=next_project)) # if next_project is None, we will be redirected to the homepage - - if request.method == 'GET' and 'project' in request.args: - selected_project = request.args['project'] - repos = find_closest_matches(selected_project, github_orgs) - selected_repo = request.args.get('repo', '') - else: - selected_project = None - repos = [] - selected_repo = '' - - return render_template( - 'home.html', - projects=projects, - repos=repos, - selected_project=selected_project, - selected_repo=selected_repo, - github_link='https://github.com/' + selected_repo if selected_repo else '#', projects_with_actual_repo=projects_with_actual_repo, - projects_without_actual_repo=projects_without_actual_repo - ) - - -if __name__ == '__main__': - app.run(debug=True, host='0.0.0.0', port=int(os.getenv('PORT', 4444))) diff --git a/indexer/utilities/contract_mapper/data/op_contract_mapping.yaml b/indexer/utilities/contract_mapper/data/op_contract_mapping.yaml deleted file mode 100644 index 747df5dd2..000000000 --- a/indexer/utilities/contract_mapper/data/op_contract_mapping.yaml +++ /dev/null @@ -1,552 +0,0 @@ -'0x932607335869cff6349ef450e74c83a3b871a9ff': Lyra -'0x41a742d0cb523b0c313518309ade763fb609da25': Lyra -'0x924ac9910c09a0215b06458653b30471a152022f': Hop Protocol -'0xfefec7d3eb14a004029d278393e6ab8b46fb4fca': Hop Protocol -'0x0e0e3d2c5c292161999474247956ef542cabf8dd': Hop Protocol -'0x3c0ffaca566fccfd9cc95139fef6cba143795963': Hop Protocol -'0xec4b41af04cf917b54aeb6df58c0f8d78895b5ef': Hop Protocol -'0x7ed52863829ab99354f3a0503a622e82acd5f7d3': Rari -'0x44045fd5d3840fec51b76fb6a87cbcda735a8629': Slingshot -'0x3204ac6f848e05557c6c7876e09059882e07962f': Rubicon -'0xee4f7b6c39e7e87af01fb9e4cee0c893ff4d63f2': 1Inch -'0x11799622f4d98a24514011e8527b969f7488ef47': 1Inch -'0x74886e43273e66da073d26d91cdb4f0eb91fc420': 1Inch -'0x7a3d05c70581bd345fe117c06e45f9669205384f': Rainbow -'0x075da589886ba445d7c7e81c472059de7ae65250': MakerDAO -'0x395ec94bf3e3dcd3afbd82cd03197731411e396b': Synthetix -'0x3c05b1239b223c969540fefc0270227a2b00e047': Synthetix -'0x6c9fc64a53c1b71fb3f9af64d1ae3a4931a5f4e9': Uniswap -'0xdead1cb30b3ca13cd67d1d6f4e2790d12484fdd8': Chainlink -'0x03863f6ad36f1fcd908517e3c56c6b3fd3f50752': Chainlink -'0x1e4e0e7dd2a854ea15f4ee852abb78a99c86357c': Uniswap -'0xced01e7617bf6052a71154daa6d50d8b243f1b26': Uniswap -'0xd640037fa41436326e4a0e3fd0511aad83d2345c': Xchain -'0x1b9dfc56e38b0f92448659c114e2347bd803911c': Celer -'0x9a8f92a830a5cb89a3816e3d267cb7791c16b04d': Across -'0x155b15a7e9ff0e34ceaf2439589d5c661adc9493': Connext -'0xc715aa67866a2fef297b12cb26e953481aed2df4': dHedge -'0xf8c3875bfa461a38532fedf90453985901c55114': Dope Wars -'0x42d4cb9514710d8b90e790aa80ac3cc635b66589': Optimistic Bunnies -'0xda32bd8e86575f827f0d36272f703d5370c06152': OptiPunks -'0x0af91fa049a7e1894f480bfe5bba20142c6c29a9': Synapse -'0x22cdc93f53ee3f6b8ad66fad6f98915a5349950e': Synapse -'0xa67b7147dce20d6f25fd9abfbcb1c3ca74e11f0b': Synapse -'0xa9e90579eb086bcda910dd94041ffe041fb4ac89': Synapse -'0xa430a8a6911e8de02acfe835eb1d5539a58cee94': Flux Protocol -'0x0e860f44d73f9fdbaf5e9b19afc554bf3c8e8a57': Poly Network -'0xe1cb04a0fa36ddd16a06ea828007e35e1a3cbc37': Gnosis Safe -'0xab0d90fde397a4362acbe1d3a1c17f24b5b53266': Gnosis Safe -'0x3e579180cf01f0e2abf6ff4d566b7891fbf9b868': BitBTC Protocol -'0xd15d5d0f5b1b56a4daef75cfe108cb825e97d015': Superfluid -'0xbabe61887f1de2713c6f97e567623453d3c79f67': Curve -'0x7eeac6cddbd1d0b8af061742d41877d7f707289a': Curve -'0x763b9dba40c3d03507df454823fe03517f84a5ab': WePiggy -'0x85671317830357f40188aa6a55e96a3338eb4d7d': Volmex -'0xee9801669c6138e84bd50deb500827b776777d28': O3 Swap -'0xe37da1e4632b94e601ac015be8db554e0456b01a': EtherOrcs -'0x849a19c0746fb0d335e02dec0d0b3e057e585176': Perpetual Protocol -'0x6c3f14da26556585706c02af737a44e67dc6954d': Go Pocket -'0x27b2458658f036838c2050b2b35e874e57fe4789': Diamondfoot NFT -'0x98ab60422568db7d58623c69cb41d29c9dea1ce6': NiftyKit -'0x644b37ea8027b81ef1bdcd10ac0a78ea8532b7c8': Nuke Vaults -'0xb56238d0225c7e6155591916167c138a4be2babc': tdao -'0xf3315a3e482a96b8c327674b7a16151dcd899a9a': FeestArb -'0x4b49652fbf286b3da10e44442c38134d841159ef': universexyz -'0xe5cd62ac8d2ca2a62a04958f07dd239c1ffe1a9e': Unlock Protocol -'0x47c8f1db1df960b2ff00b240038576098277e8da': Kwenta -'0xf2cafb99992ce6a49f948b452c783614808612d1': WePiggy -'0x6275d233a12b7cc747e41543878c31b604d0bae1': Test xidian -'0xbc0895f9d50ddcad909f7089fc642e59006a9460': thirdweb -'0x80898b704baa55e7e37f1128fc6ae5836661f54a': Pika Protocol -'0x69bdb276a17dd90f9d3a545944ccb20e593ae8e3': Set Protocol -'0x3af9fe35d280ada5a5edb1bef3ed872a3231d73c': POA -'0x39e5351e6ce3c4b19b8b0a2f5c82c511782457be': Aktionariat -'0xa8e8affc55e6ab42ba4a3e19cb5a3c7adcf0407b': Pods -'0x304c736acd582fc72db1ddff12fc368a685a5897': KratosDAO -'0x6fdbcdb16027b86ab0fa5846e53b1b0952b4580c': Rentable -'0xc0fcf8403e10b65f1d18f1b81b093004b1127275': 88mph -'0x082443883d739faea9bfd590ab550f7184f050c9': Composable Finance -'0x420220b72bbd307db8615e7aa0eadca399cf2fc0': handlefi -'0x2cc3befb75e5b9c04eb90f8167c5c8c8523c30eb': RaptureLabs -'0x0022ec3dd352bf214a9d936081f10ffac66455e1': Terrae -'0x558ef269bcc4cc9f2e14e3f4301231fbeb85d95f': BarnBridge -'0x6861d375afa2c9e5f7d82c176fb50ea70356740a': HungryBunz -'0xafd618064739a2820f5f80c2585563a8af0e6871': SupDucks -'0xab0b18523e8fe8cbf947c55632e8ab5ce936ae8c': Gridzoneio -'0x9f60699ce23f1ab86ec3e095b477ff79d4f409ad': InstaDapp -'0x7754c0584372d29510c019136220f91e25a8f706': FNDZ -'0x88888887c3ebd4a33e34a15db4254c74c75e5d4a': Stability -'0x99910e7d8895248320744b6f9a49f8afb1ad8b31': iOS DeFi Wallet -'0x9d59cf867ec98b67ab05b7f482eb38baf3884058': CST -'0x057fcd7bd66e0b0e7cbf387d12b2a49a20d30922': EPNS -'0xfaed87bc8a4721442d501bb190bd767a0d34c84b': Optimistic Loogies -'0x992dac69827a200ba112a0303fe8f79f03c37d9d': ETHDubai -'0x7126c3c6d04e4a2a89e2b1589a96d37724be9e39': Tally -'0x697ef32b4a3f5a4c39de1cb7563f24ca7bfc5947': Insula -'0x54054ea2db6edc336cb87966815fd66cc337f224': Keep3r Network -'0x3dff16210a6c9ffb7ee50c6eb21bb6675b86adf4': YIN Finance -'0xc7f73196a301948866f457add5eadb961fe05fb3': YIN Finance -'0xe4379a25a0db888e19ec2c14416fe68a62aca5cc': WPSmartContractscom -'0x4816506e22004f8ce02963f5cd9703afb1c394d4': FIDIS FI25 Crypto Index -'0x5b0f8d8f47e3fdf7ee1c337abca19dbba98524e6': Gardens -'0xdbaabc182e5fcebf216c353a3ebe32cdb7390094': Kromatikafinance -'0x74888d40a3523397500afebac92152f4617997b3': realityeth -'0x2c6d58c52accf2a04138c76ee11c1b5a100ef6a5': OptiMarket -'0xef31d75a2f85cfdd9032158a2ceb773c84d79192': dHedge -'0x9841484a4a6c0b61c4eea71376d76453fd05ec9c': Thales -'0x8314125c8b68af2afd0d151eb4a551e88128a2ae': Thales -'0xde6d6f23aabbdc9469c8907ece7c379f98e4cb75': dForce -'0x1b5caa1d3a1582a438e4cd93ee7a7e0e4d5624fb': Uniswap -'0xc73567e09e1774f6e9e5f1f9de7fd0c3c4ce94fa': NFT for my bro -'0xa71405f7d11734f03f2616b93eaed22604c978bf': Mean Finance -'0x0f5b021bb7300a83b7ff74fe39b56908fc0929c3': The Cyber Inu -'0xfdd9c344a52ace735564f1dc1ec19bd507dd5037': TRDEFI -'0xfe64a36b1465dfac28e93add6d8c5ecca816a7d0': HomepageDAO -'0x80094b8ef0b5762661e6ba3daf9d8acb5b0620e6': Imbue -'0x962193e0101ab216f362435a276b22e2172fc847': Gladiaxy -'0x876db660de545a14aa14bb6069e5b369cc233241': 18Decimal -'0x4862eb1658b72555934b91bf9aee142849e736b9': Yeti -'0xa28f0249ec1d48a0022792ec8e708020cb27c815': Crypto Sigma Males NFT -'0x000f05552f24850e75793d38c2bd0cbd249a9ff4': RigoBlock -'0x287bd66753f0ca8e9c2171d782df39e4c28cfd65': Poly Network -'0x037a9d5dd0fd9bc48d699f1010b9ec68bb839e34': CyberFrens -'0x74e5189d4258eaf1236d4fd1454225c0a4b54907': PelicanSwap -'0xd4fb8e266a490103b0515e22c99d6643a288a455': DefiYield -'0xc602aaf1b61fdf7834c4138cd96400738d298807': AllianceBridge -'0x7a68685e4d5d67322a26db639c62ac1460823905': zeroex -'0xdef1c0ded9bec7f1a1670819833240f027b25eff': zeroex -'0xe750ad66de350f8110e305fb78ec6a9f594445e3': zeroex -'0xf8ebddd333b3ab285906fd5eb603b17ecd18266f': zeroex -'0x60908c4037cbb8f3aa040d9fb668ab36280eb871': zeroex -'0x8602ee2f8aaeb671e409b26d48e36dd8cc3b7ed7': ZipSwap -'0xf7c1daf7443d7307df13c81f5f0328d4c7803e7b': BoringDAO -'0x38e63793993ae54be374d129f34a3faf2c382e97': TokenFunder -'0x512472840327530ea03cce6f58966b221f3a8b6a': Perpetual Protocol -'0x56cf1fa9185e42e90205e955e299f33b6204da59': DoraHacks -'0x6336cf6f9a7abb9efa86c04ac29541f015dd58b1': XmasBook -'0xb5bb09ecaecb7fca6c837ab706ce321b3a3dd949': VaultDefi -'0x6a932f0ae2a7a49fb24b70c8eef6ec0808163345': PhotoDAO -'0x5b9319b2cd3d702ba12398c8e7515979276c038d': PhotoDAO -'0x9986ee0c3eea15dcf8642b56179652f9589b95db': OpenOcean -'0x992ebe8be326ea0cd7d30a98740d9899612330bb': Band Protocol -'0x6a885bd0086368b56dbf2005bb72bbcc5fd7e2b9': Band Protocol -'0x21ffcbdbdd2c0f59d779cf8f1fd0a87b5f84bc44': OctoFinance -'0x8c1fd2de219c98f5f88620422e36a8a32f83324e': opengsn -'0x892e04f20f9d7118917d7f94850c3c9423120c32': Teahouse -'0x9972d940c9a23f84fcf92867d18f28d75d010e5e': Mask Network -'0x74f85fe5538dbfcf3b46399e121c3ba83f695f91': TokenPocket -'0xcf85a70d88d14d157c50aa61999f7808027a15d6': Animal Coloring Books -'0x2596b971ee0de4532566c59fa394c0d29f21d224': OE Ape -'0xec1557a67d4980c948cd473075293204f4d280fd': Quix -'0x7856f2a12a7a18b4a115d295f548434a9b078fa1': Aelin -'0xdbeabb16b017d321cbaa00c7412ce76d08ec2fc1': BitBTC Protocol -'0xc22834581ebc8527d974f8a1c97e1bea4ef910bc': Gnosis Safe -'0x5fd7d0d6b91cc4787bcb86ca47e0bd4ea0346d34': Socket -'0xfd8c6ebe0d284f9d2c9665f17bba47032259e907': Connext -'0x83bc3055649f9a829bebeccbc86e090d6a157161': Chainlink -'0x2ae8831a00b3ebd603c0028b317cf30e578a4a3c': Perpetual Protocol -'0x0c16527ec6d017541568f1998fda2b3c24b81977': CatDAO -'0xf9ce0b68f4baa50bd30128a01c5297e2b46a6428': CircusDAO -'0xd9cbe784df2ae03ea0e22335bb5cd7fcb42a0e32': Gelato -'0x88215a2794ddc031439c72922ec8983bde831c78': Arrakis Finance -'0x11978d32619cfefc2e7c75a70ef8beb077b503ca': Frax Finance -'0x68d03de837cc395ec34c61c078fa901468a3bb29': Frax Finance -'0x4707ddf20584a1df862403e7e0cc77c33330dca0': Bongswap -'0x97b62cd23a04be0e0dc4a5f03ddbd0addc8ba29a': Band Protocol -'0xf6839085f692bde6a8062573e3da35e7e947c21e': InstaDapp -'0x26ed8119c45e3871df446a13f7fdc9e2c527dacd': InstaDapp -'0x5bdb37d0ddea3a90f233c7b7f6b9394b6b2eef34': Saddle Finance -'0xde910777c787903f78c89e7a0bf7f4c435cbb1fe': Synthetix -'0xb9bac083c6a968fe5d63e9e2337312f1a40c710a': Circular Art -'0x332b9e15db0c413de6f88b278b0016de3af005a0': Pegasus Finance -'0xfeebd6b860d4f33ec03014af0ee0a35ce8d6fd97': Pegasus Finance -'0xe48a5173ade669651120cb5e99e6fe140d4d73da': Mean Finance -'0xe69d24dd645d3d6a9985665dc0d5f8d57597e915': BitBTC Protocol -'0x35a9f94af726f07b5162df7e828cc9dc8439e7d0': BtcMirror -'0x294cb241ebf6fe95bbb76071c7cda8dd62eb138e': Cryptex -'0xa2e00fbd1e9315f490ae356f69c1f6624e2ed992': Nuke Vaults -'0x5befa2d163e40e148df83921e1cc59e044df5471': XDao -'0x03238279fa90dca3a02a65113f290cea01d48d87': Mirror -'0x6eb3783165e3e9bf6f3b463524738a2147be08ff': Clipper -'0x7a4535c67f2617d2590617666295c46c9f044f0d': Set Protocol -'0x71d024c31a1324496b9f69533618cefd09770010': Pilgrim Protocol -'0x89dea587db263d8c82bc63c2ae2b6f4784c418d9': OptiMarket -'0x057c75c9b1072be82ddd90b64501f525a9a300e7': Chainlink -'0xfa9da51631268a30ec3ddd1ccbf46c65fad99251': Multichain -'0x3976d5b90cfa0a0cc4d62983455ff6a6909f0f18': Dentacoin -'0x5ce7d83f7aaac17a0ad40540b37fc7a0b688ff44': Olympus -'0x5777aa6f437399af6cef2fce0be8d4b4ed7c7232': Umbra -'0xb16a11442878d6f1ef202ae63233a7c13e98fd7f': Hundred Finance -'0x7a1285a7381a3099bfe6706549859316e6f90e6a': Aave -'0xc841a5fb20d2395316ff9d7cfa2032ab51e70f00': BitBTC Protocol -'0xfadcbe6735146c6839e2748a371450a4540e12f3': Dentacoin -'0xf663a1d22ec5cec99c4bb404ec3e948895afcd73': Chainlink -'0x8286dc6df929c4bfa4f6951cab4dae2ec02d4d72': Hundred Finance -'0xc36142c497053c42bdaa14737bf80e71daa984c5': InstaDapp -'0xf125ccc0a0332ba7b51a601d0975ac44cc3b5655': InstaDapp -'0x161b29d1919d4e06b53ee449376181b5082b30b9': Zerion -'0xfd0bd19e849493f77d8f77ed026520c1368102bd': Layer2DAO -'0xbc577bddeb479eeabadf5303a1221cf84c074ea7': xToken -'0xaa2e0c5c85acb7717e58060ab3c96d2b184ee07c': Arrakis Finance -'0xd82fbd545f8205f67006309773b2e065c6764ee5': Superfluid -'0x9f403140bc0574d7d36ea472b82daa1bbd4ef327': Layer Zero -'0x1dd2560c3c818ed81208f2e6bc042c241dc9b22e': ChainShot -'0x4365f8e70cf38c6ca67de41448508f2da8825500': Aave -'0xae0b890a625a87c23a1fccdefb4c26a798719f17': Aave -'0x1d7c6783328c145393e84fb47a7f7c548f5ee28d': Stargate Finance -'0x0298f4332e3857631385b39766325058a93e249f': Sabiler -'0xb29050965a5ac70ab487aa47546cdcbc97dae45d': Punk Domains -'0x83a15cb9781458b421ad11def469586242cd06cb': Superfluid -'0x8b1727d9322e7bb82e87018f0a4f0d60a3d0866d': Superfluid -'0xcc59c42d05bd66fe22fba27016f783af43f68fa7': Aktionariat -'0x322d58b9e75a6918f7e7849aee0ff09369977e08': DeFi Saver -'0x3527a204a5260a0e36ca695312379370328e4e6c': Mirror -'0x820f92c1b3ad8e962e6c6d9d7caf2a550aec46fb': Tipcc -'0xaa270c9cf88e3806bfa1e3cd983f34af339a7ffc': Synapse -'0xb00b19938346b745ccb3fc4fad946de0caa724a2': Router Protocol -'0xb00bc9e04698a3315b6038225a2e9e42d63c7669': Router Protocol -'0x9f76043b23125024ce5f0fb4ae707482107dd2a8': Polynomial Protocol -'0x6ece61d29b90642941c41240da924690da145696': Stargate Finance -'0x57ade2f6723dea166d17694a576403ba9750762d': Mirror -'0xacfe4511ce883c14c4ea40563f176c3c09b4c47c': Pickle Finance -'0xc9424ba7e09a46f50f8aa89203c61149091adbcd': LayerZero -'0x473ab11a7b649064ee50b255b5b14dfe284a0373': Chainlink -'0x954e3eb8de035ec1bc8fe8fa0091d5b87ab17d47': LiFi -'0x079a889eb69013d451ecf45377258948116e2b3e': tofuNFT -'0xe00eaa2787a8830a485153b7bf508bc781e4a220': QiDao -'0xb49a6213da096f72c6f83c63a68f43caabb113ad': KEVoLUTION -'0x14719476c596fce28d381bd3ac12c3ce4698a1e9': Raid Project -'0xeb10511109053787b3ed6cc02d5cb67a265806cc': Party Panda -'0x919124f5f5135d3020a0b075412679755545f7c5': Curve -'0x244a807084a3eb9fd5fe88aa0b13aec8401577bd': dLab -'0xbe9228ce3b7ed30c8646143d0e56ee16fec6c07c': WardenSwap -'0xbb73463b88b0cb9681f176d6d43a12c2fea2c237': DeFiHelper -'0x386a28709a31532d4f68b06fd28a27e4ea378364': DODO -'0x817b4eab0e595801f382f531e36245ebcd401452': Pegasus Finance -'0xb7eb3f6a9abcae7559484fb126b8f872781cf9dd': Nested -'0x4fbe899d37fb7514adf2f41b0630e018ec275a0c': Beethoven X -'0x3d64fb8a2fFd08C186e8060aA57c8011D8b999cC': Beethoven X -'0x697A71353A4BC1eb1356763018a229c27a3fbA0C': Beethoven X -'0xADE38bd2E8D5A52E60047AfFe6E595bB5E61923A': Gamma -'0x9c5a87452d4FAC0cbd53BDCA580b20A45526B3AB': Niftyswap -'0x669dcFd6C2c0B267DDa08E9478E3e2c9Def8b7f2': Putty -'0xf87BC5535602077d340806D71f805EA9907a843D': Sushi -'0xE8bc44AE4bA6EDDB88C8c087fD9b479Dff729850': Hashflow -'0x2AfAeCA15Ac8A62E9bfa3EAF0285315AbAEcf334': Elk Finance -'0x111617795C52555D45cF100D0cBF01c82E157E81': Elk Finance -'0xF220eA963D27Ebe782f09403017B29692A4fC4aE': Elk Finance -'0x8b5F94a2c2e23eE8cC0Ff56872aF973243eF7628': ChainHop -'0x00778C4222c7d837cbfe4C1539AD10c127eC1C1F': Celer -'0xbac4edFAB0FFBD3344B163fAd587F07261c6CD7E': Backed -'0x4a27c059FD7E383854Ea7DE6Be9c390a795f6eE3': Backed -'0x5b0390bccCa1F040d8993eB6e4ce8DeD93721765': Tarot Finance -'0xCDf41a135C65d0013393B3793F92b4FAF31032d0': Gelato -'0xbA3C7B0b8f29b41F0E841c1ffB1Bda8B605830dA': DefiSaver -'0x0000000000933edea24f198114758e45BF9B9f14': Mean Finance -'0x98D93988b17caDdeC301aC40E805c565F2D5925B': Optimistic Loogies -'0xc0DE1436C4E247F8652476A0B9ff55699801e1d0': Velodrome -'0xab1ef74D2C461e95f4b658ca1f94aC519ad80BA2': OP -'0x9983D8cDEAf7872501628229d311E2F7Df396aDd': EvoDefi -'0x05ec0f0112CA25846886B04f2587bEfafC2A8d68': EvoDefi -'0x7EfDC11929225F1756479006149b0bd7A2b89C1E': Apetimism -'0xe027880CEB8114F2e367211dF977899d00e66138': Granary -'0x4826B5F57600e63f4C22CED5f1FfCff442F00D3a': DeCommas -'0x0132613b3A1061816F4661Ad301612910E3Cce0B': XY Finance -'0x72f0a08d8f8102cafBB3974566b9861a1ba74d34': Elk Finance -'0x35D504BC2A8D04bf3b90e285eB5c41F53F6353cc': Dragonia -'0x010dA5FF62B6e45f89FA7B2d8CEd5a8b5754eC1b': Beefy Finance -'0xdd99b75f095d0c4d5112aCe938e4e6ed962fb024': ThirdWeb -'0x4B6B6A3Af8C882B53C2cC99f016C89B231cE441d': Matrix -'0xdC0D4BdCc70362cEDd23d3997C31528Ec1502FC4': Biconomy -'0x0c36A5b01E1668C867A5e58f23bb6cb4d83a4cc8': Rainbow -'0x3A893A7525C4263052c3C04f9C32d919c75cb8e0': Symphony Finance -'0x939c8d89ebc11fa45e576215e2353673ad0ba18a': OpenSea -'0x7c9c773e41a3b68924b3b4924df8fffcf7ae7e18': Naga DAO -'0x24bD918b03dB3f16557942A15c92b6859510c4dc': Diamond Protocol -'0x9143743c4a54fdCF81f38e2370A4e9819E98797c': Diamond Protocol -'0xF6c5B9c0B57590A5be6f16380D68eAC6fd9d0Fac': Ooki -'0x90899D3Cc800C0a9196AeC83DA43e46582cB7435': Ren -'0x3Fc25fB2fbd5b1Ae1c9528Fe5Be8c7EF91fCD95b': Holozaki -'0x4856e043a1F2CAA8aCEfd076328b4981Aca91000': Quix -'0xd7f73cbf2b6ac915976cc96706b76d6425fbc234': PoolTogether -'0x4D40eb12430A57965cEe3015348d490C6156dF20': PoolTogether -'0xAE75B29ADe678372D77A8B41225654138a7E6ff1': Brahma -'0xB9F8e55Ac4353e0eDD65F11B4A4384a718E78189': Clipper -'0x1E71AEE6081f62053123140aacC7a06021D77348': Reaper Farm -'0x982F264ce97365864181df65dF4931C593A515ad': Beefy Finance -'0xdd32a567bc09384057a1f260086618d88b28e64f': Ganland -'0x13c716188c38ce27b9b2b672a59543cf1170eabe': Alchemix -'0x2d356b114cbCA8DEFf2d8783EAc2a5A5324fE1dF': Thales -'0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba': Abacus -'0xf01121e808F782d7F34E857c27dA31AD1f151b39': Paraswap -'0xE89CB2053A04Daf86ABaa1f4bC6D50744e57d39E': Backed -'0x79f5dc19bc96ec1db776d39535bde169e67e33f4': InsureDAO -'0x3b410908e71Ee04e7dE2a87f8F9003AFe6c1c7cE': Reaper Farm -'0x7AFAc84bF3931B11548ED02b4460ad754cF54C66': KyberSwap -'0x056c7933D2f25904787bb92C26E9f90655529f8E': Adrastia Oracle -'0x26Ce2091749059a66703CD4B998156d94eC393ef': Fraxswap -'0xc5584892F5B81C58D0547891641a91c245C26209': Firebird Finance -'0xD927cE147f098cE634301e6c4281541b1939a132': Beefy Finance -'0x6539519E69343535a2aF6583D9BAE3AD74c6A293': Reaper Farm -'0x000061c160273811279508582A4AfaBc263D98d2': Galxe -'0x985A29E88E75394DbDaE41a269409f701ccf6a43': QiDao -'0x7E8A8b130272430008eCa062419ACD8B423d339D': Rango Exchange -'0xC7f8D87734aB2cbf70030aC8aa82abfe3e8126cb': Premia -'0xDa2d96eADAb3671D9DFC6b2901aA85E99F8f0EB3': Ripae -'0x952e9c6391d9C0f6C6174D395Aa9B4eC1030335A': Tarot Finance -'0x6A6B51FcB0217521601237227FA2E43a95eC06b0': KyberSwap -'0x1BA608D6a354fbEb41FD7cC799e7f09b2612AB26': Iron Bank -'0x725Bd51dCCD08272C63eFdC1a5112002da0C5540': Velodrome -'0x1826316Aec2F2D400086B3cAdfCb291238010efA': Homora V2 -'0xf8Bf80beB4cEe030d8C60e5942CA3749aE6beDF1': BlueSweep -'0x94B90d0362D32AA14314D666A8968e1247508dfC': Hidden Hand -'0xa3102370655EfE1b29De7898A85bF7Fef3f1fB8a': OptimismPad -'0x1a86596A4F3398B6c44232ba1716e20e88205A40': OptimismPrime -'0x685723b9dc89bdf28ba5f98f9a8c0ac899bd6e77': Jarvis Network -'0x1e7b390149Abb3B891e0a7027E111188032D4540': RadioShack Swap -'0x4C3490dF15edFa178333445ce568EC6D99b5d71c': Reaper Farm -'0xf0974c239b7d08b7240b690b69ec3dd8ae783efc': Apetimism Launchpad -'0x5cb01385d3097b6a189d1ac8ba3364d900666445': Overnight+ -'0xA52fECFe3F0D4a77A572dec9B72277abC5dFD9c6': NEX -'0xb98d4d4e205aff4d4755e9df19bd0b8bd4e0f148': Fuji Finance -'0xFb59Ce8986943163F14C590755b29dB2998F2322': Sonne Finance -'0x5bd973c3841b754386963abb117ffc796041aecf': OpenXProject -'0xB32D9aa786E4f53f9484539cF44B1E092F94D483': Quest3 -'0x7a6468F8161ef39d7639c67DfA5637BA1b7ba74B': Alchemix -'0xC6387E937Bcef8De3334f80EDC623275d42457ff': Yearn -'0x4B9f696c998f9549485a3a85DcA692Fd6CCE491F': Flashstake -'0x652c46a302060B324A02d2d3e4a56e3DA07FA91b': Kwenta -'0x5E28ffE6700C6643747f51349d7246202F1a3F25': Odos -'0x2cc7A5c9D8AcA37395155cB200C98bA3b3516952': Atlantis World -'0x810973bdC95221817c4Ca7999A78aFD25F6d0F6d': KyberSwap -'0xC42e9476b0a458097087336e2395Dbf45B0BdC12': Pyth -'0xdc7c7f0bea8444c12ec98ec626ff071c6fa27a19': Votium -'0x11F11121DF7256C40339393b0FB045321022ce44': LiFi -'0xcde47c1a5e2d60b9ff262b0a3b6d486048575ad9': OP -'0x59ca05674f5073f95f292aaca2d28a7dc80f12d6': Mirror -'0xedcd79f34db8b78cd7a55e04dbf991ecd1a5c0f4': Zeroex (0x) -'0x48cff7ff77b2bf83e4a6f843b5b1709601671e83': Clipper -'0x1a3daa6f487a480c1ad312b90fd0244871940b66': Quix -'0x8df57e3d9ddde355dce1adb19ebce93419ffa0fb': Revert Finance -'0xccbdbd9b0309a77fc6a56e087ff2765ff394012e': 1Inch -'0x819addc3dd780704ae85ea12c18252f7020c24d0': Synthetix -'0xbf70200dd73d5f3aafa4f0d8adacdeaf3798b7f9': Cryptovania -'0x719dafe0be7abf7d2b6c67eed4fa18d970d3abb6': Unidex -'0x2c01f405e3c525c0cb867789aaf077f3553ccbb7': Lido -'0x302d2451d9f47620374b54c521423bf0403916a2': Synthetix -'0xfda462548ce04282f4b6d6619823a7c64fdc0185': Angle -'0x9aa99c23f67c81701c772b106b4f83f6e858dd2e': Hop Protocol -'0xbcb909975715dc8fde643ee44b89e3fd6a35a259': Furucombo -'0x42405d66fda09dbdac90ff25fc5a4c2353f43e70': Beamer Bridge -'0xe029c32d412972C5F3D107DA6d6eCF8F1C1E788C': Kwenta -'0xcd526ee406bc8349ba8135758cee11fa3aaa59a0': OPX Finance -'0x4023ef3aaa0669FaAf3A712626F4D8cCc3eAF2e5': Pickle Finance -'0x370880694995Aa8A53F71645F7Bec3b0e7bb25d9': OneRing -'0xfc8367ef6bcfc10746e5b9b034314812db8d7213': OpenXSwap -'0xafd91ef047189f7e894d0fac71dcce8687e9b893': ECC Domains -'0x0E1B5AB67aF1c99F8c7Ebc71f41f75D4D6211e53': Socket -'0xd531795282a1d7857faf43416b4c135759db45c4': Sided Finance -'0x2dA7e3a7F21cCE79efeb66f3b082196EA0A8B9af': OmniSwap -'0x0c6c8f014d1B3E37F470d39356379E220e4Beb67': Resonate -'0xe25831C97aC161AD58aEf70B6ceE507B0E49688C': 2Pi Network -'0x8888888841B669313CdC735910214313d7420E25': AcryptoS -'0x72c1a1c24917eef19e7f5dea146d950841f37662': O3 Swap -'0xfb41cbf2ce16e8f626013a2f465521d27ba9a610': Beefy Finance -'0xbA22746D79E75931DD8C0336760332E5D4a372a5': Curve -'0xA80481E3f9098602954B2E5cf306e6dEE053EF3E': Gysr -'0x3cD76a3E1Ae288c11459b986362ff2f63Ba0A379': Opti Stickman Club -'0x97471c0fdddb5e5cc34cb08cb17961bd3a53f38f': Woo Network -'0x2C10aC0E6B6c1619F4976b2ba559135BFeF53c5E': Powerbomb Finance -'0x0f3BF5c241B6625C0fA781ED137fDe6786b2e66f': Lemma Finance -'0x43834c6A65C64a4529E048Ba55a685fF5Aa43cB4': fBOMB Finance -'0xc82c018dB54B894853cACb878D0F3e481E8C6b96': Via Protocol -'0x4401A1667dAFb63Cff06218A69cE11537de9A101': Clique -'0xF3808680917524CD1346b12e4845830076eB7001': Teahouse -'0x4dc45eAc9eE25Af88958c94461fd46175C47744a': Geo Web -'0xade09131C6f43fe22C2CbABb759636C43cFc181e': Connext -'0x4b327372A347aD97e45881428af26a4C28840C66': Metamask -'0x75ee82787c548daeac58af6cba5bd2a9ff863d28': Flipside Crypto -'0x745748bcfd8f9c2de519a71d789be8a63dd7d66c': Curve -'0xe0774a09b55febaf93049af328901f7a5b09827b': Woo Network -'0x413a8ba5175efd5f770e372d0ae91cf04a9473e3': Woo Network -'0x7D0831e0469e8b48e0F280459bC2CC44f23Ca7E4': 1Inch -'0xCb77b30841e1dEafFd88bA08B9654C667F80b80A': Quadrat -'0x001530e763FE9De4DCfe31BBd8548BCb579Ebf3A': Defiedge -'0xD8fA8F87129c654a6Dd7F34EEDAf58379E176eb1': Uniswap -'0xf09c27934A92c56c7C0dD6cBAc858C35fBd5170f': Scion Finance -'0x41BA3387E1a5a592E27B9EE33935957CE5F872C1': Avault -'0x86b86c24c7b1f404ada8b4000acbe04158e096bd': Defiedge -'0xf708929CCfDed4142420592caAef42bD41C8a367': Champion Finance -'0x221a8Da83f675f1Fd97105a5B2B3bb65916a5101': Rabbithole -'0x35a8e8ed19122f6707984e7c135c0d8943d4d750': OKX -'0xe2e2d9e31d7e1cc1178fe0d1c5950f6c809816a3': Wormhole -'0x44cBfc3Ce762fC0Fee9Ddd6372804b7B660176bC': Mux -'0x7f4537349A0a40cd20ba5Af0D11fdC46dCFCBB3f': Zonic -'0xDcdE7a069dEEe7b73A795A76F97Eb9dca7f812d4': Connext -'0x42004661285881D4B0F245B1eD3774d8166CF314': Optimism Governor -'0xF285E70Ca2002b796A575E473285282BBf39D790': Omnisea -'0xFbe304258DCf193b9a320541985e2078ddB34287': Decent -'0x93798ef7e3a621d7c4eff22eda50b931fe57a3cf': QiDao -'0xcD0b087E113152324FCA962488B4d9BeB6f4CaF6': NFTEarth -'0x607291C9B3b03D8C2DC1F5f7F8db2B6A06C91183': Tide Protocol -'0xEe312dDc7aCE9F08728241BF2693B67D3428271B': Aloe II -'0x33d73cc0E060939476A10E47b86A4568c7DcF261': Coinvise -'0xcD6596071e9CB8FC358796F2839A5704496795F9': Superfluid -'0x71a15Ac12ee91BF7c83D08506f3a3588143898B5': DefiLlama -'0xb48bC8FBB6283740389eF69eac58FBEE07d42f1A': Clipper -'0xF26DdF26623e8Ae83f78012a48d24704AD175431': OpenOcean -'0x6453bD91C3B06DCC24F588FFfa384b0EEB0178B3': Beefy Finance -'0xd8693368d37b502ed54c315e38efde7eb3dcdb5c': FunDex -'0x30B12942912Cee5A719edec2dD147224fCC373A0': Mummy Finance -'0xe61Bdef3FFF4C3CF7A07996DCB8802b5C85B665a': Exactly -'0x1874028262f1f4b2dd1f2700a72ee8b9b7c69090': Kyberswap -'0x76d84163bc0bbf58d6d3f2332f8a9c5b339df983': Otterspace -'0x5e9Bf1dD74b4d25B7009AF11582f537b08eA3d3c': Layer Zero -'0xac789308a29783f507f1f3baacbd43326c5767f4': Gamma -'0xba32a3d407353fc3adaa6f7ec6264df5bca51c4b': Arcadia Finance -'0x280333C41A9302448EbC070eD0300ad2Ed4B8244': Transit Finance -'0x8a700FdB6121A57C59736041D9aa21dfd8820660': Orbiter Finance -'0xe00691e65Cd4400c84a174a4C56f20bA43dffD89': Ethos Reserve -'0x4bb4c1b0745ef7b4642feeccd0740dec417ca0a0': Sushi -'0x15051107651f3420144d3a2412d49402c2fac3c0': zkBridge -'0x2313f80d53c649c7b2c9c4d101b796f34cbe80f3': Wido -'0x18606e2ABaA0bA15Cc1D0D3b55521bD2247e4d2E': Layer Zero -'0x6879fAb591ed0d62537A3Cac9D7cd41218445a84': zeroex -'0x076d6da60aAAC6c97A8a0fE8057f9564203Ee545': Aave --BGD -'0x015D83637A6904CB13C93068a48887F9ACD7EEF5': Odos -'0x45208e8d6d09d6bfce5094083ab36f22bdfc8456': Layer Zero -'0x014040C6A9cd6366f8fa858535b7DdfAc507dB20': Zerion -'0x05182E579FDfCf69E4390c3411D8FeA1fb6467cf': Paraswap -'0x86791C7b7Ea5F77b1612eCc300dD44ba3A1C9083': Socket -'0xFBE184415F4893e4E9F918F5E0e22D759c9794a7': Socket -'0xDc6d6f9aB5fcc398B92B017e8482749aE5afbF35': Hypercerts -'0x39F0bD56c1439a22Ee90b4972c16b7868D161981': zkBOB -'0x1Dc662D3D7De14a57CD369e3a9E774f8F80d4214': DODO -'0x42f4802128C56740D77824046bb13E6a38874331': Chainlink -'0xAf66c75FDA4ca9546920A4F7270265ADfB90F43D': Collab.Land -'0x02302cf47fa39f8ca2f1442259016befb3595c67': Optimism Name Service -'0x3711Ae92429CCef7581C4C6eFE0cf6042bA0b1E2': Curve -'0x51EdB9Cc0A86A32870753E4dE2C363aeFcC25D8C': Tellor -'0x4BbbDF6562daf495Bc2eb8114b990aD95E1177F6': Via Protocol -'0xe8dD38E673A93ccFC2E3d7053efcCb5c93F49365': Socket -'0xa63e6102719a36562860a804f9020018c1683c7a': RedKoin -'0xED20c81Dc8A28769eA14E3FfbD782967Cc53BB28': Layer Zero -'0xa990077c3205cbDf861e17Fa532eeB069cE9fF96': ERC1820 -'0x81b7e9d8409b857d70ad14073e785c486945caf4': Optimism Ape Yacht Club -'0x594da37b691ed83a994cE934C64844E94B9A1997': KyberSwap -'0xd22044706DeA3c342f68396bEDBCf6a2536d951D': Eden Network -'0x8FcBA7279af1d5d12C77e7062cAf1E09A0623f97': Hundred Finance -'0x01c28a1b29098175fd5c74f6dd9225b0853325fe': Layer Zero -'0xb67c0cde9974275836e77fac65bcbb5055d356e9': Fire -'0x98422d38f6c408d0224b8fa4588b807ca5e66b17': Galxe -'0xcbd1c32a1b3961cc43868b8bae431ab0da65beeb': Union Finance -'0x4A21C588c93c38833BCf3c0fe0425bC114Fe8271': Socket -'0xCbE0EDD07345A98542d779e1f6b897103eEa0aB5': Metamask -'0xc522c16e88b6bf457389566d22550bce157d3875': Bitkeep -'0x39cfca7b389529ac861cbb05add802e5b06e5101': Kwenta -'0xd61fa937b8f237901d354f48f6b14995fe468bf2': Pixelpooly -'0x60e1980343558c4c9d8f323f45d3e0e256bdb16c': Mirror -'0x8321926c8aae281ef9d8520a772eb1d94a9ec6dd': Velodrome -'0x54812dBaB593674CD4F1216264895be48B55C5e3': Cask Protocol -'0x74a3c8511e709b6463adeed6ec1b273886cefc13': nftperp -'0x904a08be742bd5bc6ad10f0924f06b0b23d1175c': AVT -'0x6102D098C063A1f282A9196428946D4E3a7BB754': Mirror -'0x000039DdCF1F63Cf3555e62a8D32a11bD1E7E1E1': MesonFi -'0xaD031EeE55f595dF6Dbd61e9ef3B908FaD1B3671': Zora -'0x6Cb6D9Fb673CfbF31b3A432F6316fE3196efd4aA': Mugen Finance -'0x0F26f9E5C297d411768D4daa43F12533f59BAEe7': Mugen Finance -'0xA2bb05D03E77978d01aa9AeC76eB162a1bDEbbd8': ReHold -'0xa3E00f22EAa8e6f751a32FFED4010DCebD00450a': LuxsFi -'0xc9a88f94652bb5ec763e4cca7cf112d459e12c6b': Dexilla -'0x7754d8b057CC1d2D857d897461DAC6C3235B4aAe': Dyson Money -'0x7DF0808CFD89Ea3995af99CB1374D2907C2399b6': Ante Finance -'0x080f08076e8EAdC66006C3CbFEd28a34918A1fA6': RigoBlock -'0xdeD212B8BAb662B98f49e757CbB409BB7808dc10': Swapline -'0x0776752096BAfc4e86E5Aa46Cf8620c2e3Fb67cc': Steer Protocol -'0xa8863bf1c8933f649e7b03Eb72109E5E187505Ea': Manifold -'0x8636fa411113d1b40b5d76f6766d16b3aa829d30': OmniX -'0x8ac863a9142781378ed302ab96105cada1697e85': Perpetual Protocol -'0x44FC6f6c8e7F4BE9e59a677BEE5Fe4FfB48AD07e': Optimistis World -'0xfffffACc41E00f96F6af4AF0154AD18749C9d5eA': Kresko Fi -'0x76ef4b28df1f590db4cd680675d734c27caa32ba': Rocket Pool -'0x6b8a8035601da0c9d31c5fc11fa66aae485654fc': Cozy Finance -'0xa2bcad247c64da44739e141b491fe4238a4d0480': Lyra Finance -'0x7d5501e0bc56d0e4de909b748f48c768c9fde841': fxdx -'0xdeadfed00b7848545d8fe5cb4e7c51ed37e5a2fa': Holograph -'0x0c8af56f7650a6e3685188d212044338c21d3f73': Holograph -'0x6dcb5e43b05918505f65bf423088af172c32be33': Symbiosis Finance -'0x9088b976e9542d0a27f4f9ddc7a716c7714806ea': Extra Finance -'0x074a2bb7686da8dacad178aece2ad280b8d4c25d': Minereum -'0xb6235eaeadfa5839cda207b454d98b328dfe2f3a': Syndicate -'0x946e9c780f3c79d80e51e68d259d0d7e794f2124': Uniswap -'0x156d8fac8cd3deb51ab194d906abfc81cf323c11': Minerva -'0x169e2ffc1c6b229b04e65a431434bf0e8ed9563d': Vesper -'0xe7a9ecdb56af2bb86fdaaa3e3b2d1f0f7a9fe664': Another World -'0x017F8Ad14A2E745ea0F756Bd57CD4852400be78c': Rabbithole -'0x6a0a93cd6d6fb7a36bf6234ef4650bf9474e7682': Gelato -'0xa66cdda1817c85ed3d232a5affd17673e933d8a7': Sound.xyz -'0xB8Cd93C83A974649D76B1c19f311f639e62272BC': Axelar -'0x98b2920d53612483f91f12ed7754e51b4a77919e': Axelar -'0xE86375704CDb8491a5Ed82D90DceCE02Ee0ac25F': Squid Router -'0x9ebC8E61f87A301fF25a606d7C06150f856F24E2': 0xSplits -'0x15bc81b35a8498cee37e2c7b857538b006cecaa5': Cinch Protocol -'0x7EEeF54Df0596633dbf6Fa3C88dEB1E3eA85ECE8': Metronome -'0x5472Cf4F1bE2aA6ad27C6F93101f7899cCAdBaf7': Rhino.fi -'0x8de39c1Fa14b6082053e7cD937d6Ebe58A69D6D2': KyberSwap -'0xfB3485c2e209A5cfBDC1447674256578f1A80eE3': Abracadabra -'0xfddfe525054efaad204600d00ca86adb1cc2ea8a': Abracadabra -'0x085909388fc0cE9E5761ac8608aF8f2F52cb8B89': Interest Protocol -'0x3d4316342acd2deD06507302404e9788fF5F4d02': Squid Router -'0x19df27089a52ccfa444ce5a73ef86403d03210d4': Getaverse -'0xBd9d5319691922e2e1F9DC69ce0Fd0B0ded51656': Manifold -'0x3B52ad533687Ce908bA0485ac177C5fb42972962': Manifold -'0xB8cEF765721A6da910f14Be93e7684e9a3714123': Allo Protocol (Gitcoin) --Gitcoin Grants Stack -'0x123694886DBf5Ac94DDA07135349534536D14cAf': Threshold Network -'0x956a5152D0f498dBA0c5966577bb44262F8F7078': OP AttestationStation -'0x9c6373de60c2d3297b18a8f964618ac46e011b58': OP Citizens House -'0x1a5309f208f161a393e8b5a253de8ab894a67188': Bond Protocol -'0x07537D4360aF2489FC1Ca086EA9741c054d804de': Velodrome -'0x8fa9aa69a6e94c1cd49fbf214c833b2911d02553': Cian Protocol -'0xD56d253d6ad8C2164Eb35B2D7D8b951fe8380027': Merkly -'0xBE2F0354D970265BFc36D383af77F72736b81B54': KyberSwap -'0xc496Dfe5da45263742cdCe69BE460c8a90dF141E': Essence Finance -'0x7492ce19d83b3a0BaC1BEBC9706ce0dF4ADD105F': Curve -'0xC253Af7B80F3DD3d469bf4b94AA9e3df59e71e43': Inverse Finance -'0x6Bd10054c59Febf0CF7c0f5359D036A997E93810': BlueNorva -'0x6fF5723435b7dfC2371B57Fb5cB4c373E5995C78': Bored Town -'0xc82Ea2afE1Fd1D61C4A12f5CeB3D7000f564F5C6': OKX -'0x86c5608362b3fbbeb721140472229392f754ef87': Worldcoin -'0xcb21ed4ca5c5fff8b1e21ddb7bbff8c6546ce5e6': Coordinape -'0xeac5f0d4a9a45e1f9fdd0e7e2882e9f60e301156': ScopeLift -'0xb9e4162c27860fc81379c4a30f7288ab9deac164': Metalswap -'0x838912bef2424e40919965dc7e0b02ba147e6744': Cloudbase -'0xe003b3fdb26d0d574b860212e07202c151ee0d24': NooBys Swap -'0x3D5a771e690Af1Bbd854Df638eD945baf1066ED0': Collab.Land -'0x03be6a29022c134762423b4cdd78b970bfa2691b': HotPots OP -'0x8fE3a2A5ae6BaA201C26fC7830EB713F33d6b313': L2Marathon -'0x12ddBd16175953a8035A009393482DD64849959A': PHISHING CONTRACT CREATOR -'0x057dd0333d0ef737c72ca39403e7d478b1f1d0b3': OP -'0x30019eb135532bddf2da17659101cc000c73c8e4': Aura -'0xB07d2d6a03F2d4878Dc1680F8581E871dae47494': Aura -'0x8cF905C50F25A481171e7f5936625754d44A9537': Stackup -'0x00e6202a8A52a3De6Ce7c45F2e5E299Ec71e875e': Candide -'0x4325018916b082B91e277C7165FD8Da6467b71F8': Fuji Finance -'0x0B5a3C04D1199283938fbe887A2C82C808aa89Fb': Summer.fi -'0x7d896339a80dd38bc3bbb04383894c62b2ef2585': Worldcoin -'0xb02748d3eee8691c94b7c8d6788776aa52571628': Hidden Hand -'0x9DB006063ee2049a35624bCCe32945E524105401': NiftyKit -'0x682bd405073dd248527e40184898ed45bb827527': Cozy Finance -'0xA58090dd84D143ca90e95B05395e0F116A727714': Optimism Governor -'0x9AaC739c133074dB445183A95149880a2156541A': OpenSea -'0x9f6798ec61a30e27f94db7e6af678d179662b27c': Chainlink --CCIP -'0xae0c5140744bfc6bc33ce73298f10f7b538f02dd': Eco -'0xb1bEF51ebCA01EB12001a639bDBbFF6eEcA12B9F': Sablier -'0x3c5e6b4292ed35e8973400bef77177a9e84e8e6e': Layerr -'0x9f76a95AA7535bb0893cf88A146396e00ed21A12': Angle Protocol --Merkl -'0x123E2974efC726FABc16ea729c3e84F17a788DB1': Beefy Finance -'0xeFd9434A2B1076D5C84D242b6f4AAb47270EcEAC': ThriveCoin -'0x24399266da004b11173Bb478E5Da2c1b194721bC': Sismo -'0x890Bc5691E6011F580e264065d09a7a1A6902131': Synapse -'0x161D61e30284A33Ab1ed227beDcac6014877B3DE': Beefy Finance -'0x5853ed4f26a3fcea565b3fbc698bb19cdf6deb85': DeBank -'0x36BDE71C97B33Cc4729cf772aE268934f7AB70B2': Worldcoin \ No newline at end of file diff --git a/indexer/utilities/contract_mapper/templates/home.html b/indexer/utilities/contract_mapper/templates/home.html deleted file mode 100644 index 9e50d295f..000000000 --- a/indexer/utilities/contract_mapper/templates/home.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - Project Repo Mapper - - -

Summary Stats:

-

Number of projects with an actual repo: {{ projects_with_actual_repo }}

-

Number of projects without an actual repo: {{ projects_without_actual_repo }}

-
- - -
- {% if repos %} -
- - - - - -
-

- {% endif %} - - - diff --git a/indexer/utilities/database/hasura.sh b/indexer/utilities/database/hasura.sh deleted file mode 100644 index 37475434b..000000000 --- a/indexer/utilities/database/hasura.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!bin/bash - -SCRIPT_RELATIVE_DIR=$(dirname "${BASH_SOURCE[0]}") -ENV_FILE=$SCRIPT_RELATIVE_DIR/../../.env - -source $ENV_FILE -docker run --rm -it \ - -p 8080:8080 \ - --env-file $ENV_FILE \ - --env HASURA_GRAPHQL_ENABLE_CONSOLE=true \ - --env HASURA_GRAPHQL_DATABASE_URL=postgres://$DB_USER:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DB_DATABASE \ - hasura/graphql-engine - #psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_DATABASE diff --git a/indexer/utilities/database/psql.sh b/indexer/utilities/database/psql.sh deleted file mode 100644 index 94b470b7b..000000000 --- a/indexer/utilities/database/psql.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!bin/bash - -SCRIPT_RELATIVE_DIR=$(dirname "${BASH_SOURCE[0]}") -ENV_FILE=$SCRIPT_RELATIVE_DIR/../../.env - -source $ENV_FILE -docker run --rm -it \ - -v /tmp:/tmp \ - --env-file $ENV_FILE \ - postgres:15 \ - psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_DATABASE \ No newline at end of file diff --git a/indexer/utilities/etherscan/lookup-contract-name.py b/indexer/utilities/etherscan/lookup-contract-name.py deleted file mode 100644 index c5ef933a5..000000000 --- a/indexer/utilities/etherscan/lookup-contract-name.py +++ /dev/null @@ -1,41 +0,0 @@ -import argparse -from dotenv import load_dotenv -import os -import requests - - -load_dotenv() -API_KEY = os.getenv("ETHERSCAN_API_KEY") - -CHAINS = { - 'optimism': 'https://api-optimistic.etherscan.io/api', - 'mainnet': 'https://api.etherscan.io/api' -} - -def fetch_contract_info(chain, address): - api_url = CHAINS[chain] - params = { - 'module': 'contract', - 'action': 'getsourcecode', - 'address': address, - 'apikey': API_KEY - } - response = requests.get(api_url, params=params) - if response.json()['status'] != '1': - print(f"Error looking up a contract at address {address}") - return None - - contract_name = response.json()['result'][0]['ContractName'] - if not contract_name: - print(f"No contract/name associated with address {address}") - return None - - print(f"{chain}: {address} -> {contract_name}") - return contract_name - - -parser = argparse.ArgumentParser(description='Fetch Ethereum contract info.') -parser.add_argument('chain', type=str, help='Blockchain chain (e.g., optimism, mainnet)') -parser.add_argument('address', type=str, help='Contract address') -args = parser.parse_args() -fetch_contract_info(args.chain, args.address) \ No newline at end of file diff --git a/indexer/utilities/json-yaml/README.md b/indexer/utilities/json-yaml/README.md deleted file mode 100644 index 5b83d9c00..000000000 --- a/indexer/utilities/json-yaml/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# json-yaml - -Converts JSON files to YAML files and vice versa - -## JSON to YAML - -```bash -yarn json-yaml json-to-yaml --in data/in.json --out data/out.yaml -``` - -## YAML to JSON - -```bash -yarn json-yaml yaml-to-json --in data/in.yaml --out data/out.json -``` diff --git a/indexer/utilities/json-yaml/index.ts b/indexer/utilities/json-yaml/index.ts deleted file mode 100644 index 129226d8d..000000000 --- a/indexer/utilities/json-yaml/index.ts +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env node -import fs from "fs"; -import YAML from "yaml"; -import yargs from "yargs"; -import { hideBin } from "yargs/helpers"; - -type Args = { - in: string; - out: string; -}; - -yargs(hideBin(process.argv)) - .option("in", { - type: "string", - describe: "Input file", - }) - .option("out", { - type: "string", - describe: "Output file", - }) - .command( - "json-to-yaml", - "Converts JSON to YAML", - (yags) => { - yags; - }, - (argv) => { - const jsonStr = fs.readFileSync(argv.in, "utf8"); - const json = JSON.parse(jsonStr); - const yaml = YAML.stringify(json); - fs.writeFileSync(argv.out, yaml); - console.log(yaml); - }, - ) - .command( - "yaml-to-json", - "Converts YAML to JSON", - (yags) => { - yags; - }, - (argv) => { - const yamlStr = fs.readFileSync(argv.in, "utf8"); - const yaml = YAML.parse(yamlStr); - const json = JSON.stringify(yaml, null, 2); - fs.writeFileSync(argv.out, json); - console.log(json); - }, - ) - .demandCommand() - .demandOption(["in", "out"]) - .strict() - .help("h") - .alias("h", "help") - .parse(); diff --git a/indexer/utilities/rpgf3-attestations/.gitignore b/indexer/utilities/rpgf3-attestations/.gitignore deleted file mode 100644 index 65fec527f..000000000 --- a/indexer/utilities/rpgf3-attestations/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# Logs -logs -*.log - -# Data exports -data/ - -# Local artifacts -.DS_Store -.ipynb_checkpoints -*.ipynb -pycache \ No newline at end of file diff --git a/indexer/utilities/rpgf3-attestations/src/address_lookup.py b/indexer/utilities/rpgf3-attestations/src/address_lookup.py deleted file mode 100644 index dc6bcedd8..000000000 --- a/indexer/utilities/rpgf3-attestations/src/address_lookup.py +++ /dev/null @@ -1,77 +0,0 @@ -import argparse -import csv -from dotenv import load_dotenv -import os -import requests -import time -from web3 import Web3 - - -load_dotenv() -ALCHEMY_API_KEY = os.environ['ALCHEMY_API_KEY'] -APIS = { - 'optimism': { - 'etherscan': f'https://api-optimistic.etherscan.io/api', - 'etherscan_api_key': os.getenv("OP_ETHERSCAN_API_KEY"), - 'alchemy': f'https://opt-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}' - }, - 'mainnet': { - 'etherscan': 'https://api.etherscan.io/api', - 'etherscan_api_key': os.getenv("ETHERSCAN_API_KEY"), - 'alchemy': f'https://eth-mainnet.g.alchemy.com/v2/{ALCHEMY_API_KEY}' - } -} -DEFAULT_API = APIS['mainnet'] - -def is_eoa(chain, address, sleep=.20): - - url = APIS.get(chain, DEFAULT_API)['alchemy'] - payload = { - "id": 1, - "jsonrpc": "2.0", - "params": [address, "latest"], - "method": "eth_getCode" - } - headers = { - "accept": "application/json", - "content-type": "application/json" - } - response = requests.post(url, json=payload, headers=headers) - if response.status_code != 200: - print(f"Error looking up address {address} on {chain}") - return None - result = response.json()['result'] - time.sleep(sleep) - return result == '0x' - - -def fetch_contract_name(chain, address, sleep=.20): - - try: - # TODO: this won't work for unmapped chains unless the project uses a deterministic deployer - api = APIS.get(chain, DEFAULT_API) - url = api['etherscan'] - api_key = api['etherscan_api_key'] - params = { - 'module': 'contract', - 'action': 'getsourcecode', - 'address': address, - 'apikey': api_key - } - response = requests.get(url, params=params) - if response.json()['status'] != '1': - print(f"Error looking up a contract at address {address} on {chain}") - print(response.text) - return None - - contract_name = response.json()['result'][0]['ContractName'] - if not contract_name: - print(f"No contract/name associated with address {address}") - return None - - print(f"{chain}: {address} -> {contract_name}") - time.sleep(sleep) - return contract_name - except: - print(f"\n\n** Fatal error looking up a contract at address {address}\n\n") - return None \ No newline at end of file diff --git a/indexer/utilities/rpgf3-attestations/src/analyze_apps.py b/indexer/utilities/rpgf3-attestations/src/analyze_apps.py deleted file mode 100644 index ed61124d8..000000000 --- a/indexer/utilities/rpgf3-attestations/src/analyze_apps.py +++ /dev/null @@ -1,149 +0,0 @@ -import json -import pandas as pd -from urllib.parse import urlparse - -from parse_links import Parser - - -RAW_APPLICANT_JSON = "data/raw_applicant_data.json" -TIDY_ATTESTATION_CSV = "data/tidy_attestations.csv" -PROJECT_SUMMARY_CSV = "data/project_attestation_summary.csv" - - -def extract_website_name(url): - - if not url: - return 'none' - parsed_url = urlparse(url.strip('/')) - domain_parts = parsed_url.netloc.split('.') - domain = domain_parts[-2] if len(domain_parts) > 1 else parsed_url.netloc - - mapping = { - "x": "twitter", - "t": "telegram", - "youtu": "youtube", - "npmjs": "npm", - "npm-stat": "npm" - } - - return mapping.get(domain, domain) - - -def tidy_dataframe(data): - - print(f"Ingesting total of {len(data)} records.") - - records = [] - for record in data: - - # Basic information - record_id = record['id'] - attester = record['attester'] - time_created = record['timeCreated'] - - # Extract nested data - json_data = {} - for item in record['data']: - if item['name'] == 'applicationMetadataPtr': - application_metadata_ptr = item['value']['value'] - json_data = item.get('json_data', {}) - break - - # Applicant details - name = next((item['value']['value'] for item in record['data'] if item['name'] == 'displayName'), None) - applicant_type = json_data.get('applicantType', '') - bio_or_description = json_data.get('bio', '') - impact_categories = ', '.join(json_data.get('impactCategory', [])) - - # Contribution Links - for contribution in json_data.get('contributionLinks', []): - records.append([record_id, attester, time_created, name, application_metadata_ptr, applicant_type, - bio_or_description, impact_categories, 'contributionLink', contribution['url'], - contribution['description'], '']) - - # Impact Metrics - for impact_metric in json_data.get('impactMetrics', []): - records.append([record_id, attester, time_created, name, application_metadata_ptr, applicant_type, - bio_or_description, impact_categories, 'impactMetric', impact_metric['url'], - impact_metric['description'], impact_metric['number']]) - - # Create DataFrame - df = pd.DataFrame(records, columns=['id', 'attester', 'timeCreated', 'name', 'applicationMetadataPtr', - 'applicantType', 'bio or description', 'impactCategory(ies)', - 'attestationType', 'attestationUrl', 'attestationDescription', - 'number']) - - # Extract artifact name from attestationUrl - df['urlType'] = df['attestationUrl'].apply(extract_website_name) - df['artifactInfo'] = None - df['artifactInfo'].update(df[df['urlType'] == 'etherscan']['attestationUrl'].apply(Parser.etherscan)) - df['artifactInfo'].update(df[df['urlType'] == 'npm']['attestationUrl'].apply(Parser.npm)) - df['artifactInfo'].update(df[df['urlType'] == 'github']['attestationUrl'].apply(Parser.github)) - df['artifactInfo'].update(df[df['urlType'] == 'twitter']['attestationUrl'].apply(Parser.twitter)) - df['artifactInfo'].update(df[df['urlType'] == 'substack']['attestationUrl'].apply(Parser.substack)) - - def create_clean_link(url_type, artifact_info): - if artifact_info is None: - return None - artifact = artifact_info[1] - if artifact is None: - return None - if url_type == 'etherscan': - return f"https://optimistic.etherscan.io/address/{artifact}" - if url_type == 'npm': - return f"https://www.npmjs.com/package/{artifact}" - if url_type == 'github': - return f"https://github.com/{artifact}" - if url_type == 'twitter': - return f"https://x.com/{artifact}" - if url_type == 'substack': - return f"https://{artifact}.substack.com/" - - df['linkValidation'] = df['artifactInfo'].apply(lambda x: x[0].title() if x is not None else 'n/a') - df['cleanArtifactLink'] = df.apply(lambda row: create_clean_link(row['urlType'], row['artifactInfo']), axis=1) - - df['potentialConflict'] = None - for artifact in df['cleanArtifactLink'].unique(): - if artifact is None: - continue - artifact_attesters = df[df['cleanArtifactLink'] == artifact]['attester'].unique() - if len(artifact_attesters) > 1: - names = df[df['cleanArtifactLink'] == artifact]['name'].unique() - conflict = " & ".join([f"{name} ({attester})" for name, attester in zip(names, artifact_attesters)]) - df.loc[df['cleanArtifactLink'] == artifact, 'potentialConflict'] = conflict - - - print(f"Created Tidy DataFrame with {len(df)} records.") - return df - - -def project_summary(df): - - groupers = ['id', 'name', 'applicationMetadataPtr', 'applicantType', 'bio or description', 'impactCategory(ies)'] - grouped_df = df.groupby(groupers).agg( - attestationCount=('id', 'count'), - contributionLinkCount=('attestationType', lambda x: (x == 'contributionLink').sum()), - impactMetricCount=('attestationType', lambda x: (x == 'impactMetric').sum()), - cleanArtifactLinkCount=('cleanArtifactLink', lambda x: x.notnull().sum()), - potentialConflictCount=('potentialConflict', lambda x: x.notnull().sum()), - urlTypeCount=('urlType', lambda x: x.value_counts().to_dict()) - ).reset_index() - - return grouped_df - -def main(): - - with open(RAW_APPLICANT_JSON, "r") as json_file: - data = json.load(json_file) - - df = tidy_dataframe(data) - df.to_csv(TIDY_ATTESTATION_CSV, index=False) - print("Exported individual metrics to:", TIDY_ATTESTATION_CSV) - - grouped_df = project_summary(df) - grouped_df.to_csv(PROJECT_SUMMARY_CSV, index=False) - print("Exported project summary metrics to:", PROJECT_SUMMARY_CSV) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/indexer/utilities/rpgf3-attestations/src/canonical.py b/indexer/utilities/rpgf3-attestations/src/canonical.py deleted file mode 100644 index 04384eed5..000000000 --- a/indexer/utilities/rpgf3-attestations/src/canonical.py +++ /dev/null @@ -1,30 +0,0 @@ -import pandas as pd -from address_lookup import is_eoa - - -PROJECT_SUMMARY_CSV = "data/project_attestation_summary.csv" -PROJECT_OSSD_MAPPINGS = "data/rpgf3_ossd_mappings.csv" -CANONICAL_PROJECTS_LIST = "data/canonical_projects.csv" -PAYOUT_ADDRESS_CSV = "data/payout_addresses.csv" - - -def update_canonical_projects(): - - df1 = pd.read_csv(PROJECT_OSSD_MAPPINGS, index_col='Project ID') - df2 = pd.read_csv(PROJECT_SUMMARY_CSV, index_col='id') - cols = ["applicantType", "contributionLinkCount", "impactMetricCount", "urlTypeCount"] - df = df1.join(df2[cols], how='inner') - - try: - df3 = pd.read_csv(PAYOUT_ADDRESS_CSV, index_col="Payout Address") - df = df.join(df3, on='Payout Address', how='inner') - except FileNotFoundError: - print("Looking up EOA status for payout addresses...") - df['isEOA'] = df['Payout Address'].apply(lambda x: is_eoa(chain='optimism', address=x, sleep=0.2)) - - df = df[~df.index.duplicated(keep='first')] - df.to_csv(CANONICAL_PROJECTS_LIST) - print(f"Exported canonical projects list to: {CANONICAL_PROJECTS_LIST}") - -if __name__ == "__main__": - update_canonical_projects() diff --git a/indexer/utilities/rpgf3-attestations/src/fetch_from_eas.py b/indexer/utilities/rpgf3-attestations/src/fetch_from_eas.py deleted file mode 100644 index 20a05ce12..000000000 --- a/indexer/utilities/rpgf3-attestations/src/fetch_from_eas.py +++ /dev/null @@ -1,274 +0,0 @@ -import json -import os -import pandas as pd -import requests - -from oss_directory import check_for_ossd_membership - - -EAS_SCHEMA = "0x76e98cce95f3ba992c2ee25cef25f756495147608a3da3aa2e5ca43109fe77cc" -APPROVAL_SCHEMA = "0xebbf697d5d3ca4b53579917ffc3597fb8d1a85b8c6ca10ec10039709903b9277" -APPROVER_ADDRESS = "0x621477dBA416E12df7FF0d48E14c4D20DC85D7D9" -START_TIME = 0 -END_TIME = 1698136205 -RAW_APPLICANT_JSON = "data/raw_applicant_data.json" -CLEANED_APPLICANT_JSON = "data/cleaned_applicant_data.json" -CLEANED_APPLICANT_CSV = "data/cleaned_applicant_data.csv" -PROJECT_OSSD_MAPPINGS = "data/rpgf3_ossd_mappings.csv" - - -def fetch_attestations(schema_id, time_created_after=0): - - url = 'https://optimism.easscan.org/graphql' - query_limit = 100 - - query = ''' - query Attestations($schemaId: StringFilter!, $skip: Int!, $take: Int!, $timeCreatedAfter: IntFilter) { - attestations(where: {schemaId: $schemaId, timeCreated: $timeCreatedAfter}, take: $take, skip: $skip) { - id - attester - recipient - refUID - revocable - revocationTime - expirationTime - timeCreated - decodedDataJson - } - } - ''' - - variables = { - "schemaId": { - "equals": schema_id - }, - "skip": 0, - "take": query_limit, - "timeCreatedAfter": {"gt": time_created_after}, - } - - headers = { - 'Content-Type': 'application/json', - } - - all_attestations = [] - - while True: - payload = { - 'query': query, - 'variables': variables - } - - try: - response = requests.post(url, headers=headers, data=json.dumps(payload)) - response.raise_for_status() - - data = response.json() - attestations = data.get('data', {}).get('attestations', []) - all_attestations.extend(attestations) - - if len(attestations) < query_limit: - break - - variables["skip"] += query_limit - - except (requests.exceptions.RequestException, json.JSONDecodeError) as e: - print(f"Failed to fetch attestations for {schema_id}: {str(e)}") - break - - print(f"Total attestations for Schema ID {schema_id}: {len(all_attestations)}") - return all_attestations - - -def fetch_json_data(url): - - try: - response = requests.get(url) - response.raise_for_status() - return response.json() - except (requests.exceptions.RequestException, json.JSONDecodeError) as e: - print(f"Error fetching JSON data from URL: {url}. Error: {str(e)}") - return None - - -def fetch_and_update_json_data(data_entry): - - for data_item in data_entry: - value = data_item.get("value", {}).get("value", {}) - if isinstance(value, str) and ".json" in value: - json_data = fetch_json_data(value) - if json_data: - data_item.update({"json_data": json_data}) - - -def update_data(schema_id, json_outpath): - - data = [] - last_time_created = 0 - - if os.path.exists(json_outpath): - with open(json_outpath, "r") as json_file: - data = json.load(json_file) - if len(data) > 0: - last_time_created = max([a["timeCreated"] for a in data]) - else: - last_time_created = START_TIME - - schema_attestations = fetch_attestations(schema_id, time_created_after=last_time_created) - - indexed_data = [] - for a in schema_attestations: - if a["timeCreated"] > last_time_created: - continue - decoded_data = json.loads(a["decodedDataJson"]) - fetch_and_update_json_data(decoded_data) - indexed_data.append({ - "id": a["id"], - "attester": a["attester"], - "timeCreated": a["timeCreated"], - "data": decoded_data - }) - - data.extend(indexed_data) - - with open(json_outpath, "w") as json_file: - json.dump(data, json_file, indent=4) - - return data - - -def clean_data(original_data): - - try: - project_name = original_data["data"][0]["value"]["value"] - project_link = original_data["data"][2]["value"]["value"] - json_data = original_data["data"][2].get("json_data", None) - - if json_data is None: - print(f"No JSON data found for {project_name} at {project_link}.") - return None - - transformed_data = { - "Project ID": original_data["id"], - "Project Name": project_name, - "Applicant Type": json_data["applicantType"], - "Bio": json_data["bio"], - "Website": json_data["websiteUrl"], - "Date": original_data["timeCreated"], - "Attester Address": original_data["attester"], - "Payout Address": json_data["payoutAddress"], - "Link": project_link, - "Tags": json_data["impactCategory"], - "Contribution Description": json_data["contributionDescription"], - "Contributions: Github": [item["url"].strip("/") for item in json_data["contributionLinks"] if item["type"] == "GITHUB_REPO" and "https://github.com/" in item["url"] and len(item["url"]) > 20], - "Contributions: Contracts": [item["url"] for item in json_data["contributionLinks"] if item["type"] == "CONTRACT_ADDRESS" and "0x" in item["url"]], - "Contributions: Other": [item["url"] for item in json_data["contributionLinks"] if item["type"] == "OTHER"], - "Impact Description": json_data["impactDescription"], - "Impact Metrics": [item["url"] for item in json_data["impactMetrics"]], - "Funding Sources": [item["type"] for item in json_data["fundingSources"]] - } - return transformed_data - except KeyError: - print(f"Error cleaning data: Missing key in original data.") - return None - - -class SetEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, set): - return list(obj) - return json.JSONEncoder.default(self, obj) - - -def export_cleaned_data(raw_data, json_outpath, csv_outpath): - - cleaned_data = [clean_data(data) for data in raw_data] - cleaned_data = [data for data in cleaned_data if data is not None] - - check_for_ossd_membership(cleaned_data) - - with open(json_outpath, "w") as json_file: - json.dump(cleaned_data, json_file, indent=4, cls=SetEncoder) - - for entry in cleaned_data: - entry["Tags"] = ", ".join(entry["Tags"]) - entry["Github (First Link)"] = entry["Contributions: Github"][0] if len(entry["Contributions: Github"]) > 0 else "" - - csv_data = pd.DataFrame(cleaned_data) - fieldnames = ["Project Name", "Applicant Type", "Date", "Attester Address", "Payout Address", "Link", "Tags", "Github (First Link)", "OSS Directory"] - csv_data = csv_data[fieldnames] - csv_data.sort_values(by="Date", inplace=True) - csv_data.drop_duplicates(subset=["Link"], keep="last", inplace=True) - csv_data.to_csv(csv_outpath, index=False) - - return cleaned_data - - -def export_canonical_projects_list(cleaned_data, csv_outpath): - - csv_data = pd.DataFrame(cleaned_data) - cols = ["Project ID", "Project Name", "Slug: Primary", "Payout Address", "Slugs: Github", - "Slugs: Contract Address", "Slugs: Attester Address", "Slugs: Payout Address", "Contributions: Github"] - csv_data = csv_data[cols] - csv_data.sort_values(by=["Slug: Primary", "Payout Address"], inplace=True) - - sep = " | " - csv_data["Slugs: Github"] = csv_data["Slugs: Github"].apply(lambda x: sep.join(x)) - csv_data["Slugs: Contract Address"] = csv_data["Slugs: Contract Address"].apply(lambda x: sep.join(x)) - csv_data["Slugs: Attester Address"] = csv_data["Slugs: Attester Address"].apply(lambda x: sep.join(x)) - csv_data["Slugs: Payout Address"] = csv_data["Slugs: Payout Address"].apply(lambda x: sep.join(x)) - - csv_data.loc[csv_data["Slugs: Github"] != "", "Contributions: Github"] = None - - csv_data.to_csv(csv_outpath, index=False) - - -def fetch_approved_project_ids(): - - approved_projects = fetch_attestations(APPROVAL_SCHEMA) - approved_project_ids = {} - rejected_project_ids = [] - for a in approved_projects: - if a["attester"] != APPROVER_ADDRESS: - continue - if a["revocationTime"]: - continue - data = json.loads(a["decodedDataJson"]) - if data[0]['value']['value'] == True: - approved_project_ids[a["refUID"]] = a['id'] - else: - print("Rejected:", a["refUID"]) - rejected_project_ids.append(a["refUID"]) - - for rejected_id in rejected_project_ids: - if rejected_id in approved_project_ids: - del approved_project_ids[rejected_id] - return approved_project_ids - - -def main(): - - print("Fetching project applications from EAS...") - data = update_data(EAS_SCHEMA, RAW_APPLICANT_JSON) - print("Dumped raw application data to:", RAW_APPLICANT_JSON) - print() - print("Fetching project approvals from EAS...") - approved_project_ids = fetch_approved_project_ids() - approved_data = [] - for project in data: - if project["id"] in approved_project_ids: - project["id"] = approved_project_ids[project["id"]] - approved_data.append(project) - print(f"Found {len(approved_data)} approved projects.") - print() - print("Cleaning application data...") - cleaned_data = export_cleaned_data(approved_data, CLEANED_APPLICANT_JSON, CLEANED_APPLICANT_CSV) - print("Export cleaned application data to:", CLEANED_APPLICANT_JSON) - print() - print("Generating a list of projects in OSS Directory...") - export_canonical_projects_list(cleaned_data, PROJECT_OSSD_MAPPINGS) - print("Exported projects list to:", PROJECT_OSSD_MAPPINGS) - - -if __name__ == "__main__": - main() diff --git a/indexer/utilities/rpgf3-attestations/src/oss_directory.py b/indexer/utilities/rpgf3-attestations/src/oss_directory.py deleted file mode 100644 index 083e812a8..000000000 --- a/indexer/utilities/rpgf3-attestations/src/oss_directory.py +++ /dev/null @@ -1,215 +0,0 @@ -from dotenv import load_dotenv -import json -import os -import yaml - -from parse_links import Parser - -load_dotenv() -LOCAL_PATH = os.getenv("LOCAL_PATH_TO_OSSD") - -OSSD_SNAPSHOT_JSON = "data/ossd_snapshot.json" -DUPLICATE_SLUG_RESOLVER = { - "bootnodedev": "0x31fa3f2b3ab4221bb89dd64e864339a6c59ab6658d91860e239e2491f4bb2435", - "filosofiacodigo": "0xaa3b9caf85727a484ac82d25594adc78d68fef1291de82be680bf00436164741", - "gitcoin": "0x60085a98f5c48d97a28d7b45d193f9a734d1704e257df91827459b09565e0e47", # Gitcoin and Passport - "itu-blockchain": "0x0517a9d930d480d5478b6b173e54376cd03ec48d17574baadde2a6322560c7a7", # OPClave & ITU - "lxdao-official": "0x79aaf24aab7a9cffbaffd2003b262aa452d31b5a07fb17c54021479284c564be", # Various - "metagov": "0xe367584886bdb185038a466f6a8e5a4ad746ffef051507a311cae85ae11c7d5e", # MetaGov and DAOstar - "playmint": "0x60e8b63d4c87c43409731ac1370379af9884f7239cf69b052fb36c14d121f736", # Client Side Proofs and Downstream - "polynomial-protocol": "0x75815ccd858e6b97c6e8435e24ec66b1bc4d67c4775a81d4b49a8c58b62daf3f", # Optimitic Indexer & Polynomial Bytes - "rainbow": "0xe9140c31f3a448f0f52124b3f9782f7947d698a686ceb20f77f60366670f3f8e", # Rainbow Kit and Rainbow Me - "reth-paradigmxyz": "0x356900a8a5aa893d522dfcd67cc89f6472f8793ea8cf41088b47833ff1c16b5f", # OP Reth - "synapse": "0x3d2fe07760753070d5f3fe44fb92923065c688b56f3069906d8eae4cc3867fb6", # Synapse Labs and Synapse DAO - "ultrasoundmoney": "0x5e97794e6f3b8af9c3b97da269e51387e788cb4b3c609d708aaa668538ef1de2", # Ultra Sound Money and Relay - "vyperlang": "0x023dfdedcfb50385c8c24fd86489a7f016633219c66583c71f154d7af99c6b51", # Vyper, Venom & Fang, Titanoboa - "waffle-truefieng": "0x877ded44fae0833c412ef1b802419a7d6d9e668c6f92f1fb439dbbd40ca12b2d", # Waffle and Vaults.fyi - "banklessdao": "0x023ec749b1a3ad4335595edad791b6dd5523a94817f53cba336060779471c3c8" # Bankless DAO and International Media Nodes -} - - -def get_yaml_files(path): - yaml_files = [] - for root, dirs, files in os.walk(path): - for file in files: - if file.endswith(".yaml"): - yaml_files.append(os.path.join(root, file)) - return yaml_files - - -def get_yaml_data(yaml_files): - yaml_data = [] - for file in yaml_files: - with open(file, 'r') as stream: - try: - data = yaml.safe_load(stream) - if data: - yaml_data.append(data) - except yaml.YAMLError as exc: - print(f"Error in {file}: {exc}") - return yaml_data - - -def get_yaml_data_from_path(): - yaml_files = get_yaml_files(LOCAL_PATH) - if not yaml_files: - print("No YAML files found.") - return [] - - print(f"Found {len(yaml_files)} yaml files.") - yaml_data = get_yaml_data(yaml_files) - print(f"Ingested {len(yaml_data)} yaml records.") - return yaml_data - - -def map_addresses_to_slugs(yaml_data, chain, lowercase=True): - """ - Returns a mapping of addresses to slugs. - Example: - { - "0x1234...": "project-slug" - } - """ - addresses = {} - for data in yaml_data: - if not data: - continue - slug = data['slug'] - blockchain_entries = data.get('blockchain', []) - if not blockchain_entries: - continue - for entry in blockchain_entries: - if chain not in entry.get('networks', []): - continue - address = entry.get('address', None) - if address: - if lowercase: - address = address.lower() - addresses[address] = slug - return addresses - - -def map_repos_to_slugs(yaml_data, lowercase=True): - """ - Returns a mapping of github repo urls to slugs. - Example: - { - "https://github.com/my-repo": "project-slug" - } - """ - repos = {} - for data in yaml_data: - if not data: - continue - slug = data['slug'] - repo_entries = data.get('github', []) - if not repo_entries: - continue - for entry in repo_entries: - url = entry.get('url', None) - if url: - if lowercase: - url = url.lower().strip('/') - repos[url] = slug - return repos - - -def check_for_ossd_membership(cleaned_data): - - with open(OSSD_SNAPSHOT_JSON, "r") as json_file: - ossd_data = json.load(json_file) - - # TODO: handle this more elegantly - github_slugs_to_ignore = ['op', 'spellbook-duneanalytics'] - repos_to_slugs = { - repo.replace("https://github.com/",""): slug - for repo,slug in ossd_data["repos"].items() - if slug not in github_slugs_to_ignore - } - - addresses_to_slugs = ossd_data["addresses"] - address_set = set(addresses_to_slugs.keys()) - - for project in cleaned_data: - - project["OSS Directory"] = "Not Found" - address_found = False - contract_found = False - github_found = False - - project["Slugs: Payout Address"] = set() - project["Slugs: Attester Address"] = set() - project["Slugs: Contract Address"] = set() - project["Slugs: Github"] = set() - project["Slug: Primary"] = None - - if project["Payout Address"].lower() in address_set: - address_found = True - project["Slugs: Payout Address"].add(addresses_to_slugs[project["Payout Address"].lower()]) - - if project["Attester Address"].lower() in address_set: - address_found = True - project["Slugs: Attester Address"].add(addresses_to_slugs[project["Attester Address"].lower()]) - - for url in project["Contributions: Contracts"]: - owner = Parser.etherscan(url)[1] - if owner is None: - continue - if owner in address_set: - contract_found = True - project["Slugs: Contract Address"].add(addresses_to_slugs[owner]) - - for url in project["Contributions: Github"]: - repo = Parser.github(url)[1] - if repo is None: - continue - owner = repo.split("/")[0] if "/" in repo else repo - if repo in repos_to_slugs: - github_found = True - project["Slugs: Github"].add(repos_to_slugs[repo]) - elif owner in repos_to_slugs: - github_found = True - project["Slugs: Github"].add(repos_to_slugs[owner]) - - if address_found and github_found: - project["OSS Directory"] = "Address & Github Found" - elif address_found: - project["OSS Directory"] = "Address Found" - elif github_found: - project["OSS Directory"] = "Github Found" - else: - continue - - intersection = project["Slugs: Payout Address"].intersection(project["Slugs: Github"]) - union = project["Slugs: Payout Address"].union(project["Slugs: Github"]).union(project["Slugs: Contract Address"]) - if len(intersection) == 1: - project["Slug: Primary"] = list(intersection)[0] - elif len(intersection) > 1: - project["Slug: Primary"] = list(project["Slugs: Payout Address"])[0] - elif len(project["Slugs: Github"]) == 1: - project["Slug: Primary"] = list(project["Slugs: Github"])[0] - elif len(union) >= 1 and project["OSS Directory"] == "Address Found": - project["Slug: Primary"] = list(project["Slugs: Payout Address"])[0] - - if project["Slug: Primary"] in DUPLICATE_SLUG_RESOLVER: - expected_id = DUPLICATE_SLUG_RESOLVER[project["Slug: Primary"]] - if project["Project ID"] != expected_id: - print(f"Duplicate: {project['Project ID']} referencing {project['Slug: Primary']}") - project["Slug: Primary"] = None - - -def main(): - - yaml_data = get_yaml_data_from_path() - addresses = map_addresses_to_slugs(yaml_data, chain='optimism') - repos = map_repos_to_slugs(yaml_data) - - with open(OSSD_SNAPSHOT_JSON, 'w') as outfile: - json.dump({ - 'addresses': addresses, - 'repos': repos - }, outfile, indent=4) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/indexer/utilities/rpgf3-attestations/src/parse_links.py b/indexer/utilities/rpgf3-attestations/src/parse_links.py deleted file mode 100644 index 1399357c4..000000000 --- a/indexer/utilities/rpgf3-attestations/src/parse_links.py +++ /dev/null @@ -1,110 +0,0 @@ -import re -from urllib.parse import urlparse - - -class Parser: - @classmethod - def parse_url(cls, url, domain_check, success_callback, validate_path=True): - if not isinstance(url, str): - return "error: no data", None - - url = url.strip().lower() - - if not domain_check(url): - return "error: no data", None - - if validate_path: - paths = urlparse(url).path.split('/') - if not len(paths) > 1 or not len(paths[1]): - return "error: no data", None - elif len(paths[1]) <= 2: - return f"review: {url}", None - - return success_callback(url) - - @classmethod - def extract_matches(cls, url, pattern): - matches = re.findall(pattern, url) - return "success", matches[0] if matches else None - - @classmethod - def github(cls, url): - def github_domain_check(url): - return 'github.com' in url - - def github_success_callback(url): - url = url.replace("orgs/","").replace("repositories","").strip("/") - paths = urlparse(url).path.split('/') - if len(paths) == 2 and "?" not in paths[1] and paths[1] != "search": - return "success", paths[1] - elif len(paths) >= 3 and "?" not in paths[2]: - return "success", paths[1] + "/" + paths[2] - return f"review: {url}", None - - return cls.parse_url(url, github_domain_check, github_success_callback) - - @classmethod - def etherscan(cls, url): - def etherscan_domain_check(url): - return 'etherscan.io' in url - - def etherscan_success_callback(url): - if '/txs' in url or '/token' in url: - return f"review: {url}", None - eth_address_pattern = r'(0x[a-fA-F0-9]{40})' - return cls.extract_matches(url, eth_address_pattern) - - return cls.parse_url(url, etherscan_domain_check, etherscan_success_callback) - - @classmethod - def npm(cls, url): - def npm_domain_check(url): - return 'npmjs.com' in url or 'npm.im' in url or 'npm-stat' in url - - def npm_success_callback(url): - if 'npmjs.com' in url: - paths = urlparse(url).path.split('/') - return "success", "/".join(paths[2:]) - if 'npm.im' in url: - paths = urlparse(url).path.split('/') - return "success", "/".join(paths[1:]) - if 'npm-stat' in url: - package = url.split('?package=')[1].split('&')[0] - package = package.replace('%40', '@').replace('%2f', '/') - return "success", package - return f"review: {url}", None - - return cls.parse_url(url, npm_domain_check, npm_success_callback) - - @classmethod - def twitter(cls, url): - def twitter_domain_check(url): - return 'twitter.com' in url or 'x.com' in url - - def twitter_success_callback(url): - paths = urlparse(url).path.split('/') - if len(paths) >= 2 and paths[1] not in ["search", "home"]: - return "success", paths[1] - return f"review: {url}", None - - return cls.parse_url(url, twitter_domain_check, twitter_success_callback) - - @classmethod - def substack(cls, url): - def substack_domain_check(url): - return 'substack.com' in url - - def substack_success_callback(url): - domain = urlparse(url) - try: - subdomain = domain.hostname.split('.')[0] - return "success", subdomain - except: - return f"review: {url} {len(paths)}", None - - return cls.parse_url(url, substack_domain_check, substack_success_callback,validate_path=False) - - -# Usage example: -result, data = Parser.etherscan("https://optimistic.etherscan.io/tx/0xcbae480ab46d58588ab81f3c59551713cd364f5df38e4e6ed917e51f9a2db2bb") -print(result, data) \ No newline at end of file diff --git a/indexer/utilities/rpgf3-attestations/src/refresh_views.py b/indexer/utilities/rpgf3-attestations/src/refresh_views.py deleted file mode 100644 index 7c04e3070..000000000 --- a/indexer/utilities/rpgf3-attestations/src/refresh_views.py +++ /dev/null @@ -1,75 +0,0 @@ -from dotenv import load_dotenv -import os -import psycopg2 -import time - - -def connect_to_database(): - - load_dotenv() - db_host = os.getenv("DB_HOST") - db_port = os.getenv("DB_PORT") - db_name = os.getenv("DB_NAME") - db_user = os.getenv("DB_USER") - db_password = os.getenv("DB_PASSWORD") - connection_string = f"postgres://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}" - - try: - connection = psycopg2.connect(connection_string) - return connection - except psycopg2.Error as e: - print("Error connecting to the database:", e) - return None - - -def refresh_mv(sql): - conn = connect_to_database() - conn.autocommit = True - cursor = conn.cursor() - try: - cursor.execute(sql) - except Exception as e: - print(e) - finally: - cursor.close() - - -def main(): - - start_date = '2000-01-01' - end_date = '2023-11-20' - mvs = [ - 'events_daily_to_artifact', - 'events_daily_to_project', - 'events_daily_from_artifact', - 'events_daily_from_project', - - 'events_monthly_to_artifact', - 'events_monthly_to_project', - 'events_monthly_from_artifact', - 'events_monthly_from_project' - - 'events_weekly_to_artifact', - 'events_weekly_to_project', - 'events_weekly_from_artifact', - 'events_weekly_from_project', - ] - for mv in mvs: - - sql = f"CALL refresh_continuous_aggregate('{mv}', '{start_date}', '{end_date}');" - print("Executing...", mv) - start_time = time.time() - refresh_mv(sql) - end_time = time.time() - print(f"Query took: {round(end_time-start_time)} seconds") - - print("Executing FirstContributionToProject...") - sql = "REFRESH MATERIALIZED VIEW first_contribution_to_project;" - refresh_mv(sql) - - print("Executing LastContributionToProject...") - sql = "REFRESH MATERIALIZED VIEW last_contribution_to_project;" - refresh_mv(sql) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/indexer/utilities/rpgf3-attestations/src/reviewer.py b/indexer/utilities/rpgf3-attestations/src/reviewer.py deleted file mode 100644 index 27a1aca9e..000000000 --- a/indexer/utilities/rpgf3-attestations/src/reviewer.py +++ /dev/null @@ -1,145 +0,0 @@ -import json -import pandas as pd - -from parse_links import Parser -from address_lookup import is_eoa, fetch_contract_name - - -CLEANED_APPLICANT_JSON = "data/cleaned_applicant_data.json" -OSSD_SNAPSHOT_JSON = "data/ossd_snapshot.json" -OSSD_REVIEWER_CSV = "data/ossd_reviewer.csv" - -ETHERSCAN_BASE = "https://optimistic.etherscan.io/address/" -GITHUB_BASE = "https://github.com/" - - -def normalize_github_url(url): - return url.lower().replace(GITHUB_BASE, "").strip('/') - - -def process_wallet(payout_address): - - result = { - "artifact": payout_address, - "type": ["wallet"], - "status": "review", - "artifact_name": "RPGF3 Payout Address" - } - if is_eoa('optimism', payout_address): - result['type'].append('eoa') - result['status'] = 'OK' - else: - contract_name = fetch_contract_name('optimism', payout_address) - if contract_name is not None and 'safe' in contract_name.lower(): - result['type'].append('safe') - result['status'] = 'OK' - result['type'] = " ".join(result['type']) - - return result - - -def process_github(github_url): - - github_url = normalize_github_url(github_url) - result = { - "artifact": github_url, - "type": "github", - "status": "review", - "artifact_name": "Github URL" - } - artifact = Parser.github(github_url) - if artifact is not None and artifact[1] is not None: - repo = artifact[1] - result['artifact'] = GITHUB_BASE + repo - result['status'] = "OK" - - return result - - -def process_contract(contract_url): - - result = { - "artifact": contract_url, - "type": "contract", - "status": "review", - "artifact_name": None, - } - artifact = Parser.etherscan(contract_url) - if artifact is not None and artifact[1] is not None: - address = artifact[1].lower() - result['artifact'] = address - if is_eoa('optimism', address): - result['type'] = "eoa" - else: - contract_name = fetch_contract_name('optimism', address) - if contract_name is not None: - result['artifact_name'] = contract_name - contract_name = contract_name.lower() - if 'safe' in contract_name: - result['type'] = "safe" - elif 'factory' in contract_name: - result['type'] = "contract factory" - result['status'] = "OK" - else: - result['status'] = "OK" - - return result - - -def review_data(): - - cleaned_applicant_data = json.load(open(CLEANED_APPLICANT_JSON, 'r')) - existing_data = json.load(open(OSSD_SNAPSHOT_JSON, 'r')) - existing_data['repos'] = {normalize_github_url(k): v for k, v in existing_data['repos'].items()} - - records = [] - for project in cleaned_applicant_data: - - name = project['Project Name'] - project_type = project['Applicant Type'] - - # TODO: handle individual projects - if project_type == 'INDIVIDUAL': - continue - - slugs = project['Slug(s)'] - if len(slugs) > 1: - slug = " && ".join(slugs) - elif not slugs: - slug = None - else: - slug = slugs[0] - - props = dict(name=name, slug=slug, project_type=project_type) - - payout_address = project['Payout Address'].lower() - wallet = process_wallet(payout_address) - if payout_address not in existing_data['addresses']: - records.append({**props, **wallet, "workflow": "new"}) - else: - records.append({**props, **wallet, "workflow": "existing"}) - - githubs = project['Contributions: Github'] - for github_url in githubs: - github_artifact = process_github(github_url) - if github_artifact['artifact'] not in existing_data['repos']: - records.append({**props, **github_artifact, "workflow": "new"}) - else: - records.append({**props, **github_artifact, "workflow": "existing"}) - - contracts = project['Contributions: Contracts'] - for contract_url in contracts: - if 'goerli' in contract_url or 'mirror' in contract_url: - continue - contract_artifact = process_contract(contract_url) - if contract_artifact['artifact'] not in existing_data['addresses']: - records.append({**props, **contract_artifact, "workflow": "new"}) - else: - records.append({**props, **contract_artifact, "workflow": "existing"}) - - df = pd.DataFrame(records) - df.to_csv(OSSD_REVIEWER_CSV) - - -if __name__ == "__main__": - review_data() diff --git a/indexer/utilities/rpgf3-attestations/src/tablist.py b/indexer/utilities/rpgf3-attestations/src/tablist.py deleted file mode 100644 index c5bcafa78..000000000 --- a/indexer/utilities/rpgf3-attestations/src/tablist.py +++ /dev/null @@ -1,209 +0,0 @@ -import json -import os -import pandas as pd -import requests - - -START_TIME = 1699246800 # 6 Nov 2023 00:00:00 -VOTERS_SCHEMA = "0xfdcfdad2dbe7489e0ce56b260348b7f14e8365a8a325aef9834818c00d46b31b" -LIST_SCHEMA = "0x3e3e2172aebb902cf7aa6e1820809c5b469af139e7a4265442b1c22b97c6b2a5" -DATA_EXPORT_JSON = "data/list_data.json" -DATA_EXPORT_CSV = "data/list_data.csv" - - -def fetch_attestations(schema_id, time_created_after=0): - """ - Generalized function to fetch attestations for a given schema ID. - """ - - url = 'https://optimism.easscan.org/graphql' - query_limit = 100 - - query = ''' - query Attestations($schemaId: StringFilter!, $skip: Int!, $take: Int!, $timeCreatedAfter: IntFilter) { - attestations(where: {schemaId: $schemaId, timeCreated: $timeCreatedAfter}, take: $take, skip: $skip) { - id - attester - recipient - refUID - revocable - revocationTime - expirationTime - timeCreated - decodedDataJson - } - } - ''' - - variables = { - "schemaId": { - "equals": schema_id - }, - "skip": 0, - "take": query_limit, - "timeCreatedAfter": { - "gt": time_created_after - }, - } - - headers = { - 'Content-Type': 'application/json', - } - - all_attestations = [] - - while True: - payload = { - 'query': query, - 'variables': variables - } - - try: - response = requests.post(url, headers=headers, data=json.dumps(payload)) - response.raise_for_status() - - data = response.json() - attestations = data.get('data', {}).get('attestations', []) - all_attestations.extend(attestations) - - if len(attestations) < query_limit: - break - - variables["skip"] += query_limit - - except (requests.exceptions.RequestException, json.JSONDecodeError) as e: - print(f"Failed to fetch attestations for {schema_id}: {str(e)}") - break - - print(f"Total attestations for Schema ID {schema_id}: {len(all_attestations)}") - return all_attestations - - -def fetch_all_attestations (app_schema_id): - """ - Fetch all lists from EAS and decode the data. - """ - - lists = fetch_attestations(app_schema_id) - list_data = [] - for lst in lists: - if lst["revocationTime"]: - continue - decoded_data = json.loads(lst["decodedDataJson"]) - list_data.append({ - **lst, - "data": decoded_data - }) - return list_data - - -def fetch_json_data(url): - """ - Fetch JSON data from a URL. - """ - - try: - response = requests.get(url) - response.raise_for_status() - return response.json() - except (requests.exceptions.RequestException, json.JSONDecodeError) as e: - print(f"Error fetching JSON data from URL: {url}. Error: {str(e)}") - return None - - -def dump_json(data, path): - path = f"data/lists/{path}.json" - with open(path, "w") as json_file: - json.dump(data, json_file, indent=4) - - -def request_json_data(attestations): - """ - Request and insert the JSON data for each attestation. - """ - - for a in attestations: - url = a["data"][2]["value"]["value"] - gateway = "https://cloudflare-ipfs.com" - if isinstance(url, str): - url = url.replace("https://w3s.link", gateway) - if "https://" not in url: - url = f"{gateway}/ipfs/{url}" - print(url) - json_data = fetch_json_data(url) - if json_data: - a.update({"json_data": json_data}) - dump_json(json_data, a["id"]) - - -def generate_csv(voting_lists): - csv_data = [] - for vlist in voting_lists: - list_id = vlist['id'] - attester = vlist['attester'] - list_name = vlist['data'][0]['value']['value'] - time_created = vlist['timeCreated'] - - if 'json_data' not in vlist: - print(f"Missing data for list {list_id}") - continue - allocations = {item['RPGF3_Application_UID']: item['OPAmount'] for item in vlist['json_data']['listContent']} - - impact_category = vlist['json_data']['impactCategory'] - if not isinstance(impact_category, list): - continue - series = pd.Series(allocations) - stats = { - "count": series.count(), - "sum": series.sum(), - "mean": series.mean(), - "std": series.std(), - "min": series.min(), - "max": series.max() - } - csv_row = { - 'id': list_id, - 'attester': attester, - 'listName': list_name, - 'timeCreated': time_created, - **stats, - **allocations - } - csv_data.append(csv_row) - df = pd.DataFrame(csv_data) - return df - - -def main(): - - voters_data = fetch_all_attestations(VOTERS_SCHEMA) - voters = [v["recipient"] for v in voters_data] - print(f"\nTotal valid voters: {len(voters)}") - - data = fetch_all_attestations(LIST_SCHEMA) - data = [ - d for d in data - if ( - d["attester"] in voters - and d["attester"] == d["recipient"] - and d['timeCreated'] > START_TIME - ) - ] - print(f"\nTotal valid lists: {len(data)}") - - print("\nRequesting JSON data for valid lists (this may take a minute)...") - request_json_data(data) - - with open(DATA_EXPORT_JSON, "w") as json_file: - json.dump(data, json_file, indent=4) - print(f"\nExported data to {DATA_EXPORT_JSON}.") - - df = generate_csv(data) - df.sort_values(by=['timeCreated'], inplace=True) - df.set_index('id', inplace=True, drop=True) - df.drop(['attester', 'timeCreated'], axis=1, inplace=True) - df.T.to_csv(DATA_EXPORT_CSV) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/indexer/utilities/rpgf3-attestations/src/workflow.sh b/indexer/utilities/rpgf3-attestations/src/workflow.sh deleted file mode 100644 index 63f54f112..000000000 --- a/indexer/utilities/rpgf3-attestations/src/workflow.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -echo "*******************************************" -echo "Indexing local version of OSS Directory..." -python src/oss_directory.py -echo "" - -echo "*******************************************" -echo "Reviewing EAS applications..." -python src/fetch_from_eas.py -echo "" - -echo "*******************************************" -echo "Analyzing projects' contribution links & impact metrics..." -python src/analyze_apps.py -echo "" - -echo "*******************************************" -echo "Updating the canonical list of projects..." -python src/canonical.py - -#python src/reviewer.py \ No newline at end of file diff --git a/oso/__init__.py b/oso/__init__.py deleted file mode 100644 index 352915ec6..000000000 --- a/oso/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# This is a dummy __init__.py because of an issue with python poetry. We are -# waiting for https://github.com/python-poetry/poetry/pull/8650 to be merged -# then we can remove this. We added this here because without it the -# installation for the dbt folder appears to error on some versions of poetry. diff --git a/package.json b/package.json index d1123c24e..5fd95c4c2 100644 --- a/package.json +++ b/package.json @@ -15,20 +15,17 @@ "build:docs": "turbo run build --filter=@opensource-observer/docs", "build:frontend": "turbo run build --filter=@opensource-observer/frontend", "build:hasura": "turbo run build --filter=@opensource-observer/hasura", - "build:indexer": "turbo run build --filter=@opensource-observer/indexer", "copy": "yarn copy:frontend && yarn copy:docs && yarn copy:html", "copy:docs": "mkdir -p ./build/docs/ && cp -r ./docs/build/* ./build/docs/", "copy:frontend": "mkdir -p ./build/ && cp -r ./frontend/out/* ./build/ && cp ./frontend/_redirects ./build/", "copy:html": "find build/ -name '*.html' -type f | grep -v index.html | sed s/\\.html$// | xargs -I _ bash -c 'mkdir -p _ && cp -v _.html _/index.html'", "deploy:hasura": "turbo run deploy --filter=@opensource-observer/hasura --parallel", - "deploy:indexer": "turbo run deploy --filter=@opensource-observer/indexer --parallel", "deploy:site": "turbo run build --filter=@opensource-observer/docs --filter=@opensource-observer/frontend && turbo run deploy --filter=@opensource-observer/frontend && yarn copy", "dev:docs": "turbo run dev --filter=@opensource-observer/docs --parallel", "dev:frontend": "turbo run dev --filter=@opensource-observer/frontend --parallel", "format:staged": "lint-staged", "lint": "turbo run lint --concurrency=100%", "serve": "yarn serve build", - "start:indexer": "turbo run start --filter=@opensource-observer/indexer --", "test": "turbo run test --concurrency=1", "test:integration": "(docker compose -f docker/compose.yaml down || true) && docker compose -f docker/compose.yaml run --rm test", "prepare": "husky install" @@ -45,4 +42,4 @@ "node": ">=18.x", "pnpm": "8" } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24b600508..11b0bb655 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,102 +24,7 @@ importers: specifier: ^1.11.3 version: 1.11.3 - cloudquery/github-resolve-repos: - dependencies: - '@apidevtools/json-schema-ref-parser': - specifier: ^11.1.0 - version: 11.1.0 - '@cloudquery/plugin-sdk-javascript': - specifier: ^0.1.4 - version: 0.1.4 - '@octokit/core': - specifier: ^5.0.0 - version: 5.0.0 - '@octokit/plugin-throttling': - specifier: ^8.0.0 - version: 8.0.0(@octokit/core@5.0.0) - '@octokit/types': - specifier: ^11.1.0 - version: 11.1.0 - '@types/lodash': - specifier: ^4.14.196 - version: 4.14.196 - ajv: - specifier: ^8.12.0 - version: 8.12.0 - camelcase-keys: - specifier: ^9.1.2 - version: 9.1.2 - dayjs: - specifier: ^1.11.9 - version: 1.11.9 - graphql-request: - specifier: ^6.1.0 - version: 6.1.0(graphql@16.8.1) - lodash: - specifier: ^4.17.21 - version: 4.17.21 - octokit: - specifier: ^3.1.0 - version: 3.1.0 - oss-directory: - specifier: ^0.0.7 - version: 0.0.7(ts-node@10.9.1)(typescript@5.2.2) - read-pkg-up: - specifier: ^11.0.0 - version: 11.0.0 - typescript: - specifier: ^5.2.2 - version: 5.2.2 - devDependencies: - json-schema-to-typescript: - specifier: ^13.1.1 - version: 13.1.1 - ts-node: - specifier: ^10.9.1 - version: 10.9.1(@types/node@20.11.17)(typescript@5.2.2) - - cloudquery/oss-directory: - dependencies: - '@cloudquery/plugin-sdk-javascript': - specifier: ^0.1.6 - version: 0.1.6 - '@types/lodash': - specifier: ^4.14.202 - version: 4.14.202 - ajv: - specifier: ^8.12.0 - version: 8.12.0 - camelcase-keys: - specifier: ^9.1.2 - version: 9.1.2 - dayjs: - specifier: ^1.11.9 - version: 1.11.9 - lodash: - specifier: ^4.17.21 - version: 4.17.21 - oss-directory: - specifier: ^0.0.9 - version: 0.0.9(ts-node@10.9.1)(typescript@5.2.2) - read-pkg-up: - specifier: ^11.0.0 - version: 11.0.0 - ts-essentials: - specifier: ^9.4.1 - version: 9.4.1(typescript@5.2.2) - typescript: - specifier: ^5.2.2 - version: 5.2.2 - devDependencies: - json-schema-to-typescript: - specifier: ^13.1.1 - version: 13.1.1 - ts-node: - specifier: ^10.9.1 - version: 10.9.1(@types/node@20.11.17)(typescript@5.2.2) - - docs: + apps/docs: dependencies: '@docusaurus/core': specifier: 3.1.0 @@ -156,7 +61,7 @@ importers: specifier: ~5.3.3 version: 5.3.3 - frontend: + apps/frontend: dependencies: '@apollo/client': specifier: 3.9.0-alpha.4 @@ -284,7 +189,7 @@ importers: devDependencies: '@graphql-codegen/cli': specifier: ^5.0.0 - version: 5.0.0(@types/node@20.11.6)(graphql@16.8.1)(typescript@5.3.3) + version: 5.0.0(@types/node@20.11.17)(graphql@16.8.1)(typescript@5.3.3) '@graphql-codegen/client-preset': specifier: ^4.1.0 version: 4.1.0(graphql@16.8.1) @@ -311,7 +216,7 @@ importers: version: 4.14.202 '@types/node': specifier: ^20 - version: 20.11.6 + version: 20.11.17 '@types/papaparse': specifier: ^5.3.14 version: 5.3.14 @@ -338,7 +243,7 @@ importers: version: 6.19.1(eslint@8.56.0)(typescript@5.3.3) autoprefixer: specifier: ^10.0.1 - version: 10.4.17(postcss@8.4.33) + version: 10.4.17(postcss@8.4.31) eslint: specifier: ^8 version: 8.56.0 @@ -353,13 +258,13 @@ importers: version: 1.1.7 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.11.6) + version: 29.7.0(@types/node@20.11.17) jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0 postcss: specifier: ^8 - version: 8.4.33 + version: 8.4.31 supabase: specifier: ^1.136.3 version: 1.136.3 @@ -370,11 +275,11 @@ importers: specifier: ^5 version: 5.3.3 - hasura: + apps/hasura: dependencies: yaml: specifier: ^2.3.1 - version: 2.3.4 + version: 2.3.1 devDependencies: '@types/node': specifier: ^20.11.17 @@ -392,165 +297,100 @@ importers: specifier: ^5.3.3 version: 5.3.3 - indexer: + warehouse/cloudquery-github-resolve-repos: dependencies: - '@cowprotocol/ts-dune-client': - specifier: ^0.0.2 - version: 0.0.2 - '@datastructures-js/queue': - specifier: ^4.2.3 - version: 4.2.3 - '@google-cloud/bigquery': - specifier: ^7.3.0 - version: 7.3.0 - '@google-cloud/storage': - specifier: ^7.6.0 - version: 7.6.0 - '@jest/globals': - specifier: ^29.7.0 - version: 29.7.0 + '@apidevtools/json-schema-ref-parser': + specifier: ^11.1.0 + version: 11.1.0 + '@cloudquery/plugin-sdk-javascript': + specifier: ^0.1.4 + version: 0.1.6 '@octokit/core': specifier: ^5.0.0 version: 5.0.0 - '@octokit/graphql': - specifier: ^7.0.1 - version: 7.0.1 - '@octokit/plugin-paginate-graphql': - specifier: ^4.0.0 - version: 4.0.0(@octokit/core@5.0.0) '@octokit/plugin-throttling': specifier: ^8.0.0 version: 8.0.0(@octokit/core@5.0.0) '@octokit/types': specifier: ^11.1.0 version: 11.1.0 - '@types/inquirer': - specifier: ^9.0.4 - version: 9.0.4 - async-mutex: - specifier: ^0.4.0 - version: 0.4.0 - chalk: - specifier: ^5.3.0 - version: 5.3.0 - class-validator: - specifier: ^0.14.0 - version: 0.14.0 - csv: - specifier: ^6.3.5 - version: 6.3.5 + '@types/lodash': + specifier: ^4.14.196 + version: 4.14.202 + ajv: + specifier: ^8.12.0 + version: 8.12.0 + camelcase-keys: + specifier: ^9.1.2 + version: 9.1.2 dayjs: specifier: ^1.11.9 - version: 1.11.9 - dotenv: - specifier: ^16.3.1 - version: 16.3.1 - graphql: - specifier: ^16.7.1 - version: 16.7.1 + version: 1.11.10 graphql-request: specifier: ^6.1.0 - version: 6.1.0(graphql@16.7.1) - inquirer: - specifier: ^9.2.11 - version: 9.2.11 - json-to-graphql-query: - specifier: ^2.2.5 - version: 2.2.5 + version: 6.1.0(graphql@16.8.1) lodash: specifier: ^4.17.21 version: 4.17.21 - lru-cache: - specifier: ^10.1.0 - version: 10.1.0 - luxon: - specifier: ^3.4.0 - version: 3.4.0 - mkdirp: - specifier: ^3.0.1 - version: 3.0.1 - node-fetch: - specifier: ^3.3.2 - version: 3.3.2 - npm-registry-fetch: - specifier: ^15.0.0 - version: 15.0.0 octokit: specifier: ^3.1.0 version: 3.1.0 - ora: - specifier: ^7.0.1 - version: 7.0.1 oss-directory: specifier: ^0.0.7 - version: 0.0.7(ts-node@10.9.1)(typescript@5.2.2) - pg: - specifier: ^8.4.0 - version: 8.11.3 - redis: - specifier: ^4.6.11 - version: 4.6.11 - reflect-metadata: - specifier: ^0.1.13 - version: 0.1.13 - rimraf: - specifier: ^5.0.1 - version: 5.0.1 - ts-adt: - specifier: ^2.1.2 - version: 2.1.2 - typeorm: - specifier: ^0.3.17 - version: 0.3.17(pg@8.11.3)(redis@4.6.11)(ts-node@10.9.1) - utility-types: - specifier: ^3.10.0 - version: 3.10.0 - winston: - specifier: ^3.10.0 - version: 3.10.0 - yaml: - specifier: ^2.3.1 - version: 2.3.1 - yargs: - specifier: ^17.7.2 - version: 17.7.2 + version: 0.0.7(ts-node@10.9.1)(typescript@5.3.3) + read-pkg-up: + specifier: ^11.0.0 + version: 11.0.0 + typescript: + specifier: ^5.2.2 + version: 5.3.3 devDependencies: - '@types/jest': - specifier: ^29.5.3 - version: 29.5.3 - '@types/lodash': - specifier: ^4.14.196 - version: 4.14.196 - '@types/luxon': - specifier: ^3.3.1 - version: 3.3.1 - '@types/node': - specifier: ^20.6.3 - version: 20.6.3 - '@types/npm-registry-fetch': - specifier: ^8.0.4 - version: 8.0.4 - '@types/yargs': - specifier: ^17.0.24 - version: 17.0.24 - jest: - specifier: ^29.6.2 - version: 29.6.2(@types/node@20.6.3)(ts-node@10.9.1) - ts-jest: - specifier: ^29.1.1 - version: 29.1.1(@babel/core@7.23.7)(jest@29.6.2)(typescript@5.2.2) - ts-jest-resolver: - specifier: ^2.0.1 - version: 2.0.1 + json-schema-to-typescript: + specifier: ^13.1.1 + version: 13.1.1 ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@20.6.3)(typescript@5.2.2) - tsc-watch: - specifier: ^6.0.4 - version: 6.0.4(typescript@5.2.2) + version: 10.9.1(@types/node@20.11.17)(typescript@5.3.3) + + warehouse/cloudquery-oss-directory: + dependencies: + '@cloudquery/plugin-sdk-javascript': + specifier: ^0.1.6 + version: 0.1.6 + '@types/lodash': + specifier: ^4.14.202 + version: 4.14.202 + ajv: + specifier: ^8.12.0 + version: 8.12.0 + camelcase-keys: + specifier: ^9.1.2 + version: 9.1.2 + dayjs: + specifier: ^1.11.9 + version: 1.11.10 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + oss-directory: + specifier: ^0.0.9 + version: 0.0.9(ts-node@10.9.1)(typescript@5.3.3) + read-pkg-up: + specifier: ^11.0.0 + version: 11.0.0 + ts-essentials: + specifier: ^9.4.1 + version: 9.4.1(typescript@5.3.3) typescript: specifier: ^5.2.2 - version: 5.2.2 + version: 5.3.3 + devDependencies: + json-schema-to-typescript: + specifier: ^13.1.1 + version: 13.1.1 + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@20.11.17)(typescript@5.3.3) packages: @@ -794,6 +634,7 @@ packages: /@apollo/experimental-nextjs-app-support@0.6.0(@apollo/client@3.9.0-alpha.4)(next@14.1.0)(react@18.2.0): resolution: {integrity: sha512-mtkbskOfzasBlqe5mLTu79pd1YuJ2fNuWHE+j5v4P//ZAHG30D2L+l0D/zuZISPFVEfFOdgSkIzR0U60uQ2GSg==} + deprecated: Version is deprecated due to a XSS vulnerability. Please see https://github.com/apollographql/apollo-client-nextjs/security/advisories/GHSA-rv8p-rr2h-fgpg for more information. peerDependencies: '@apollo/client': '>=3.8.0-rc || ^3.8.0 || >=3.9.0-alpha || >=3.9.0-beta || >=3.9.0-rc' next: ^13.4.1 || ^14.0.0 @@ -813,13 +654,13 @@ packages: peerDependencies: graphql: '*' dependencies: - '@babel/core': 7.22.10 - '@babel/generator': 7.22.10 - '@babel/parser': 7.22.10 + '@babel/core': 7.23.7 + '@babel/generator': 7.23.6 + '@babel/parser': 7.23.6 '@babel/runtime': 7.23.8 - '@babel/traverse': 7.22.10 - '@babel/types': 7.22.10 - babel-preset-fbjs: 3.4.0(@babel/core@7.22.10) + '@babel/traverse': 7.23.7 + '@babel/types': 7.23.6 + babel-preset-fbjs: 3.4.0(@babel/core@7.23.7) chalk: 4.1.2 fb-watchman: 2.0.2 fbjs: 3.0.5 @@ -845,13 +686,6 @@ packages: - encoding dev: true - /@babel/code-frame@7.22.10: - resolution: {integrity: sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.22.10 - chalk: 2.4.2 - /@babel/code-frame@7.23.5: resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} engines: {node: '>=6.9.0'} @@ -859,36 +693,10 @@ packages: '@babel/highlight': 7.23.4 chalk: 2.4.2 - /@babel/compat-data@7.22.9: - resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==} - engines: {node: '>=6.9.0'} - /@babel/compat-data@7.23.5: resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} engines: {node: '>=6.9.0'} - /@babel/core@7.22.10: - resolution: {integrity: sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.22.10 - '@babel/helper-compilation-targets': 7.22.10 - '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.10) - '@babel/helpers': 7.22.10 - '@babel/parser': 7.22.10 - '@babel/template': 7.22.5 - '@babel/traverse': 7.22.10 - '@babel/types': 7.22.10 - convert-source-map: 1.9.0 - debug: 4.3.4(supports-color@8.1.1) - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - /@babel/core@7.23.7: resolution: {integrity: sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==} engines: {node: '>=6.9.0'} @@ -911,24 +719,6 @@ packages: transitivePeerDependencies: - supports-color - /@babel/generator@7.22.10: - resolution: {integrity: sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.10 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 - jsesc: 2.5.2 - - /@babel/generator@7.23.5: - resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.5 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.21 - jsesc: 2.5.2 - /@babel/generator@7.23.6: resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} engines: {node: '>=6.9.0'} @@ -942,7 +732,7 @@ packages: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.6 /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15: resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==} @@ -951,16 +741,6 @@ packages: '@babel/types': 7.23.6 dev: false - /@babel/helper-compilation-targets@7.22.10: - resolution: {integrity: sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/compat-data': 7.22.9 - '@babel/helper-validator-option': 7.22.5 - browserslist: 4.22.2 - lru-cache: 5.1.1 - semver: 6.3.1 - /@babel/helper-compilation-targets@7.23.6: resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} engines: {node: '>=6.9.0'} @@ -971,24 +751,6 @@ packages: lru-cache: 5.1.1 semver: 6.3.1 - /@babel/helper-create-class-features-plugin@7.22.15(@babel/core@7.22.10): - resolution: {integrity: sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.22.5 - '@babel/helper-member-expression-to-functions': 7.23.0 - '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.22.10) - '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - semver: 6.3.1 - dev: true - /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.23.7): resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==} engines: {node: '>=6.9.0'} @@ -1005,7 +767,6 @@ packages: '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 semver: 6.3.1 - dev: false /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.7): resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==} @@ -1038,74 +799,30 @@ packages: resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} engines: {node: '>=6.9.0'} - /@babel/helper-environment-visitor@7.22.5: - resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==} - engines: {node: '>=6.9.0'} - - /@babel/helper-function-name@7.22.5: - resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.5 - '@babel/types': 7.23.0 - /@babel/helper-function-name@7.23.0: resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.22.15 - '@babel/types': 7.23.5 + '@babel/types': 7.23.6 /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.6 /@babel/helper-member-expression-to-functions@7.23.0: resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.6 /@babel/helper-module-imports@7.22.15: resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 - - /@babel/helper-module-imports@7.22.5: - resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.10 - - /@babel/helper-module-transforms@7.22.9(@babel/core@7.22.10): - resolution: {integrity: sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-module-imports': 7.22.5 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - - /@babel/helper-module-transforms@7.23.3(@babel/core@7.22.10): - resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - dev: true + '@babel/types': 7.23.6 /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} @@ -1124,7 +841,7 @@ packages: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.6 /@babel/helper-plugin-utils@7.22.5: resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} @@ -1142,18 +859,6 @@ packages: '@babel/helper-wrap-function': 7.22.20 dev: false - /@babel/helper-replace-supers@7.22.20(@babel/core@7.22.10): - resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-member-expression-to-functions': 7.23.0 - '@babel/helper-optimise-call-expression': 7.22.5 - dev: true - /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.7): resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} engines: {node: '>=6.9.0'} @@ -1164,29 +869,24 @@ packages: '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - dev: false /@babel/helper-simple-access@7.22.5: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.6 /@babel/helper-skip-transparent-expression-wrappers@7.22.5: resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.6 /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 - - /@babel/helper-string-parser@7.22.5: - resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} - engines: {node: '>=6.9.0'} + '@babel/types': 7.23.6 /@babel/helper-string-parser@7.23.4: resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} @@ -1196,14 +896,6 @@ packages: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier@7.22.5: - resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} - engines: {node: '>=6.9.0'} - - /@babel/helper-validator-option@7.22.5: - resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} - engines: {node: '>=6.9.0'} - /@babel/helper-validator-option@7.23.5: resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} engines: {node: '>=6.9.0'} @@ -1217,16 +909,6 @@ packages: '@babel/types': 7.23.6 dev: false - /@babel/helpers@7.22.10: - resolution: {integrity: sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.5 - '@babel/traverse': 7.22.10 - '@babel/types': 7.23.6 - transitivePeerDependencies: - - supports-color - /@babel/helpers@7.23.8: resolution: {integrity: sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==} engines: {node: '>=6.9.0'} @@ -1237,14 +919,6 @@ packages: transitivePeerDependencies: - supports-color - /@babel/highlight@7.22.10: - resolution: {integrity: sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.22.20 - chalk: 2.4.2 - js-tokens: 4.0.0 - /@babel/highlight@7.23.4: resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} engines: {node: '>=6.9.0'} @@ -1253,20 +927,6 @@ packages: chalk: 2.4.2 js-tokens: 4.0.0 - /@babel/parser@7.22.10: - resolution: {integrity: sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.22.10 - - /@babel/parser@7.23.5: - resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.23.5 - /@babel/parser@7.23.6: resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} engines: {node: '>=6.0.0'} @@ -1307,31 +967,31 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: false - /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.22.10): + /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.23.7): resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.22.10) + '@babel/core': 7.23.7 + '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.7) '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.22.10): + /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.23.7): resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead. peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.22.9 - '@babel/core': 7.22.10 + '@babel/compat-data': 7.23.5 + '@babel/core': 7.23.7 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.22.10) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.7) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.7) dev: true /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.7): @@ -1343,14 +1003,6 @@ packages: '@babel/core': 7.23.7 dev: false - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.22.10): - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.7): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -1359,14 +1011,6 @@ packages: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.22.10): - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.7): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: @@ -1376,14 +1020,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.22.10): - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.7): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: @@ -1420,23 +1056,13 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: false - /@babel/plugin-syntax-flow@7.22.5(@babel/core@7.22.10): + /@babel/plugin-syntax-flow@7.22.5(@babel/core@7.23.7): resolution: {integrity: sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-import-assertions@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 dev: true @@ -1448,7 +1074,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==} @@ -1460,131 +1085,53 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: false - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.22.10): - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.7): - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.7 - '@babel/helper-plugin-utils': 7.22.5 - - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.22.10): - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.7): - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.7 - '@babel/helper-plugin-utils': 7.22.5 - - /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - - /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.7): - resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.7 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - - /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.22.10): - resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - - /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.7): - resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.7 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.22.10): - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.7): - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.22.10): - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.7): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.7): - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.7): + resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.22.10): - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.7): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.7): - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.7): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.22.10): - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.7): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.7): @@ -1595,14 +1142,6 @@ packages: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.22.10): - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.7): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: @@ -1611,14 +1150,6 @@ packages: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.22.10): - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.7): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: @@ -1637,15 +1168,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: false - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.22.10): - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.7): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} @@ -1655,15 +1177,6 @@ packages: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} engines: {node: '>=6.9.0'} @@ -1672,7 +1185,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.7): resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} @@ -1685,16 +1197,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: false - /@babel/plugin-transform-arrow-functions@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==} engines: {node: '>=6.9.0'} @@ -1703,7 +1205,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.23.7): resolution: {integrity: sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==} @@ -1730,16 +1231,6 @@ packages: '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.7) dev: false - /@babel/plugin-transform-block-scoped-functions@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==} engines: {node: '>=6.9.0'} @@ -1748,17 +1239,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false - - /@babel/plugin-transform-block-scoping@7.23.0(@babel/core@7.22.10): - resolution: {integrity: sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.23.7): resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==} @@ -1768,7 +1248,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==} @@ -1793,24 +1272,6 @@ packages: '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.7) dev: false - /@babel/plugin-transform-classes@7.22.15(@babel/core@7.22.10): - resolution: {integrity: sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.22.5 - '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.22.10) - '@babel/helper-split-export-declaration': 7.22.6 - globals: 11.12.0 - dev: true - /@babel/plugin-transform-classes@7.23.5(@babel/core@7.23.7): resolution: {integrity: sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==} engines: {node: '>=6.9.0'} @@ -1827,18 +1288,6 @@ packages: '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.7) '@babel/helper-split-export-declaration': 7.22.6 globals: 11.12.0 - dev: false - - /@babel/plugin-transform-computed-properties@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/template': 7.22.15 - dev: true /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==} @@ -1849,17 +1298,6 @@ packages: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 '@babel/template': 7.22.15 - dev: false - - /@babel/plugin-transform-destructuring@7.23.0(@babel/core@7.22.10): - resolution: {integrity: sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==} @@ -1869,7 +1307,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==} @@ -1925,25 +1362,15 @@ packages: '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.7) dev: false - /@babel/plugin-transform-flow-strip-types@7.22.5(@babel/core@7.22.10): + /@babel/plugin-transform-flow-strip-types@7.22.5(@babel/core@7.23.7): resolution: {integrity: sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-flow': 7.22.5(@babel/core@7.22.10) - dev: true - - /@babel/plugin-transform-for-of@7.22.15(@babel/core@7.22.10): - resolution: {integrity: sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 + '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 + '@babel/plugin-syntax-flow': 7.22.5(@babel/core@7.23.7) dev: true /@babel/plugin-transform-for-of@7.23.3(@babel/core@7.23.7): @@ -1954,19 +1381,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false - - /@babel/plugin-transform-function-name@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-function-name': 7.22.5 - '@babel/helper-plugin-utils': 7.22.5 - dev: true /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==} @@ -1978,7 +1392,6 @@ packages: '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-function-name': 7.23.0 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.23.7): resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==} @@ -1991,16 +1404,6 @@ packages: '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.7) dev: false - /@babel/plugin-transform-literals@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-literals@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==} engines: {node: '>=6.9.0'} @@ -2009,7 +1412,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.23.7): resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==} @@ -2022,16 +1424,6 @@ packages: '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.7) dev: false - /@babel/plugin-transform-member-expression-literals@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==} engines: {node: '>=6.9.0'} @@ -2040,7 +1432,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==} @@ -2053,18 +1444,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: false - /@babel/plugin-transform-modules-commonjs@7.23.0(@babel/core@7.22.10): - resolution: {integrity: sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.22.10) - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-simple-access': 7.22.5 - dev: true - /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==} engines: {node: '>=6.9.0'} @@ -2075,7 +1454,6 @@ packages: '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.7) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-simple-access': 7.22.5 - dev: false /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==} @@ -2158,17 +1536,6 @@ packages: '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.7) dev: false - /@babel/plugin-transform-object-super@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.22.10) - dev: true - /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==} engines: {node: '>=6.9.0'} @@ -2178,7 +1545,6 @@ packages: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.7) - dev: false /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.23.7): resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==} @@ -2203,16 +1569,6 @@ packages: '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.7) dev: false - /@babel/plugin-transform-parameters@7.22.15(@babel/core@7.22.10): - resolution: {integrity: sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==} engines: {node: '>=6.9.0'} @@ -2221,7 +1577,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==} @@ -2247,16 +1602,6 @@ packages: '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.7) dev: false - /@babel/plugin-transform-property-literals@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==} engines: {node: '>=6.9.0'} @@ -2265,7 +1610,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-transform-react-constant-elements@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-zP0QKq/p6O42OL94udMgSfKXyse4RyJ0JqbQ34zDAONWjyrEsghYEyTSK5FIpmXmCpB55SHokL1cRRKHv8L2Qw==} @@ -2277,16 +1621,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: false - /@babel/plugin-transform-react-display-name@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-react-display-name@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==} engines: {node: '>=6.9.0'} @@ -2295,7 +1629,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-transform-react-jsx-development@7.22.5(@babel/core@7.23.7): resolution: {integrity: sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==} @@ -2307,20 +1640,6 @@ packages: '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.7) dev: false - /@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.22.10): - resolution: {integrity: sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10) - '@babel/types': 7.23.0 - dev: true - /@babel/plugin-transform-react-jsx@7.22.15(@babel/core@7.23.7): resolution: {integrity: sha512-oKckg2eZFa8771O/5vi7XeTvmM6+O9cxZu+kanTU7tD4sin5nO/G8jGJhq8Hvt2Z0kUoEDRayuZLaUlYl8QuGA==} engines: {node: '>=6.9.0'} @@ -2331,9 +1650,8 @@ packages: '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-module-imports': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.7) - '@babel/types': 7.23.0 - dev: false + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.7) + '@babel/types': 7.23.6 /@babel/plugin-transform-react-pure-annotations@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==} @@ -2384,16 +1702,6 @@ packages: - supports-color dev: false - /@babel/plugin-transform-shorthand-properties@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==} engines: {node: '>=6.9.0'} @@ -2402,18 +1710,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false - - /@babel/plugin-transform-spread@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - dev: true /@babel/plugin-transform-spread@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==} @@ -2424,7 +1720,6 @@ packages: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - dev: false /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==} @@ -2436,16 +1731,6 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: false - /@babel/plugin-transform-template-literals@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.10 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==} engines: {node: '>=6.9.0'} @@ -2454,7 +1739,6 @@ packages: dependencies: '@babel/core': 7.23.7 '@babel/helper-plugin-utils': 7.22.5 - dev: false /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.23.7): resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==} @@ -2665,13 +1949,6 @@ packages: regenerator-runtime: 0.14.1 dev: false - /@babel/runtime@7.22.10: - resolution: {integrity: sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.0 - dev: false - /@babel/runtime@7.23.8: resolution: {integrity: sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==} engines: {node: '>=6.9.0'} @@ -2683,33 +1960,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.23.5 - '@babel/parser': 7.23.5 - '@babel/types': 7.23.5 - - /@babel/template@7.22.5: - resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.22.10 - '@babel/parser': 7.22.10 - '@babel/types': 7.22.10 - - /@babel/traverse@7.22.10: - resolution: {integrity: sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.5 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.22.5 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.22.10 - '@babel/types': 7.23.0 - debug: 4.3.4(supports-color@8.1.1) - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + '@babel/parser': 7.23.6 + '@babel/types': 7.23.6 /@babel/traverse@7.23.7: resolution: {integrity: sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==} @@ -2722,35 +1974,11 @@ packages: '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.6 - '@babel/types': 7.23.6 - debug: 4.3.4(supports-color@8.1.1) - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - /@babel/types@7.22.10: - resolution: {integrity: sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.5 - to-fast-properties: 2.0.0 - - /@babel/types@7.23.0: - resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.20 - to-fast-properties: 2.0.0 - - /@babel/types@7.23.5: - resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.23.4 - '@babel/helper-validator-identifier': 7.22.20 - to-fast-properties: 2.0.0 + '@babel/types': 7.23.6 + debug: 4.3.4(supports-color@8.1.1) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color /@babel/types@7.23.6: resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} @@ -2805,13 +2033,6 @@ packages: case: 1.6.3 dev: false - /@cloudquery/plugin-pb-javascript@0.0.13: - resolution: {integrity: sha512-H/bJ/xZ47xjTZBAL/RUU/etO4/bd4PN/6oziT51myms40ncMZw4wJtQ6LQDFznV+BqQbbDvPsBmyAlkhH+vN8g==} - engines: {node: '>=16.17.0'} - dependencies: - google-protobuf: 3.21.2 - dev: false - /@cloudquery/plugin-pb-javascript@0.0.15: resolution: {integrity: sha512-qYLRxRGr2Hgg3+YQ73a0Zogk0Hm4Z0WVSmfmpkU7n18Fs7HbmL3Rr0O+EfzfpmeU6LuCnE/48RglS/YSAd6vVQ==} engines: {node: '>=16.17.0'} @@ -2819,33 +2040,6 @@ packages: google-protobuf: 3.21.2 dev: false - /@cloudquery/plugin-sdk-javascript@0.1.4: - resolution: {integrity: sha512-aHxowNwaJwfV/HDfh4nKB9/UuhT3EKyhTlabQ0s5ssZllfNgIk3eXbufhbxMe1Y6wSZwaTDaAKURQJdsGSJ/Ww==} - engines: {node: '>=20.0.0'} - dependencies: - '@apache-arrow/esnext-esm': 12.0.1 - '@cloudquery/plugin-pb-javascript': 0.0.13 - '@grpc/grpc-js': 1.9.14 - '@types/luxon': 3.3.1 - ajv: 8.12.0 - boolean: 3.2.0 - dot-prop: 8.0.2 - execa: 8.0.1 - luxon: 3.4.0 - matcher: 5.0.0 - modern-errors: 7.0.0 - modern-errors-bugs: 5.0.0(modern-errors@7.0.0) - p-map: 7.0.1 - p-timeout: 6.1.2 - path-exists: 5.0.0 - path-type: 5.0.0 - semver: 7.5.4 - uuid: 9.0.0 - winston: 3.11.0 - winston-error-format: 3.0.0(winston@3.11.0) - yargs: 17.7.2 - dev: false - /@cloudquery/plugin-sdk-javascript@0.1.6: resolution: {integrity: sha512-8+wdpLtgeEkmseGp2f/NTphC/eiOcsYTpV/AJs+zuhSZ2LiMZnSm8OhoN+09fsG6sRnodBK13MBX5cZEW1X4mA==} engines: {node: '>=20.0.0'} @@ -2876,22 +2070,15 @@ packages: /@colors/colors@1.5.0: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} + requiresBuild: true dev: false + optional: true /@colors/colors@1.6.0: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} dev: false - /@cowprotocol/ts-dune-client@0.0.2: - resolution: {integrity: sha512-OtqzwjmTQpvhDiWpgROleTff4XASMoa4GElEvFYgzlg/W90GV1vS187XxZbCKRg2vmBI8SLZAB/8hmH93hWV4Q==} - dependencies: - cross-fetch: 3.1.8 - loglevel: 1.8.1 - transitivePeerDependencies: - - encoding - dev: false - /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -2906,10 +2093,6 @@ packages: kuler: 2.0.0 dev: false - /@datastructures-js/queue@4.2.3: - resolution: {integrity: sha512-GWVMorC/xi2V2ta+Z/CPgPGHL2ZJozcj48g7y2nIX5GIGZGRrbShSHgvMViJwHJurUzJYOdIdRZnWDRrROFwJA==} - dev: false - /@discoveryjs/json-ext@0.5.7: resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} @@ -3715,11 +2898,11 @@ packages: /@emotion/babel-plugin@11.11.0: resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==} dependencies: - '@babel/helper-module-imports': 7.22.5 + '@babel/helper-module-imports': 7.22.15 '@babel/runtime': 7.23.8 '@emotion/hash': 0.9.1 '@emotion/memoize': 0.8.1 - '@emotion/serialize': 1.1.2 + '@emotion/serialize': 1.1.3 babel-plugin-macros: 3.1.0 convert-source-map: 1.9.0 escape-string-regexp: 4.0.0 @@ -3773,16 +2956,6 @@ packages: react: 18.2.0 dev: false - /@emotion/serialize@1.1.2: - resolution: {integrity: sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==} - dependencies: - '@emotion/hash': 0.9.1 - '@emotion/memoize': 0.8.1 - '@emotion/unitless': 0.8.1 - '@emotion/utils': 1.2.1 - csstype: 3.1.3 - dev: false - /@emotion/serialize@1.1.3: resolution: {integrity: sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==} dependencies: @@ -3807,11 +2980,11 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.22.10 + '@babel/runtime': 7.23.8 '@emotion/babel-plugin': 11.11.0 '@emotion/is-prop-valid': 1.2.1 '@emotion/react': 11.11.3(@types/react@18.2.48)(react@18.2.0) - '@emotion/serialize': 1.1.2 + '@emotion/serialize': 1.1.3 '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) '@emotion/utils': 1.2.1 '@types/react': 18.2.48 @@ -3871,10 +3044,10 @@ packages: resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - /@ethereum-attestation-service/eas-contracts@1.3.7(ts-node@10.9.1)(typescript@5.2.2): + /@ethereum-attestation-service/eas-contracts@1.3.7(ts-node@10.9.1)(typescript@5.3.3): resolution: {integrity: sha512-msCKGDhqSjfkx0lbs0PPkf/Z6bWxaYT9ze/uLoRh1tM7W8N27mobX4g6AsjATqGC+eWHRfliK9eCwJQj7TU8WA==} dependencies: - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.3.3) transitivePeerDependencies: - bufferutil - supports-color @@ -3883,10 +3056,10 @@ packages: - utf-8-validate dev: false - /@ethereum-attestation-service/eas-sdk@1.4.0(ts-node@10.9.1)(typescript@5.2.2): + /@ethereum-attestation-service/eas-sdk@1.4.0(ts-node@10.9.1)(typescript@5.3.3): resolution: {integrity: sha512-BmWBCcl3LD0fsoVxOzhuDwUkotvA/gx8DvYzgvtHSD5orItdydyfhpBkVpkCA4IH5B5YiqrCangSmnseBoMKuw==} dependencies: - '@ethereum-attestation-service/eas-contracts': 1.3.7(ts-node@10.9.1)(typescript@5.2.2) + '@ethereum-attestation-service/eas-contracts': 1.3.7(ts-node@10.9.1)(typescript@5.3.3) ethers: 6.10.0 js-base64: 3.7.5 lodash: 4.17.21 @@ -4281,93 +3454,6 @@ packages: resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} dev: false - /@google-cloud/bigquery@7.3.0: - resolution: {integrity: sha512-a/qQBOMlSRIVm262Kp3+Zg2+Et2fl+AwQv8mpVtvrpyk+wYQaY3dTK3U+RE6aWCiq/R1xo1vIXIYCRAQAoBsfw==} - engines: {node: '>=14.0.0'} - dependencies: - '@google-cloud/common': 5.0.0 - '@google-cloud/paginator': 5.0.0 - '@google-cloud/precise-date': 4.0.0 - '@google-cloud/promisify': 4.0.0 - arrify: 2.0.1 - big.js: 6.2.1 - duplexify: 4.1.2 - extend: 3.0.2 - is: 3.3.0 - stream-events: 1.0.5 - uuid: 9.0.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /@google-cloud/common@5.0.0: - resolution: {integrity: sha512-IsbTVr7Ag+04GMT87X738vDs85QU1rMvaesm2wEQrtTbZAR92tGmUQ8/D/kdnYgAi98Q4zmfhF+T8Xs/Lw4zAA==} - engines: {node: '>=14.0.0'} - dependencies: - '@google-cloud/projectify': 4.0.0 - '@google-cloud/promisify': 4.0.0 - arrify: 2.0.1 - duplexify: 4.1.2 - ent: 2.2.0 - extend: 3.0.2 - google-auth-library: 9.1.0 - retry-request: 6.0.0 - teeny-request: 9.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /@google-cloud/paginator@5.0.0: - resolution: {integrity: sha512-87aeg6QQcEPxGCOthnpUjvw4xAZ57G7pL8FS0C4e/81fr3FjkpUpibf1s2v5XGyGhUVGF4Jfg7yEcxqn2iUw1w==} - engines: {node: '>=14.0.0'} - dependencies: - arrify: 2.0.1 - extend: 3.0.2 - dev: false - - /@google-cloud/precise-date@4.0.0: - resolution: {integrity: sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA==} - engines: {node: '>=14.0.0'} - dev: false - - /@google-cloud/projectify@4.0.0: - resolution: {integrity: sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==} - engines: {node: '>=14.0.0'} - dev: false - - /@google-cloud/promisify@4.0.0: - resolution: {integrity: sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==} - engines: {node: '>=14'} - dev: false - - /@google-cloud/storage@7.6.0: - resolution: {integrity: sha512-/pQl5pXePhVRvgaR+huKIlSrjEjmxv5pdMieW09Sbo7pa1I9ofQ54y7QN/z9xZ1Yj2j9znfMPAmae1F+Tt4q4g==} - engines: {node: '>=14'} - dependencies: - '@google-cloud/paginator': 5.0.0 - '@google-cloud/projectify': 4.0.0 - '@google-cloud/promisify': 4.0.0 - abort-controller: 3.0.0 - async-retry: 1.3.3 - compressible: 2.0.18 - duplexify: 4.1.2 - ent: 2.2.0 - fast-xml-parser: 4.3.2 - gaxios: 6.1.1 - google-auth-library: 9.1.0 - mime: 3.0.0 - mime-types: 2.1.35 - p-limit: 3.1.0 - retry-request: 7.0.1 - teeny-request: 9.0.0 - uuid: 8.3.2 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - /@graphql-codegen/add@5.0.0(graphql@16.8.1): resolution: {integrity: sha512-ynWDOsK2yxtFHwcJTB9shoSkUd7YXd6ZE57f0nk7W5cu/nAgxZZpEsnTPEpZB/Mjf14YRGe2uJHQ7AfElHjqUQ==} peerDependencies: @@ -4378,7 +3464,7 @@ packages: tslib: 2.5.3 dev: true - /@graphql-codegen/cli@5.0.0(@types/node@20.11.6)(graphql@16.8.1)(typescript@5.3.3): + /@graphql-codegen/cli@5.0.0(@types/node@20.11.17)(graphql@16.8.1)(typescript@5.3.3): resolution: {integrity: sha512-A7J7+be/a6e+/ul2KI5sfJlpoqeqwX8EzktaKCeduyVKgOLA6W5t+NUGf6QumBDXU8PEOqXk3o3F+RAwCWOiqA==} hasBin: true peerDependencies: @@ -4388,20 +3474,20 @@ packages: '@parcel/watcher': optional: true dependencies: - '@babel/generator': 7.22.10 - '@babel/template': 7.22.5 - '@babel/types': 7.22.10 + '@babel/generator': 7.23.6 + '@babel/template': 7.22.15 + '@babel/types': 7.23.6 '@graphql-codegen/core': 4.0.0(graphql@16.8.1) '@graphql-codegen/plugin-helpers': 5.0.1(graphql@16.8.1) '@graphql-tools/apollo-engine-loader': 8.0.0(graphql@16.8.1) '@graphql-tools/code-file-loader': 8.0.2(graphql@16.8.1) '@graphql-tools/git-loader': 8.0.2(graphql@16.8.1) - '@graphql-tools/github-loader': 8.0.0(@types/node@20.11.6)(graphql@16.8.1) + '@graphql-tools/github-loader': 8.0.0(@types/node@20.11.17)(graphql@16.8.1) '@graphql-tools/graphql-file-loader': 8.0.0(graphql@16.8.1) '@graphql-tools/json-file-loader': 8.0.0(graphql@16.8.1) '@graphql-tools/load': 8.0.0(graphql@16.8.1) - '@graphql-tools/prisma-loader': 8.0.1(@types/node@20.11.6)(graphql@16.8.1) - '@graphql-tools/url-loader': 8.0.0(@types/node@20.11.6)(graphql@16.8.1) + '@graphql-tools/prisma-loader': 8.0.1(@types/node@20.11.17)(graphql@16.8.1) + '@graphql-tools/url-loader': 8.0.0(@types/node@20.11.17)(graphql@16.8.1) '@graphql-tools/utils': 10.0.7(graphql@16.8.1) '@whatwg-node/fetch': 0.8.8 chalk: 4.1.2 @@ -4409,10 +3495,10 @@ packages: debounce: 1.2.1 detect-indent: 6.1.0 graphql: 16.8.1 - graphql-config: 5.0.3(@types/node@20.11.6)(graphql@16.8.1)(typescript@5.3.3) + graphql-config: 5.0.3(@types/node@20.11.17)(graphql@16.8.1)(typescript@5.3.3) inquirer: 8.2.6 is-glob: 4.0.3 - jiti: 1.19.3 + jiti: 1.21.0 json-to-pretty-yaml: 1.2.2 listr2: 4.0.5 log-symbols: 4.1.0 @@ -4440,7 +3526,7 @@ packages: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: '@babel/helper-plugin-utils': 7.22.5 - '@babel/template': 7.22.5 + '@babel/template': 7.22.15 '@graphql-codegen/add': 5.0.0(graphql@16.8.1) '@graphql-codegen/gql-tag-operations': 4.0.1(graphql@16.8.1) '@graphql-codegen/plugin-helpers': 5.0.1(graphql@16.8.1) @@ -4657,7 +3743,7 @@ packages: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: '@graphql-tools/utils': 10.0.7(graphql@16.8.1) - '@types/ws': 8.5.7 + '@types/ws': 8.5.10 graphql: 16.8.1 graphql-ws: 5.14.1(graphql@16.8.1) isomorphic-ws: 5.0.0(ws@8.16.0) @@ -4668,7 +3754,7 @@ packages: - utf-8-validate dev: true - /@graphql-tools/executor-http@1.0.2(@types/node@20.11.6)(graphql@16.8.1): + /@graphql-tools/executor-http@1.0.2(@types/node@20.11.17)(graphql@16.8.1): resolution: {integrity: sha512-JKTB4E3kdQM2/1NEcyrVPyQ8057ZVthCV5dFJiKktqY9IdmF00M8gupFcW3jlbM/Udn78ickeUBsUzA3EouqpA==} engines: {node: '>=16.0.0'} peerDependencies: @@ -4679,7 +3765,7 @@ packages: '@whatwg-node/fetch': 0.9.13 extract-files: 11.0.0 graphql: 16.8.1 - meros: 1.3.0(@types/node@20.11.6) + meros: 1.3.0(@types/node@20.11.17) tslib: 2.6.2 value-or-promise: 1.0.12 transitivePeerDependencies: @@ -4693,7 +3779,7 @@ packages: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: '@graphql-tools/utils': 10.0.7(graphql@16.8.1) - '@types/ws': 8.5.7 + '@types/ws': 8.5.10 graphql: 16.8.1 isomorphic-ws: 5.0.0(ws@8.14.1) tslib: 2.6.2 @@ -4734,14 +3820,14 @@ packages: - supports-color dev: true - /@graphql-tools/github-loader@8.0.0(@types/node@20.11.6)(graphql@16.8.1): + /@graphql-tools/github-loader@8.0.0(@types/node@20.11.17)(graphql@16.8.1): resolution: {integrity: sha512-VuroArWKcG4yaOWzV0r19ElVIV6iH6UKDQn1MXemND0xu5TzrFme0kf3U9o0YwNo0kUYEk9CyFM0BYg4he17FA==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: '@ardatan/sync-fetch': 0.0.1 - '@graphql-tools/executor-http': 1.0.2(@types/node@20.11.6)(graphql@16.8.1) + '@graphql-tools/executor-http': 1.0.2(@types/node@20.11.17)(graphql@16.8.1) '@graphql-tools/graphql-tag-pluck': 8.0.2(graphql@16.8.1) '@graphql-tools/utils': 10.0.7(graphql@16.8.1) '@whatwg-node/fetch': 0.9.13 @@ -4774,11 +3860,11 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@babel/core': 7.22.10 - '@babel/parser': 7.22.10 - '@babel/plugin-syntax-import-assertions': 7.22.5(@babel/core@7.22.10) - '@babel/traverse': 7.22.10 - '@babel/types': 7.22.10 + '@babel/core': 7.23.7 + '@babel/parser': 7.23.6 + '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.23.7) + '@babel/traverse': 7.23.7 + '@babel/types': 7.23.6 '@graphql-tools/utils': 10.0.7(graphql@16.8.1) graphql: 16.8.1 tslib: 2.6.2 @@ -4845,13 +3931,13 @@ packages: tslib: 2.6.2 dev: true - /@graphql-tools/prisma-loader@8.0.1(@types/node@20.11.6)(graphql@16.8.1): + /@graphql-tools/prisma-loader@8.0.1(@types/node@20.11.17)(graphql@16.8.1): resolution: {integrity: sha512-bl6e5sAYe35Z6fEbgKXNrqRhXlCJYeWKBkarohgYA338/SD9eEhXtg3Cedj7fut3WyRLoQFpHzfiwxKs7XrgXg==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/url-loader': 8.0.0(@types/node@20.11.6)(graphql@16.8.1) + '@graphql-tools/url-loader': 8.0.0(@types/node@20.11.17)(graphql@16.8.1) '@graphql-tools/utils': 10.0.7(graphql@16.8.1) '@types/js-yaml': 4.0.7 '@types/json-stable-stringify': 1.0.34 @@ -4906,7 +3992,7 @@ packages: value-or-promise: 1.0.12 dev: true - /@graphql-tools/url-loader@8.0.0(@types/node@20.11.6)(graphql@16.8.1): + /@graphql-tools/url-loader@8.0.0(@types/node@20.11.17)(graphql@16.8.1): resolution: {integrity: sha512-rPc9oDzMnycvz+X+wrN3PLrhMBQkG4+sd8EzaFN6dypcssiefgWKToXtRKI8HHK68n2xEq1PyrOpkjHFJB+GwA==} engines: {node: '>=16.0.0'} peerDependencies: @@ -4915,11 +4001,11 @@ packages: '@ardatan/sync-fetch': 0.0.1 '@graphql-tools/delegate': 10.0.3(graphql@16.8.1) '@graphql-tools/executor-graphql-ws': 1.1.0(graphql@16.8.1) - '@graphql-tools/executor-http': 1.0.2(@types/node@20.11.6)(graphql@16.8.1) + '@graphql-tools/executor-http': 1.0.2(@types/node@20.11.17)(graphql@16.8.1) '@graphql-tools/executor-legacy-ws': 1.0.3(graphql@16.8.1) '@graphql-tools/utils': 10.0.7(graphql@16.8.1) '@graphql-tools/wrap': 10.0.1(graphql@16.8.1) - '@types/ws': 8.5.7 + '@types/ws': 8.5.10 '@whatwg-node/fetch': 0.9.13 graphql: 16.8.1 isomorphic-ws: 5.0.0(ws@8.16.0) @@ -4959,14 +4045,6 @@ packages: value-or-promise: 1.0.12 dev: true - /@graphql-typed-document-node/core@3.2.0(graphql@16.7.1): - resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} - peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - dependencies: - graphql: 16.7.1 - dev: false - /@graphql-typed-document-node/core@3.2.0(graphql@16.8.1): resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} peerDependencies: @@ -4979,7 +4057,7 @@ packages: engines: {node: ^8.13.0 || >=10.10.0} dependencies: '@grpc/proto-loader': 0.7.10 - '@types/node': 20.11.6 + '@types/node': 20.11.17 dev: false /@grpc/proto-loader@0.7.10: @@ -5060,21 +4138,11 @@ packages: get-package-type: 0.1.0 js-yaml: 3.14.1 resolve-from: 5.0.0 + dev: true /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - - /@jest/console@29.6.2: - resolution: {integrity: sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 20.11.17 - chalk: 4.1.2 - jest-message-util: 29.6.2 - jest-util: 29.7.0 - slash: 3.0.0 dev: true /@jest/console@29.7.0: @@ -5089,49 +4157,6 @@ packages: slash: 3.0.0 dev: true - /@jest/core@29.6.2(ts-node@10.9.1): - resolution: {integrity: sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg==} - 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 - dependencies: - '@jest/console': 29.6.2 - '@jest/reporters': 29.6.2 - '@jest/test-result': 29.6.2 - '@jest/transform': 29.6.2 - '@jest/types': 29.6.1 - '@types/node': 20.11.17 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.5.0 - jest-config: 29.6.2(@types/node@20.11.17)(ts-node@10.9.1) - jest-haste-map: 29.6.2 - jest-message-util: 29.6.2 - jest-regex-util: 29.4.3 - jest-resolve: 29.6.2 - jest-resolve-dependencies: 29.6.2 - jest-runner: 29.6.2 - jest-runtime: 29.6.2 - jest-snapshot: 29.6.2 - jest-util: 29.6.2 - jest-validate: 29.6.2 - jest-watcher: 29.6.2 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /@jest/core@29.7.0: resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5183,12 +4208,6 @@ packages: '@jest/types': 29.6.3 '@types/node': 20.11.17 jest-mock: 29.7.0 - - /@jest/expect-utils@29.6.2: - resolution: {integrity: sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.4.3 dev: true /@jest/expect-utils@29.7.0: @@ -5196,15 +4215,6 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: jest-get-type: 29.6.3 - - /@jest/expect@29.6.2: - resolution: {integrity: sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color dev: true /@jest/expect@29.7.0: @@ -5215,6 +4225,7 @@ packages: jest-snapshot: 29.7.0 transitivePeerDependencies: - supports-color + dev: true /@jest/fake-timers@29.7.0: resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} @@ -5226,17 +4237,6 @@ packages: jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 - - /@jest/globals@29.6.2: - resolution: {integrity: sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.6.2 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color dev: true /@jest/globals@29.7.0: @@ -5249,42 +4249,6 @@ packages: jest-mock: 29.7.0 transitivePeerDependencies: - supports-color - - /@jest/reporters@29.6.2: - resolution: {integrity: sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw==} - 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 - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.6.2 - '@jest/test-result': 29.6.2 - '@jest/transform': 29.6.2 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.21 - '@types/node': 20.11.17 - chalk: 4.1.2 - collect-v8-coverage: 1.0.2 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 5.2.1 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.6 - jest-message-util: 29.6.2 - jest-util: 29.7.0 - jest-worker: 29.6.2 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.1.0 - transitivePeerDependencies: - - supports-color dev: true /@jest/reporters@29.7.0: @@ -5324,28 +4288,12 @@ packages: - supports-color dev: true - /@jest/schemas@29.6.0: - resolution: {integrity: sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.27.8 - dev: true - /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@sinclair/typebox': 0.27.8 - /@jest/source-map@29.6.0: - resolution: {integrity: sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jridgewell/trace-mapping': 0.3.21 - callsites: 3.1.0 - graceful-fs: 4.2.11 - dev: true - /@jest/source-map@29.6.3: resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5355,16 +4303,6 @@ packages: graceful-fs: 4.2.11 dev: true - /@jest/test-result@29.6.2: - resolution: {integrity: sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.6.2 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.2 - dev: true - /@jest/test-result@29.7.0: resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5375,16 +4313,6 @@ packages: collect-v8-coverage: 1.0.2 dev: true - /@jest/test-sequencer@29.6.2: - resolution: {integrity: sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.6.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - slash: 3.0.0 - dev: true - /@jest/test-sequencer@29.7.0: resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5395,29 +4323,6 @@ packages: slash: 3.0.0 dev: true - /@jest/transform@29.6.2: - resolution: {integrity: sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.22.10 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.21 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.6.2 - jest-regex-util: 29.4.3 - jest-util: 29.7.0 - micromatch: 4.0.5 - pirates: 4.0.6 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/transform@29.7.0: resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5432,24 +4337,13 @@ packages: graceful-fs: 4.2.11 jest-haste-map: 29.7.0 jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.5 - pirates: 4.0.6 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - - /@jest/types@29.6.1: - resolution: {integrity: sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.0 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 20.11.17 - '@types/yargs': 17.0.24 - chalk: 4.1.2 + jest-util: 29.7.0 + micromatch: 4.0.5 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color dev: true /@jest/types@29.6.3: @@ -5488,12 +4382,6 @@ packages: /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - /@jridgewell/trace-mapping@0.3.19: - resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} - dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 - /@jridgewell/trace-mapping@0.3.21: resolution: {integrity: sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==} dependencies: @@ -5525,13 +4413,6 @@ packages: resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} dev: false - /@ljharb/through@2.3.11: - resolution: {integrity: sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - dev: false - /@mdx-js/mdx@3.0.0: resolution: {integrity: sha512-Icm0TBKBLYqroYbNW3BPnzMGn+7mwpQOK310aZ7+fkCtiU3aqv2cdcX+nd0Ydo3wI5Rx8bX2Z2QmGb/XcAClCw==} dependencies: @@ -5583,29 +4464,6 @@ packages: tweetnacl-util: 0.15.1 dev: false - /@mui/base@5.0.0-beta.32(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-4VptvYeLUYMJhZapWBkD50GmKfOc0XT381KJcTK3ncZYIl8MdBhpR6l8jOyeP5cixUPBJhstjrnmQEAHjCLriw==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.8 - '@floating-ui/react-dom': 2.0.6(react-dom@18.2.0)(react@18.2.0) - '@mui/types': 7.2.13(@types/react@18.2.48) - '@mui/utils': 5.15.5(@types/react@18.2.48)(react@18.2.0) - '@popperjs/core': 2.11.8 - '@types/react': 18.2.48 - clsx: 2.1.0 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - /@mui/base@5.0.0-beta.33(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-WcSpoJUw/UYHXpvgtl4HyMar2Ar97illUpqiS/X1gtSBp6sdDW6kB2BJ9OlVQ+Kk/RL2GDp/WHA9sbjAYV35ow==} engines: {node: '>=12.0.0'} @@ -5766,24 +4624,6 @@ packages: '@types/react': 18.2.48 dev: false - /@mui/utils@5.15.5(@types/react@18.2.48)(react@18.2.0): - resolution: {integrity: sha512-jEywgaMGZWPSlVFO7ZZAyXxNeLmq5XBp5At9Ne/sGohRJdesUcdxvyi8TP3odJxwQuL5L6PJV+JQ4DyIDM849A==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.8 - '@types/prop-types': 15.7.11 - '@types/react': 18.2.48 - prop-types: 15.8.1 - react: 18.2.0 - react-is: 18.2.0 - dev: false - /@mui/utils@5.15.6(@types/react@18.2.48)(react@18.2.0): resolution: {integrity: sha512-qfEhf+zfU9aQdbzo1qrSWlbPQhH1nCgeYgwhOVnj9Bn39shJQitEnXpSQpSNag8+uty5Od6PxmlNKPTnPySRKA==} engines: {node: '>=12.0.0'} @@ -5842,10 +4682,10 @@ packages: '@babel/runtime': 7.23.8 '@emotion/react': 11.11.3(@types/react@18.2.48)(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.3)(@types/react@18.2.48)(react@18.2.0) - '@mui/base': 5.0.0-beta.32(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) + '@mui/base': 5.0.0-beta.33(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) '@mui/material': 5.15.6(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) '@mui/system': 5.15.6(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@types/react@18.2.48)(react@18.2.0) - '@mui/utils': 5.15.5(@types/react@18.2.48)(react@18.2.0) + '@mui/utils': 5.15.6(@types/react@18.2.48)(react@18.2.0) '@types/react-transition-group': 4.4.10 clsx: 2.1.0 date-fns: 2.30.0 @@ -6266,21 +5106,6 @@ packages: '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.1 dev: false - /@npmcli/agent@1.1.0: - resolution: {integrity: sha512-I9g/2XFOkflxm5IDrGSjCcR2d12Jmic0di9w/WpJBbzYuSXmfgoL+WwEV7zY/ajxzQr7o4vSkEJh6piyFLYtuQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - lru-cache: 7.18.3 - socks: 2.7.1 - dev: false - - /@npmcli/fs@3.1.0: - resolution: {integrity: sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - semver: 7.5.4 - dev: false - /@octokit/app@14.0.0: resolution: {integrity: sha512-g/zDXttroZ9Se08shK0d0d/j0cgSA+h4WV7qGUevNEM0piNBkIlfb4Fm6bSwCNAZhNf72mBgERmYOoxicPkqdw==} engines: {node: '>= 18'} @@ -6797,55 +5622,6 @@ packages: resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} dev: false - /@redis/bloom@1.2.0(@redis/client@1.5.12): - resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} - peerDependencies: - '@redis/client': ^1.0.0 - dependencies: - '@redis/client': 1.5.12 - dev: false - - /@redis/client@1.5.12: - resolution: {integrity: sha512-/ZjE18HRzMd80eXIIUIPcH81UoZpwulbo8FmbElrjPqH0QC0SeIKu1BOU49bO5trM5g895kAjhvalt5h77q+4A==} - engines: {node: '>=14'} - dependencies: - cluster-key-slot: 1.1.2 - generic-pool: 3.9.0 - yallist: 4.0.0 - dev: false - - /@redis/graph@1.1.1(@redis/client@1.5.12): - resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} - peerDependencies: - '@redis/client': ^1.0.0 - dependencies: - '@redis/client': 1.5.12 - dev: false - - /@redis/json@1.0.6(@redis/client@1.5.12): - resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==} - peerDependencies: - '@redis/client': ^1.0.0 - dependencies: - '@redis/client': 1.5.12 - dev: false - - /@redis/search@1.1.6(@redis/client@1.5.12): - resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==} - peerDependencies: - '@redis/client': ^1.0.0 - dependencies: - '@redis/client': 1.5.12 - dev: false - - /@redis/time-series@1.0.5(@redis/client@1.5.12): - resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} - peerDependencies: - '@redis/client': ^1.0.0 - dependencies: - '@redis/client': 1.5.12 - dev: false - /@repeaterjs/repeater@3.0.4: resolution: {integrity: sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==} dev: true @@ -6977,11 +5753,13 @@ packages: resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} dependencies: type-detect: 4.0.8 + dev: true /@sinonjs/fake-timers@10.3.0: resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} dependencies: '@sinonjs/commons': 3.0.0 + dev: true /@slorber/remark-comment@1.0.0: resolution: {integrity: sha512-RCE24n7jsOj1M0UPvIQCHTe7fI0sFL4S2nwKVWwHyVr/wI/H8GosgsJGyhnsZoGFnD/P2hLf1mSbrrgSLN93NA==} @@ -7004,10 +5782,6 @@ packages: resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} dev: false - /@sqltools/formatter@1.2.5: - resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} - dev: false - /@stitches/core@1.2.8: resolution: {integrity: sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==} dev: false @@ -7331,7 +6105,7 @@ packages: chalk: 3.0.0 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 - jest: 29.7.0(@types/node@20.11.6) + jest: 29.7.0(@types/node@20.11.17) lodash: 4.17.21 redent: 3.0.0 dev: true @@ -7362,6 +6136,7 @@ packages: /@tootallnate/once@2.0.0: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} + dev: true /@tremor/react@3.13.3(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.4.1): resolution: {integrity: sha512-v0JTAhZr1VTj67nmrb5WF/vI5Mq3Fj7LigPYwqFZcYwrF1UXkUwv5mEt8V5GR5QVMmprmYx7A6m8baImt99IQQ==} @@ -7472,10 +6247,6 @@ packages: resolution: {integrity: sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg==} dev: false - /@types/caseless@0.12.5: - resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} - dev: false - /@types/command-line-args@5.2.0: resolution: {integrity: sha512-UuKzKpJJ/Ief6ufIaIzr3A/0XnluX7RvFgwkV89Yzvm77wCh1kFaFmqN8XEnGcN62EuHdedQjEMb8mYxFLGPyA==} dev: false @@ -7601,6 +6372,7 @@ packages: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: '@types/node': 20.11.17 + dev: true /@types/gtag.js@0.0.12: resolution: {integrity: sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==} @@ -7643,13 +6415,6 @@ packages: '@types/node': 20.11.17 dev: false - /@types/inquirer@9.0.4: - resolution: {integrity: sha512-x8UgutCLm5tsp995aeYB8dlT+sGBCtv0zE43tHvo7OljtlA2Rn4+COyLKe9+YjB20uy0G14y0C9vCD2KtNtyGA==} - dependencies: - '@types/through': 0.0.31 - rxjs: 7.8.1 - dev: false - /@types/istanbul-lib-coverage@2.0.4: resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} @@ -7666,8 +6431,8 @@ packages: /@types/jest@29.5.3: resolution: {integrity: sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==} dependencies: - expect: 29.6.2 - pretty-format: 29.6.2 + expect: 29.7.0 + pretty-format: 29.7.0 dev: true /@types/js-cookie@2.2.7: @@ -7686,10 +6451,6 @@ packages: parse5: 7.1.2 dev: true - /@types/json-schema@7.0.12: - resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} - dev: true - /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -7701,17 +6462,10 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/jsonwebtoken@9.0.2: - resolution: {integrity: sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==} - dependencies: - '@types/node': 20.11.17 - dev: false - /@types/jsonwebtoken@9.0.5: resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} dependencies: '@types/node': 20.11.17 - dev: true /@types/lodash.clonedeep@4.5.9: resolution: {integrity: sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==} @@ -7719,9 +6473,6 @@ packages: '@types/lodash': 4.14.202 dev: false - /@types/lodash@4.14.196: - resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==} - /@types/lodash@4.14.202: resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==} @@ -7731,6 +6482,7 @@ packages: /@types/luxon@3.3.1: resolution: {integrity: sha512-XOS5nBcgEeP2PpcqJHjCWhUCAzGfXIU8ILOSLpx2FhxqMW9KdxgCGXNOEKGVBfveKtIpztHzKK5vSRVLyW/NqA==} + dev: false /@types/mdast@4.0.3: resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} @@ -7755,13 +6507,6 @@ packages: /@types/ms@0.7.34: resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - /@types/node-fetch@2.6.4: - resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==} - dependencies: - '@types/node': 20.11.17 - form-data: 3.0.1 - dev: true - /@types/node-forge@1.3.10: resolution: {integrity: sha512-y6PJDYN4xYBxwd22l+OVH35N+1fCYWiuC3aiP2SlXVE6Lo7SS+rSx9r89hLxrP4pn6n1lBGhHJ12pj3F3Mpttw==} dependencies: @@ -7785,36 +6530,10 @@ packages: dependencies: undici-types: 5.26.5 - /@types/node@20.11.6: - resolution: {integrity: sha512-+EOokTnksGVgip2PbYbr3xnR7kZigh4LbybAfBAw5BpnQ+FqBYUsvCEjYd70IXKlbohQ64mzEYmMtlWUY8q//Q==} - dependencies: - undici-types: 5.26.5 - - /@types/node@20.6.3: - resolution: {integrity: sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA==} - /@types/normalize-package-data@2.4.4: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} dev: false - /@types/npm-package-arg@6.1.1: - resolution: {integrity: sha512-452/1Kp9IdM/oR10AyqAgZOxUt7eLbm+EMJ194L6oarMYdZNiFIFAOJ7IIr0OrZXTySgfHjJezh2oiyk2kc3ag==} - dev: true - - /@types/npm-registry-fetch@8.0.4: - resolution: {integrity: sha512-R9yEj6+NDmXLpKNS19cIaMyaHfV0aHjy/1qbo8K9jiHyjyaYg0CEmuOV/L0Q91DZDi3SuxlYY+2XYwh9TbB+eQ==} - dependencies: - '@types/node': 20.11.6 - '@types/node-fetch': 2.6.4 - '@types/npm-package-arg': 6.1.1 - '@types/npmlog': 4.1.4 - '@types/ssri': 7.1.1 - dev: true - - /@types/npmlog@4.1.4: - resolution: {integrity: sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ==} - dev: true - /@types/pad-left@2.1.1: resolution: {integrity: sha512-Xd22WCRBydkGSApl5Bw0PhAOHKSVjNL3E3AwzKaps96IMraPqy5BvZIsBVK6JLwdybUzjHnuWVwpDd0JjTfHXA==} dev: false @@ -7822,7 +6541,7 @@ packages: /@types/papaparse@5.3.14: resolution: {integrity: sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g==} dependencies: - '@types/node': 20.11.6 + '@types/node': 20.11.17 dev: true /@types/parse-json@4.0.0: @@ -7903,15 +6622,6 @@ packages: safe-buffer: 5.1.2 dev: false - /@types/request@2.48.12: - resolution: {integrity: sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==} - dependencies: - '@types/caseless': 0.12.5 - '@types/node': 20.11.17 - '@types/tough-cookie': 4.0.2 - form-data: 2.5.1 - dev: false - /@types/retry@0.12.0: resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} dev: false @@ -7962,14 +6672,9 @@ packages: '@types/node': 20.11.17 dev: false - /@types/ssri@7.1.1: - resolution: {integrity: sha512-DPP/jkDaqGiyU75MyMURxLWyYLwKSjnAuGe9ZCsLp9QZOpXmDfuevk769F0BS86TmRuD5krnp06qw9nSoNO+0g==} - dependencies: - '@types/node': 20.11.17 - dev: true - /@types/stack-utils@2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + dev: true /@types/testing-library__jest-dom@5.14.9: resolution: {integrity: sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==} @@ -7977,14 +6682,9 @@ packages: '@types/jest': 29.5.3 dev: true - /@types/through@0.0.31: - resolution: {integrity: sha512-LpKpmb7FGevYgXnBXYs6HWnmiFyVG07Pt1cnbgM1IhEacITTiUaBXXvOR3Y50ksaJWGSfhbEvQFivQEFGCC55w==} - dependencies: - '@types/node': 20.11.17 - dev: false - /@types/tough-cookie@4.0.2: resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==} + dev: true /@types/triple-beam@1.3.5: resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} @@ -8000,21 +6700,10 @@ packages: resolution: {integrity: sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==} dev: true - /@types/validator@13.11.1: - resolution: {integrity: sha512-d/MUkJYdOeKycmm75Arql4M5+UuXmf4cHdHKsyw1GcvnNgL6s77UkgSgJ8TE/rI5PYsnwYq5jkcWBLuN/MpQ1A==} - dev: false - /@types/ws@8.5.10: resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} dependencies: '@types/node': 20.11.17 - dev: false - - /@types/ws@8.5.7: - resolution: {integrity: sha512-6UrLjiDUvn40CMrAubXuIVtj2PEfKDffJS7ychvnPU44j+KVeXmdHHTgqcM/dxLUTHxlXHiFM8Skmb8ozGdTnQ==} - dependencies: - '@types/node': 20.11.17 - dev: true /@types/yargs-parser@21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} @@ -8153,7 +6842,7 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dependencies: '@typescript-eslint/types': 6.19.1 - eslint-visitor-keys: 3.4.2 + eslint-visitor-keys: 3.4.3 dev: true /@ungap/structured-clone@1.2.0: @@ -8342,13 +7031,6 @@ packages: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} dev: false - /abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - dependencies: - event-target-shim: 5.0.1 - dev: false - /abstract-level@1.0.3: resolution: {integrity: sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==} engines: {node: '>=12'} @@ -8437,6 +7119,7 @@ packages: debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color + dev: true /aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} @@ -8584,6 +7267,7 @@ packages: /ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + dev: true /ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} @@ -8599,11 +7283,6 @@ packages: normalize-path: 3.0.0 picomatch: 2.3.1 - /app-root-path@3.1.0: - resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} - engines: {node: '>= 6.0.0'} - dev: false - /arch@2.2.0: resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} dev: false @@ -8654,7 +7333,7 @@ packages: /array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 is-array-buffer: 3.0.2 dev: true @@ -8666,25 +7345,14 @@ packages: resolution: {integrity: sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==} dev: false - /array-includes@3.1.6: - resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 - get-intrinsic: 1.2.1 - is-string: 1.0.7 - dev: true - /array-includes@3.1.7: resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 is-string: 1.0.7 dev: true @@ -8696,39 +7364,19 @@ packages: resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 - es-shim-unscopables: 1.0.0 - get-intrinsic: 1.2.1 - dev: true - - /array.prototype.flat@1.3.1: - resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 es-shim-unscopables: 1.0.0 + get-intrinsic: 1.2.2 dev: true /array.prototype.flat@1.3.2: resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 - es-shim-unscopables: 1.0.0 - dev: true - - /array.prototype.flatmap@1.3.1: - resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 es-shim-unscopables: 1.0.0 dev: true @@ -8737,8 +7385,8 @@ packages: resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 es-shim-unscopables: 1.0.0 dev: true @@ -8746,11 +7394,11 @@ packages: /array.prototype.tosorted@1.1.1: resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 es-shim-unscopables: 1.0.0 - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 dev: true /arraybuffer.prototype.slice@1.0.1: @@ -8758,18 +7406,13 @@ packages: engines: {node: '>= 0.4'} dependencies: array-buffer-byte-length: 1.0.0 - call-bind: 1.0.2 - define-properties: 1.2.0 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + define-properties: 1.2.1 + get-intrinsic: 1.2.2 is-array-buffer: 3.0.2 is-shared-array-buffer: 1.0.2 dev: true - /arrify@2.0.1: - resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} - engines: {node: '>=8'} - dev: false - /asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} dev: true @@ -8796,22 +7439,6 @@ packages: resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==} hasBin: true - /async-mutex@0.4.0: - resolution: {integrity: sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==} - dependencies: - tslib: 2.6.2 - dev: false - - /async-retry@1.3.3: - resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} - dependencies: - retry: 0.13.1 - dev: false - - /async@3.2.4: - resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} - dev: false - /async@3.2.5: resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} dev: false @@ -8824,6 +7451,7 @@ packages: /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: true /at-least-node@1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} @@ -8835,6 +7463,22 @@ packages: engines: {node: '>=8'} dev: true + /autoprefixer@10.4.17(postcss@8.4.31): + resolution: {integrity: sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.22.2 + caniuse-lite: 1.0.30001580 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.31 + postcss-value-parser: 4.2.0 + dev: true + /autoprefixer@10.4.17(postcss@8.4.33): resolution: {integrity: sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==} engines: {node: ^10 || ^12 || >=14} @@ -8843,12 +7487,13 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.22.2 - caniuse-lite: 1.0.30001578 + caniuse-lite: 1.0.30001580 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.0 postcss: 8.4.33 postcss-value-parser: 4.2.0 + dev: false /available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} @@ -8874,24 +7519,6 @@ packages: dequal: 2.0.3 dev: true - /babel-jest@29.6.2(@babel/core@7.22.10): - resolution: {integrity: sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.22.10 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.1 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.5.0(@babel/core@7.22.10) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /babel-jest@29.7.0(@babel/core@7.23.7): resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -8940,15 +7567,6 @@ packages: test-exclude: 6.0.0 transitivePeerDependencies: - supports-color - - /babel-plugin-jest-hoist@29.5.0: - resolution: {integrity: sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.6 - '@types/babel__core': 7.20.1 - '@types/babel__traverse': 7.20.1 dev: true /babel-plugin-jest-hoist@29.6.3: @@ -9010,25 +7628,6 @@ packages: resolution: {integrity: sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==} dev: true - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.22.10): - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.22.10 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.10) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.22.10) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.22.10) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.10) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.10) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.10) - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.7): resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: @@ -9049,52 +7648,41 @@ packages: '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.7) dev: true - /babel-preset-fbjs@3.4.0(@babel/core@7.22.10): + /babel-preset-fbjs@3.4.0(@babel/core@7.23.7): resolution: {integrity: sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.10 - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.22.10) - '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.22.10) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.22.10) - '@babel/plugin-syntax-flow': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.22.10) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.10) - '@babel/plugin-transform-arrow-functions': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-block-scoped-functions': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-block-scoping': 7.23.0(@babel/core@7.22.10) - '@babel/plugin-transform-classes': 7.22.15(@babel/core@7.22.10) - '@babel/plugin-transform-computed-properties': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-destructuring': 7.23.0(@babel/core@7.22.10) - '@babel/plugin-transform-flow-strip-types': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-for-of': 7.22.15(@babel/core@7.22.10) - '@babel/plugin-transform-function-name': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-literals': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-member-expression-literals': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.22.10) - '@babel/plugin-transform-object-super': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.22.10) - '@babel/plugin-transform-property-literals': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-react-display-name': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.22.10) - '@babel/plugin-transform-shorthand-properties': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-spread': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-template-literals': 7.22.5(@babel/core@7.22.10) + '@babel/core': 7.23.7 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.23.7) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.23.7) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.7) + '@babel/plugin-syntax-flow': 7.22.5(@babel/core@7.23.7) + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.7) + '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.23.7) + '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.23.7) + '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-flow-strip-types': 7.22.5(@babel/core@7.23.7) + '@babel/plugin-transform-for-of': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-react-display-name': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-react-jsx': 7.22.15(@babel/core@7.23.7) + '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.7) babel-plugin-syntax-trailing-function-commas: 7.0.0-beta.0 dev: true - /babel-preset-jest@29.5.0(@babel/core@7.22.10): - resolution: {integrity: sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.22.10 - babel-plugin-jest-hoist: 29.5.0 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.10) - dev: true - /babel-preset-jest@29.6.3(@babel/core@7.23.7): resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -9146,19 +7734,11 @@ packages: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} dev: false - /big.js@6.2.1: - resolution: {integrity: sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==} - dev: false - /bigint-crypto-utils@3.3.0: resolution: {integrity: sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg==} engines: {node: '>=14.0.0'} dev: false - /bignumber.js@9.1.2: - resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} - dev: false - /bin-links@4.0.3: resolution: {integrity: sha512-obsRaULtJurnfox/MDwgq6Yo9kzbv1CPTk/1/s7Z/61Lezc8IKkFCOXNeVLXz0456WRzBQmSsDWlai2tIhBsfA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -9179,14 +7759,7 @@ packages: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - - /bl@5.1.0: - resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} - dependencies: - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 3.6.2 - dev: false + dev: true /blakejs@1.2.1: resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} @@ -9326,18 +7899,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001578 + caniuse-lite: 1.0.30001580 electron-to-chromium: 1.4.604 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.22.2) - /bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - dependencies: - fast-json-stable-stringify: 2.1.0 - dev: true - /bs58@4.0.1: resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} dependencies: @@ -9356,6 +7922,7 @@ packages: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: node-int64: 0.4.0 + dev: true /btoa-lite@1.0.0: resolution: {integrity: sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==} @@ -9368,11 +7935,6 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - /buffer-writer@2.0.0: - resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==} - engines: {node: '>=4'} - dev: false - /buffer-xor@1.0.3: resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} dev: false @@ -9382,6 +7944,7 @@ packages: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + dev: true /buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -9390,12 +7953,6 @@ packages: ieee754: 1.2.1 dev: false - /builtins@5.0.1: - resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} - dependencies: - semver: 7.5.4 - dev: false - /bundle-name@3.0.0: resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} engines: {node: '>=12'} @@ -9419,24 +7976,6 @@ packages: engines: {node: '>= 0.8'} dev: false - /cacache@17.1.3: - resolution: {integrity: sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - '@npmcli/fs': 3.1.0 - fs-minipass: 3.0.2 - glob: 10.3.10 - lru-cache: 7.18.3 - minipass: 5.0.0 - minipass-collect: 1.0.2 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - p-map: 4.0.0 - ssri: 10.0.4 - tar: 6.2.0 - unique-filename: 3.0.0 - dev: false - /cacheable-lookup@7.0.0: resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} engines: {node: '>=14.16'} @@ -9455,19 +7994,12 @@ packages: responselike: 3.0.0 dev: false - /call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.2.1 - /call-bind@1.0.5: resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} dependencies: function-bind: 1.1.2 get-intrinsic: 1.2.2 set-function-length: 1.2.0 - dev: true /call-me-maybe@1.0.2: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} @@ -9500,6 +8032,7 @@ packages: /camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} + dev: true /camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} @@ -9519,17 +8052,13 @@ packages: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: browserslist: 4.22.2 - caniuse-lite: 1.0.30001578 + caniuse-lite: 1.0.30001580 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: false - /caniuse-lite@1.0.30001578: - resolution: {integrity: sha512-J/jkFgsQ3NEl4w2lCoM9ZPxrD+FoBNJ7uJUpGVjIg/j0OwJosWM36EPDv+Yyi0V4twBk9pPmlFS+PLykgEvUmg==} - /caniuse-lite@1.0.30001580: resolution: {integrity: sha512-mtj5ur2FFPZcCEpXFy8ADXbDACuNFXg6mxVDqp7tqooX6l3zwm+d8EPoeOSIFRDvHs8qu7/SLFOGniULkcH2iA==} - dev: false /capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} @@ -9645,6 +8174,7 @@ packages: /chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true /cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -9687,6 +8217,7 @@ packages: /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} + dev: true /chrome-trace-event@1.0.3: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} @@ -9711,14 +8242,6 @@ packages: resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} dev: true - /class-validator@0.14.0: - resolution: {integrity: sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==} - dependencies: - '@types/validator': 13.11.1 - libphonenumber-js: 1.10.44 - validator: 13.11.0 - dev: false - /classic-level@1.3.0: resolution: {integrity: sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg==} engines: {node: '>=12'} @@ -9763,29 +8286,19 @@ packages: engines: {node: '>=8'} dependencies: restore-cursor: 3.1.0 + dev: true /cli-cursor@4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: restore-cursor: 4.0.0 - - /cli-highlight@2.1.11: - resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} - engines: {node: '>=8.0.0', npm: '>=5.0.0'} - hasBin: true - dependencies: - chalk: 4.1.2 - highlight.js: 10.7.3 - mz: 2.7.0 - parse5: 5.1.1 - parse5-htmlparser2-tree-adapter: 6.0.1 - yargs: 16.2.0 - dev: false + dev: true /cli-spinners@2.9.0: resolution: {integrity: sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==} engines: {node: '>=6'} + dev: true /cli-table3@0.6.3: resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} @@ -9817,11 +8330,6 @@ packages: engines: {node: '>= 10'} dev: true - /cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} - engines: {node: '>= 12'} - dev: false - /client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} dev: false @@ -9870,17 +8378,13 @@ packages: /clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + dev: true /clsx@2.1.0: resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==} engines: {node: '>=6'} dev: false - /cluster-key-slot@1.1.2: - resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} - engines: {node: '>=0.10.0'} - dev: false - /cmd-shim@6.0.2: resolution: {integrity: sha512-+FFYbB0YLaAkhkcrjkyNLYDiOsFSfRjwjY19LXk/psmMx1z00xlCv7hhQoTGXXIKi+YXHL/iiFo8NqMVQX9nOw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -9953,6 +8457,7 @@ packages: engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 + dev: true /comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -10239,7 +8744,7 @@ packages: sha.js: 2.4.11 dev: false - /create-jest@29.7.0(@types/node@20.11.6): + /create-jest@29.7.0(@types/node@20.11.17): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -10248,7 +8753,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.11.6) + jest-config: 29.7.0(@types/node@20.11.17) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -10491,28 +8996,6 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - /csv-generate@4.3.0: - resolution: {integrity: sha512-7KdVId/2RgwPIKfWHaHtjBq7I9mgdi8ICzsUyIhP8is6UwpwVGGSC/aPnrZ8/SkgBcCP20lXrdPuP64Irs1VBg==} - dev: false - - /csv-parse@5.5.2: - resolution: {integrity: sha512-YRVtvdtUNXZCMyK5zd5Wty1W6dNTpGKdqQd4EQ8tl/c6KW1aMBB1Kg1ppky5FONKmEqGJ/8WjLlTNLPne4ioVA==} - dev: false - - /csv-stringify@6.4.4: - resolution: {integrity: sha512-NDshLupGa7gp4UG4sSNIqwYJqgSwvds0SvENntxoVoVvTzXcrHvd5gG2MWpbRpSNvk59dlmIe1IwNvSxN4IVmg==} - dev: false - - /csv@6.3.5: - resolution: {integrity: sha512-Y+KTCAUljtq2JaGP42ZL1bymqlU5BkfnFpZhxRczGFDZox2VXhlRHnG5DRshyUrwQzmCdEiLjSqNldCfm1OVCA==} - engines: {node: '>= 0.1.90'} - dependencies: - csv-generate: 4.3.0 - csv-parse: 5.5.2 - csv-stringify: 6.4.4 - stream-transform: 3.2.10 - dev: false - /d3-array@3.2.4: resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} engines: {node: '>=12'} @@ -10598,6 +9081,7 @@ packages: /data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} + dev: true /data-urls@3.0.2: resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} @@ -10623,10 +9107,6 @@ packages: resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} dev: false - /dayjs@1.11.9: - resolution: {integrity: sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==} - dev: false - /debounce@1.2.1: resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} @@ -10707,9 +9187,9 @@ packages: resolution: {integrity: sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==} dependencies: array-buffer-byte-length: 1.0.0 - call-bind: 1.0.2 + call-bind: 1.0.5 es-get-iterator: 1.1.3 - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 is-arguments: 1.1.1 is-array-buffer: 3.0.2 is-date-object: 1.0.5 @@ -10772,6 +9252,7 @@ packages: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: clone: 1.0.4 + dev: true /defer-to-connect@2.0.1: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} @@ -10782,10 +9263,9 @@ packages: resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 gopd: 1.0.1 - has-property-descriptors: 1.0.0 - dev: true + has-property-descriptors: 1.0.1 /define-lazy-prop@2.0.0: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} @@ -10797,21 +9277,13 @@ packages: engines: {node: '>=12'} dev: true - /define-properties@1.2.0: - resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} - engines: {node: '>= 0.4'} - dependencies: - has-property-descriptors: 1.0.0 - object-keys: 1.1.1 - /define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} dependencies: define-data-property: 1.1.1 - has-property-descriptors: 1.0.0 + has-property-descriptors: 1.0.1 object-keys: 1.1.1 - dev: true /del@6.1.1: resolution: {integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==} @@ -10830,6 +9302,7 @@ packages: /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + dev: true /depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} @@ -10902,14 +9375,10 @@ packages: /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - /diff-sequences@29.4.3: - resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - /diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} @@ -11073,11 +9542,6 @@ packages: engines: {node: '>=12'} dev: true - /dotenv@16.3.1: - resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} - engines: {node: '>=12'} - dev: false - /dotenv@16.4.1: resolution: {integrity: sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==} engines: {node: '>=12'} @@ -11089,14 +9553,6 @@ packages: /duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - - /duplexify@4.1.2: - resolution: {integrity: sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==} - dependencies: - end-of-stream: 1.4.4 - inherits: 2.0.4 - readable-stream: 3.6.2 - stream-shift: 1.0.1 dev: false /eastasianwidth@0.2.0: @@ -11134,6 +9590,7 @@ packages: /emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} + dev: true /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -11163,20 +9620,6 @@ packages: engines: {node: '>= 0.8'} dev: false - /encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - requiresBuild: true - dependencies: - iconv-lite: 0.6.3 - dev: false - optional: true - - /end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - dependencies: - once: 1.4.0 - dev: false - /engine.io-client@6.5.2: resolution: {integrity: sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==} dependencies: @@ -11211,10 +9654,6 @@ packages: strip-ansi: 6.0.1 dev: false - /ent@2.2.0: - resolution: {integrity: sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==} - dev: false - /entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} dev: false @@ -11228,10 +9667,6 @@ packages: engines: {node: '>=6'} dev: false - /err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - dev: false - /error-class-utils@4.0.0: resolution: {integrity: sha512-przSMatQ20oCvuiKYOX9hHDsXMA2AhFrGRCiR7RwoixDzu7ChsWx41XshLBpGKc+qgC2FOSvOX69FNFaBYArhg==} engines: {node: '>=18.18.0'} @@ -11273,16 +9708,16 @@ packages: array-buffer-byte-length: 1.0.0 arraybuffer.prototype.slice: 1.0.1 available-typed-arrays: 1.0.5 - call-bind: 1.0.2 + call-bind: 1.0.5 es-set-tostringtag: 2.0.1 es-to-primitive: 1.2.1 function.prototype.name: 1.1.5 - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 get-symbol-description: 1.0.0 globalthis: 1.0.3 gopd: 1.0.1 has: 1.0.3 - has-property-descriptors: 1.0.0 + has-property-descriptors: 1.0.1 has-proto: 1.0.1 has-symbols: 1.0.3 internal-slot: 1.0.5 @@ -11298,7 +9733,7 @@ packages: object-keys: 1.1.1 object.assign: 4.1.4 regexp.prototype.flags: 1.5.0 - safe-array-concat: 1.0.0 + safe-array-concat: 1.1.0 safe-regex-test: 1.0.0 string.prototype.trim: 1.2.7 string.prototype.trimend: 1.0.6 @@ -11314,8 +9749,8 @@ packages: /es-get-iterator@1.1.3: resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 has-symbols: 1.0.3 is-arguments: 1.1.1 is-map: 2.0.2 @@ -11329,14 +9764,14 @@ packages: resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} dependencies: asynciterator.prototype: 1.0.0 - call-bind: 1.0.2 + call-bind: 1.0.5 define-properties: 1.2.1 es-abstract: 1.22.1 es-set-tostringtag: 2.0.1 - function-bind: 1.1.1 - get-intrinsic: 1.2.1 + function-bind: 1.1.2 + get-intrinsic: 1.2.2 globalthis: 1.0.3 - has-property-descriptors: 1.0.0 + has-property-descriptors: 1.0.1 has-proto: 1.0.1 has-symbols: 1.0.3 internal-slot: 1.0.5 @@ -11351,7 +9786,7 @@ packages: resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 has: 1.0.3 has-tostringtag: 1.0.0 dev: true @@ -11425,6 +9860,7 @@ packages: /escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} + dev: true /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} @@ -11489,7 +9925,7 @@ packages: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: debug: 3.2.7 - is-core-module: 2.13.0 + is-core-module: 2.13.1 resolve: 1.22.8 transitivePeerDependencies: - supports-color @@ -11509,7 +9945,7 @@ packages: eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-typescript@3.5.5)(eslint@8.56.0) get-tsconfig: 4.6.2 globby: 13.2.2 - is-core-module: 2.13.0 + is-core-module: 2.13.1 is-glob: 4.0.3 synckit: 0.8.5 transitivePeerDependencies: @@ -11592,8 +10028,8 @@ packages: dependencies: '@babel/runtime': 7.23.8 aria-query: 5.3.0 - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 ast-types-flow: 0.0.7 axe-core: 4.7.2 axobject-query: 3.2.1 @@ -11605,7 +10041,7 @@ packages: language-tags: 1.0.5 minimatch: 3.1.2 object.entries: 1.1.6 - object.fromentries: 2.0.6 + object.fromentries: 2.0.7 semver: 6.3.1 dev: true @@ -11624,8 +10060,8 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 dependencies: - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 array.prototype.tosorted: 1.1.1 doctrine: 2.1.0 es-iterator-helpers: 1.0.15 @@ -11634,9 +10070,9 @@ packages: jsx-ast-utils: 3.3.5 minimatch: 3.1.2 object.entries: 1.1.6 - object.fromentries: 2.0.6 + object.fromentries: 2.0.7 object.hasown: 1.1.2 - object.values: 1.1.6 + object.values: 1.1.7 prop-types: 15.8.1 resolve: 2.0.0-next.5 semver: 6.3.1 @@ -11657,11 +10093,6 @@ packages: esrecurse: 4.3.0 estraverse: 5.3.0 - /eslint-visitor-keys@3.4.2: - resolution: {integrity: sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -11926,23 +10357,6 @@ packages: es5-ext: 0.10.62 dev: true - /event-stream@3.3.4: - resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} - dependencies: - duplexer: 0.1.2 - from: 0.1.7 - map-stream: 0.1.0 - pause-stream: 0.0.11 - split: 0.3.3 - stream-combiner: 0.0.4 - through: 2.3.8 - dev: true - - /event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - dev: false - /eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} dev: false @@ -12010,18 +10424,6 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /expect@29.6.2: - resolution: {integrity: sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/expect-utils': 29.6.2 - '@types/node': 20.11.17 - jest-get-type: 29.4.3 - jest-matcher-utils: 29.6.2 - jest-message-util: 29.6.2 - jest-util: 29.6.2 - dev: true - /expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -12031,6 +10433,7 @@ packages: jest-matcher-utils: 29.7.0 jest-message-util: 29.7.0 jest-util: 29.7.0 + dev: true /express@4.18.2: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} @@ -12094,6 +10497,7 @@ packages: chardet: 0.7.0 iconv-lite: 0.4.24 tmp: 0.0.33 + dev: true /extract-files@11.0.0: resolution: {integrity: sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==} @@ -12147,13 +10551,6 @@ packages: dependencies: punycode: 1.4.1 - /fast-xml-parser@4.3.2: - resolution: {integrity: sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==} - hasBin: true - dependencies: - strnum: 1.0.5 - dev: false - /fastest-stable-stringify@2.0.2: resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==} dev: false @@ -12180,6 +10577,7 @@ packages: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} dependencies: bser: 2.1.1 + dev: true /fbjs-css-vars@1.0.2: resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} @@ -12216,6 +10614,7 @@ packages: dependencies: node-domexception: 1.0.0 web-streams-polyfill: 3.2.1 + dev: true /figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} @@ -12224,14 +10623,6 @@ packages: escape-string-regexp: 1.0.5 dev: true - /figures@5.0.0: - resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} - engines: {node: '>=14'} - dependencies: - escape-string-regexp: 5.0.0 - is-unicode-supported: 1.3.0 - dev: false - /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -12324,6 +10715,7 @@ packages: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 + dev: true /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} @@ -12423,24 +10815,6 @@ packages: engines: {node: '>= 14.17'} dev: false - /form-data@2.5.1: - resolution: {integrity: sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==} - engines: {node: '>= 0.12'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: false - - /form-data@3.0.1: - resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: true - /form-data@4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} @@ -12460,6 +10834,7 @@ packages: engines: {node: '>=12.20.0'} dependencies: fetch-blob: 3.2.0 + dev: true /formik-mui-x-date-pickers@0.0.1(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@mui/material@5.15.6)(@mui/system@5.15.6)(@mui/x-date-pickers@6.19.0)(formik@2.4.5)(react@18.2.0)(tiny-warning@1.0.3): resolution: {integrity: sha512-IxZsY6er+g0eNsucIDHcNs6DLaPDdG14IYx/lS2HSuKnTgV4vGEWpXyGMpkY/vGyh+W3N5U4TrBVu+7eRb5rLA==} @@ -12534,10 +10909,6 @@ packages: engines: {node: '>= 0.6'} dev: false - /from@0.1.7: - resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} - dev: true - /fs-extra@0.30.0: resolution: {integrity: sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==} dependencies: @@ -12581,13 +10952,7 @@ packages: engines: {node: '>= 8'} dependencies: minipass: 3.3.6 - - /fs-minipass@3.0.2: - resolution: {integrity: sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - minipass: 5.0.0 - dev: false + dev: true /fs-monkey@1.0.5: resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==} @@ -12603,9 +10968,6 @@ packages: requiresBuild: true optional: true - /function-bind@1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -12613,8 +10975,8 @@ packages: resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 functions-have-names: 1.2.3 dev: true @@ -12627,30 +10989,6 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /gaxios@6.1.1: - resolution: {integrity: sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==} - engines: {node: '>=14'} - dependencies: - extend: 3.0.2 - https-proxy-agent: 7.0.2 - is-stream: 2.0.1 - node-fetch: 2.6.12 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /gcp-metadata@6.0.0: - resolution: {integrity: sha512-Ozxyi23/1Ar51wjUT2RDklK+3HxqDr8TLBNK8rBBFQ7T85iIGnXnVusauj06QyqCXRFZig8LZC+TUddWbndlpQ==} - engines: {node: '>=14'} - dependencies: - gaxios: 6.1.1 - json-bigint: 1.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - /generate-api-key@1.0.2: resolution: {integrity: sha512-4rPSpXyboIXfugOTN3/0Qaoqpzbk0sepzPS0XyxPh3UMuu+Trk+0JMyJ6mB/7FEgp7oZ1juqsRW+8wSYeKDbfA==} engines: {node: '>=14'} @@ -12661,11 +10999,6 @@ packages: uuid: 8.3.2 dev: false - /generic-pool@3.9.0: - resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} - engines: {node: '>= 4'} - dev: false - /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -12679,14 +11012,6 @@ packages: engines: {node: '>=18'} dev: true - /get-intrinsic@1.2.1: - resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} - dependencies: - function-bind: 1.1.1 - has: 1.0.3 - has-proto: 1.0.1 - has-symbols: 1.0.3 - /get-intrinsic@1.2.2: resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} dependencies: @@ -12694,7 +11019,6 @@ packages: has-proto: 1.0.1 has-symbols: 1.0.3 hasown: 2.0.0 - dev: true /get-own-enumerable-property-symbols@3.0.2: resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} @@ -12703,6 +11027,7 @@ packages: /get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} + dev: true /get-stdin@8.0.0: resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} @@ -12721,8 +11046,8 @@ packages: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 dev: true /get-tsconfig@4.6.2: @@ -12758,21 +11083,10 @@ packages: dev: true /glob-to-regexp@0.4.1: - resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - - /glob@10.3.10: - resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - dependencies: - foreground-child: 3.1.1 - jackspeak: 2.3.6 - minimatch: 9.0.3 - minipass: 7.0.4 - path-scurry: 1.10.1 + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - /glob@10.3.3: - resolution: {integrity: sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==} + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true dependencies: @@ -12781,7 +11095,6 @@ packages: minimatch: 9.0.3 minipass: 7.0.4 path-scurry: 1.10.1 - dev: false /glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} @@ -12814,17 +11127,6 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 - /glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 5.1.6 - once: 1.4.0 - dev: false - /global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} engines: {node: '>=10'} @@ -12886,22 +11188,6 @@ packages: merge2: 1.4.1 slash: 4.0.0 - /google-auth-library@9.1.0: - resolution: {integrity: sha512-1M9HdOcQNPV5BwSXqwwT238MTKodJFBxZ/V2JP397ieOLv4FjQdfYb9SooR7Mb+oUT2IJ92mLJQf804dyx0MJA==} - engines: {node: '>=14'} - dependencies: - base64-js: 1.5.1 - ecdsa-sig-formatter: 1.0.11 - gaxios: 6.1.1 - gcp-metadata: 6.0.0 - gtoken: 7.0.1 - jws: 4.0.0 - lru-cache: 6.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - /google-protobuf@3.21.2: resolution: {integrity: sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==} dev: false @@ -12909,8 +11195,7 @@ packages: /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: - get-intrinsic: 1.2.1 - dev: true + get-intrinsic: 1.2.2 /got@12.6.1: resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} @@ -12939,7 +11224,7 @@ packages: /graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - /graphql-config@5.0.3(@types/node@20.11.6)(graphql@16.8.1)(typescript@5.3.3): + /graphql-config@5.0.3(@types/node@20.11.17)(graphql@16.8.1)(typescript@5.3.3): resolution: {integrity: sha512-BNGZaoxIBkv9yy6Y7omvsaBUHOzfFcII3UN++tpH8MGOKFPFkCPZuwx09ggANMt8FgyWP1Od8SWPmrUEZca4NQ==} engines: {node: '>= 16.0.0'} peerDependencies: @@ -12953,11 +11238,11 @@ packages: '@graphql-tools/json-file-loader': 8.0.0(graphql@16.8.1) '@graphql-tools/load': 8.0.0(graphql@16.8.1) '@graphql-tools/merge': 9.0.0(graphql@16.8.1) - '@graphql-tools/url-loader': 8.0.0(@types/node@20.11.6)(graphql@16.8.1) + '@graphql-tools/url-loader': 8.0.0(@types/node@20.11.17)(graphql@16.8.1) '@graphql-tools/utils': 10.0.7(graphql@16.8.1) cosmiconfig: 8.3.6(typescript@5.3.3) graphql: 16.8.1 - jiti: 1.19.3 + jiti: 1.21.0 minimatch: 4.2.3 string-env-interpolation: 1.0.1 tslib: 2.6.2 @@ -12969,18 +11254,6 @@ packages: - utf-8-validate dev: true - /graphql-request@6.1.0(graphql@16.7.1): - resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} - peerDependencies: - graphql: 14 - 16 - dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.7.1) - cross-fetch: 3.1.8 - graphql: 16.7.1 - transitivePeerDependencies: - - encoding - dev: false - /graphql-request@6.1.0(graphql@16.8.1): resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} peerDependencies: @@ -13010,11 +11283,6 @@ packages: graphql: 16.8.1 dev: true - /graphql@16.7.1: - resolution: {integrity: sha512-DRYR9tf+UGU0KOsMcKAlXeFfX89UiiIZ0dRU3mR0yJfu6OjZqUcp68NnFLnqQU5RexygFoDy1EW+ccOYcPfmHg==} - engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - dev: false - /graphql@16.8.1: resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} @@ -13029,17 +11297,6 @@ packages: strip-bom-string: 1.0.0 dev: false - /gtoken@7.0.1: - resolution: {integrity: sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==} - engines: {node: '>=14.0.0'} - dependencies: - gaxios: 6.1.1 - jws: 4.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - /gzip-size@6.0.0: resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} engines: {node: '>=10'} @@ -13051,7 +11308,7 @@ packages: resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} dev: false - /hardhat@2.19.1(ts-node@10.9.1)(typescript@5.2.2): + /hardhat@2.19.1(ts-node@10.9.1)(typescript@5.3.3): resolution: {integrity: sha512-bsWa63g1GB78ZyMN08WLhFElLPA+J+pShuKD1BFO2+88g3l+BL3R07vj9deIi9dMbssxgE714Gof1dBEDGqnCw==} hasBin: true peerDependencies: @@ -13107,9 +11364,9 @@ packages: solc: 0.7.3(debug@4.3.4) source-map-support: 0.5.21 stacktrace-parser: 0.1.10 - ts-node: 10.9.1(@types/node@20.11.17)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@20.11.17)(typescript@5.3.3) tsort: 0.0.1 - typescript: 5.2.2 + typescript: 5.3.3 undici: 5.28.2 uuid: 8.3.2 ws: 7.5.9 @@ -13131,16 +11388,10 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - /has-property-descriptors@1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} - dependencies: - get-intrinsic: 1.2.1 - /has-property-descriptors@1.0.1: resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} dependencies: get-intrinsic: 1.2.2 - dev: true /has-proto@1.0.1: resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} @@ -13166,7 +11417,8 @@ packages: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: - function-bind: 1.1.1 + function-bind: 1.1.2 + dev: true /hash-base@3.1.0: resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} @@ -13321,10 +11573,6 @@ packages: tslib: 2.6.2 dev: true - /highlight.js@10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - dev: false - /history@4.10.1: resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} dependencies: @@ -13358,13 +11606,6 @@ packages: react-is: 16.13.1 dev: false - /hosted-git-info@6.1.1: - resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - lru-cache: 7.18.3 - dev: false - /hosted-git-info@7.0.1: resolution: {integrity: sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==} engines: {node: ^16.14.0 || >=18.0.0} @@ -13510,6 +11751,7 @@ packages: debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color + dev: true /http-proxy-agent@7.0.0: resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==} @@ -13576,6 +11818,7 @@ packages: debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color + dev: true /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} @@ -13612,6 +11855,7 @@ packages: requiresBuild: true dependencies: safer-buffer: 2.1.2 + dev: true /icss-utils@5.1.0(postcss@8.4.33): resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} @@ -13750,27 +11994,6 @@ packages: wrap-ansi: 6.2.0 dev: true - /inquirer@9.2.11: - resolution: {integrity: sha512-B2LafrnnhbRzCWfAdOXisUzL89Kg8cVJlYmhqoi3flSiV/TveO+nsXwgKr9h9PIo+J1hz7nBSk6gegRIMBBf7g==} - engines: {node: '>=14.18.0'} - dependencies: - '@ljharb/through': 2.3.11 - ansi-escapes: 4.3.2 - chalk: 5.3.0 - cli-cursor: 3.1.0 - cli-width: 4.1.0 - external-editor: 3.1.0 - figures: 5.0.0 - lodash: 4.17.21 - mute-stream: 1.0.0 - ora: 5.4.1 - run-async: 3.0.0 - rxjs: 7.8.1 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - dev: false - /instantsearch.css@8.1.0: resolution: {integrity: sha512-rPhcAZ02bLwUn3iOXbldZW/yl+17guWoH3qWYZ8nQEwNBx5+wZ6Bv8mFqqK448+R2aU4nbFKIhmoTIPXI5Zobg==} dev: false @@ -13800,7 +12023,7 @@ packages: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 has: 1.0.3 side-channel: 1.0.4 dev: true @@ -13826,10 +12049,6 @@ packages: fp-ts: 1.19.3 dev: false - /ip@2.0.0: - resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} - dev: false - /ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -13861,15 +12080,15 @@ packages: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 has-tostringtag: 1.0.0 dev: true /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 is-typed-array: 1.1.12 dev: true @@ -13903,7 +12122,7 @@ packages: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 has-tostringtag: 1.0.0 dev: true @@ -13924,11 +12143,6 @@ packages: ci-info: 3.9.0 dev: false - /is-core-module@2.13.0: - resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} - dependencies: - has: 1.0.3 - /is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: @@ -13977,7 +12191,7 @@ packages: /is-finalizationregistry@1.0.2: resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 dev: true /is-fullwidth-code-point@3.0.0: @@ -14041,15 +12255,7 @@ packages: /is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} - - /is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} - dev: false - - /is-lambda@1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - dev: false + dev: true /is-lower-case@2.0.2: resolution: {integrity: sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==} @@ -14148,7 +12354,7 @@ packages: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 has-tostringtag: 1.0.0 dev: true @@ -14176,7 +12382,7 @@ packages: /is-shared-array-buffer@1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 dev: true /is-stream@2.0.1: @@ -14223,11 +12429,6 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - /is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - dev: false - /is-upper-case@2.0.2: resolution: {integrity: sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==} dependencies: @@ -14241,14 +12442,14 @@ packages: /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 dev: true /is-weakset@2.0.2: resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 dev: true /is-what@4.1.15: @@ -14272,10 +12473,6 @@ packages: engines: {node: '>=12'} dev: false - /is@3.3.0: - resolution: {integrity: sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==} - dev: false - /isarray@0.0.1: resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} dev: false @@ -14314,6 +12511,7 @@ packages: /istanbul-lib-coverage@3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} + dev: true /istanbul-lib-instrument@5.2.1: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} @@ -14326,6 +12524,7 @@ packages: semver: 6.3.1 transitivePeerDependencies: - supports-color + dev: true /istanbul-lib-instrument@6.0.1: resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} @@ -14372,7 +12571,7 @@ packages: resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} dependencies: define-properties: 1.2.1 - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 has-symbols: 1.0.3 reflect.getprototypeof: 1.0.4 set-function-name: 2.0.1 @@ -14386,14 +12585,6 @@ packages: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - /jest-changed-files@29.5.0: - resolution: {integrity: sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - execa: 5.1.1 - p-limit: 3.1.0 - dev: true - /jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -14403,35 +12594,6 @@ packages: p-limit: 3.1.0 dev: true - /jest-circus@29.6.2: - resolution: {integrity: sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.6.2 - '@jest/test-result': 29.6.2 - '@jest/types': 29.6.3 - '@types/node': 20.11.17 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.5.1 - is-generator-fn: 2.1.0 - jest-each: 29.6.2 - jest-matcher-utils: 29.6.2 - jest-message-util: 29.7.0 - jest-runtime: 29.6.2 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - p-limit: 3.1.0 - pretty-format: 29.7.0 - pure-rand: 6.0.2 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - /jest-circus@29.7.0: resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -14461,186 +12623,35 @@ packages: - supports-color dev: true - /jest-cli@29.6.2(@types/node@20.6.3)(ts-node@10.9.1): - resolution: {integrity: sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.6.2(ts-node@10.9.1) - '@jest/test-result': 29.6.2 - '@jest/types': 29.6.1 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - import-local: 3.1.0 - jest-config: 29.6.2(@types/node@20.6.3)(ts-node@10.9.1) - jest-util: 29.6.2 - jest-validate: 29.6.2 - prompts: 2.4.2 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /jest-cli@29.7.0(@types/node@20.11.6): + /jest-cli@29.7.0(@types/node@20.11.17): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.11.6) - exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.11.6) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /jest-config@29.6.2(@types/node@20.11.17)(ts-node@10.9.1): - resolution: {integrity: sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==} - 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 - dependencies: - '@babel/core': 7.22.10 - '@jest/test-sequencer': 29.6.2 - '@jest/types': 29.6.3 - '@types/node': 20.11.17 - babel-jest: 29.6.2(@babel/core@7.22.10) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.6.2 - jest-environment-node: 29.6.2 - jest-get-type: 29.4.3 - jest-regex-util: 29.4.3 - jest-resolve: 29.6.2 - jest-runner: 29.6.2 - jest-util: 29.7.0 - jest-validate: 29.6.2 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@20.6.3)(typescript@5.2.2) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-config@29.6.2(@types/node@20.6.3)(ts-node@10.9.1): - resolution: {integrity: sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==} - 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 - dependencies: - '@babel/core': 7.22.10 - '@jest/test-sequencer': 29.6.2 - '@jest/types': 29.6.3 - '@types/node': 20.6.3 - babel-jest: 29.6.2(@babel/core@7.22.10) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.6.2 - jest-environment-node: 29.6.2 - jest-get-type: 29.4.3 - jest-regex-util: 29.4.3 - jest-resolve: 29.6.2 - jest-runner: 29.6.2 - jest-util: 29.7.0 - jest-validate: 29.6.2 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@20.6.3)(typescript@5.2.2) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-config@29.7.0(@types/node@20.11.17): - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - 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 - dependencies: - '@babel/core': 7.23.7 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.11.17 - babel-jest: 29.7.0(@babel/core@7.23.7) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.11.17) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@20.11.17) jest-util: 29.7.0 jest-validate: 29.7.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 + yargs: 17.7.2 transitivePeerDependencies: + - '@types/node' - babel-plugin-macros - supports-color + - ts-node dev: true - /jest-config@29.7.0(@types/node@20.11.6): + /jest-config@29.7.0(@types/node@20.11.17): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -14655,7 +12666,7 @@ packages: '@babel/core': 7.23.7 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.6 + '@types/node': 20.11.17 babel-jest: 29.7.0(@babel/core@7.23.7) chalk: 4.1.2 ci-info: 3.9.0 @@ -14680,16 +12691,6 @@ packages: - supports-color dev: true - /jest-diff@29.6.2: - resolution: {integrity: sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 29.4.3 - jest-get-type: 29.4.3 - pretty-format: 29.7.0 - dev: true - /jest-diff@29.7.0: resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -14698,12 +12699,6 @@ packages: diff-sequences: 29.6.3 jest-get-type: 29.6.3 pretty-format: 29.7.0 - - /jest-docblock@29.4.3: - resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - detect-newline: 3.1.0 dev: true /jest-docblock@29.7.0: @@ -14713,17 +12708,6 @@ packages: detect-newline: 3.1.0 dev: true - /jest-each@29.6.2: - resolution: {integrity: sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 - dev: true - /jest-each@29.7.0: resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -14748,7 +12732,7 @@ packages: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 20.11.6 + '@types/node': 20.11.17 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -14758,18 +12742,6 @@ packages: - utf-8-validate dev: true - /jest-environment-node@29.6.2: - resolution: {integrity: sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.11.17 - jest-mock: 29.7.0 - jest-util: 29.7.0 - dev: true - /jest-environment-node@29.7.0: resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -14782,32 +12754,9 @@ packages: jest-util: 29.7.0 dev: true - /jest-get-type@29.4.3: - resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - /jest-get-type@29.6.3: resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - /jest-haste-map@29.6.2: - resolution: {integrity: sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.6 - '@types/node': 20.11.17 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.4.3 - jest-util: 29.7.0 - jest-worker: 29.6.2 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 dev: true /jest-haste-map@29.7.0: @@ -14827,13 +12776,6 @@ packages: walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 - - /jest-leak-detector@29.6.2: - resolution: {integrity: sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.4.3 - pretty-format: 29.7.0 dev: true /jest-leak-detector@29.7.0: @@ -14844,16 +12786,6 @@ packages: pretty-format: 29.7.0 dev: true - /jest-matcher-utils@29.6.2: - resolution: {integrity: sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 29.6.2 - jest-get-type: 29.4.3 - pretty-format: 29.7.0 - dev: true - /jest-matcher-utils@29.7.0: resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -14862,20 +12794,6 @@ packages: jest-diff: 29.7.0 jest-get-type: 29.6.3 pretty-format: 29.7.0 - - /jest-message-util@29.6.2: - resolution: {integrity: sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/code-frame': 7.23.5 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 dev: true /jest-message-util@29.7.0: @@ -14891,6 +12809,7 @@ packages: pretty-format: 29.7.0 slash: 3.0.0 stack-utils: 2.0.6 + dev: true /jest-mock@29.7.0: resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} @@ -14899,17 +12818,6 @@ packages: '@jest/types': 29.6.3 '@types/node': 20.11.17 jest-util: 29.7.0 - - /jest-pnp-resolver@1.2.3(jest-resolve@29.6.2): - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - dependencies: - jest-resolve: 29.6.2 dev: true /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -14924,23 +12832,9 @@ packages: jest-resolve: 29.7.0 dev: true - /jest-regex-util@29.4.3: - resolution: {integrity: sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - /jest-regex-util@29.6.3: resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - /jest-resolve-dependencies@29.6.2: - resolution: {integrity: sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-regex-util: 29.4.3 - jest-snapshot: 29.6.2 - transitivePeerDependencies: - - supports-color dev: true /jest-resolve-dependencies@29.7.0: @@ -14953,21 +12847,6 @@ packages: - supports-color dev: true - /jest-resolve@29.6.2: - resolution: {integrity: sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.6.2 - jest-pnp-resolver: 1.2.3(jest-resolve@29.6.2) - jest-util: 29.6.2 - jest-validate: 29.6.2 - resolve: 1.22.8 - resolve.exports: 2.0.2 - slash: 3.0.0 - dev: true - /jest-resolve@29.7.0: resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -14983,35 +12862,6 @@ packages: slash: 3.0.0 dev: true - /jest-runner@29.6.2: - resolution: {integrity: sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.6.2 - '@jest/environment': 29.7.0 - '@jest/test-result': 29.6.2 - '@jest/transform': 29.6.2 - '@jest/types': 29.6.3 - '@types/node': 20.11.17 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.4.3 - jest-environment-node: 29.6.2 - jest-haste-map: 29.6.2 - jest-leak-detector: 29.6.2 - jest-message-util: 29.6.2 - jest-resolve: 29.6.2 - jest-runtime: 29.6.2 - jest-util: 29.7.0 - jest-watcher: 29.6.2 - jest-worker: 29.6.2 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - dev: true - /jest-runner@29.7.0: resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -15041,36 +12891,6 @@ packages: - supports-color dev: true - /jest-runtime@29.6.2: - resolution: {integrity: sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.6.2 - '@jest/source-map': 29.6.0 - '@jest/test-result': 29.6.2 - '@jest/transform': 29.6.2 - '@jest/types': 29.6.3 - '@types/node': 20.11.17 - chalk: 4.1.2 - cjs-module-lexer: 1.2.3 - collect-v8-coverage: 1.0.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.6.2 - jest-message-util: 29.6.2 - jest-mock: 29.7.0 - jest-regex-util: 29.4.3 - jest-resolve: 29.6.2 - jest-snapshot: 29.6.2 - jest-util: 29.7.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /jest-runtime@29.7.0: resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -15101,47 +12921,19 @@ packages: - supports-color dev: true - /jest-snapshot@29.6.2: - resolution: {integrity: sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.22.10 - '@babel/generator': 7.22.10 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.10) - '@babel/types': 7.22.10 - '@jest/expect-utils': 29.6.2 - '@jest/transform': 29.6.2 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.10) - chalk: 4.1.2 - expect: 29.6.2 - graceful-fs: 4.2.11 - jest-diff: 29.6.2 - jest-get-type: 29.4.3 - jest-matcher-utils: 29.6.2 - jest-message-util: 29.6.2 - jest-util: 29.7.0 - natural-compare: 1.4.0 - pretty-format: 29.7.0 - semver: 7.5.4 - transitivePeerDependencies: - - supports-color - dev: true - /jest-snapshot@29.7.0: resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/core': 7.22.10 - '@babel/generator': 7.22.10 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.10) - '@babel/types': 7.22.10 + '@babel/core': 7.23.7 + '@babel/generator': 7.23.6 + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.7) + '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.7) + '@babel/types': 7.23.6 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.10) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.7) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -15155,17 +12947,6 @@ packages: semver: 7.5.4 transitivePeerDependencies: - supports-color - - /jest-util@29.6.2: - resolution: {integrity: sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.1 - '@types/node': 20.11.17 - chalk: 4.1.2 - ci-info: 3.9.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 dev: true /jest-util@29.7.0: @@ -15179,18 +12960,6 @@ packages: graceful-fs: 4.2.11 picomatch: 2.3.1 - /jest-validate@29.6.2: - resolution: {integrity: sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.4.3 - leven: 3.1.0 - pretty-format: 29.7.0 - dev: true - /jest-validate@29.7.0: resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -15203,20 +12972,6 @@ packages: pretty-format: 29.7.0 dev: true - /jest-watcher@29.6.2: - resolution: {integrity: sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.6.2 - '@jest/types': 29.6.3 - '@types/node': 20.11.17 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 - dev: true - /jest-watcher@29.7.0: resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -15239,16 +12994,6 @@ packages: merge-stream: 2.0.0 supports-color: 8.1.1 - /jest-worker@29.6.2: - resolution: {integrity: sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 20.11.17 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - dev: true - /jest-worker@29.7.0: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -15258,28 +13003,7 @@ packages: merge-stream: 2.0.0 supports-color: 8.1.1 - /jest@29.6.2(@types/node@20.6.3)(ts-node@10.9.1): - resolution: {integrity: sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.6.2(ts-node@10.9.1) - '@jest/types': 29.6.1 - import-local: 3.1.0 - jest-cli: 29.6.2(@types/node@20.6.3)(ts-node@10.9.1) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - - /jest@29.7.0(@types/node@20.11.6): + /jest@29.7.0(@types/node@20.11.17): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -15292,7 +13016,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.11.6) + jest-cli: 29.7.0(@types/node@20.11.17) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -15300,11 +13024,6 @@ packages: - ts-node dev: true - /jiti@1.19.3: - resolution: {integrity: sha512-5eEbBDQT/jF1xg6l36P+mWGGoH9Spuy0PCdSr2dtWRDGC6ph/w9ZCL4lmESW8f8F7MwT3XKescfP0wnZWAKL9w==} - hasBin: true - dev: true - /jiti@1.21.0: resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} hasBin: true @@ -15320,6 +13039,7 @@ packages: /jose@4.15.2: resolution: {integrity: sha512-IY73F228OXRl9ar3jJagh7Vnuhj/GzBunPiZP13K0lOl7Am9SoWW3kEzq3MCllJMTtZqHTiDXQvoRd4U95aU6A==} + deprecated: this version is no longer supported /js-base64@3.7.5: resolution: {integrity: sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==} @@ -15404,12 +13124,6 @@ packages: engines: {node: '>=4'} hasBin: true - /json-bigint@1.0.0: - resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} - dependencies: - bignumber.js: 9.1.2 - dev: false - /json-bignum@0.0.3: resolution: {integrity: sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg==} engines: {node: '>=0.8'} @@ -15432,7 +13146,7 @@ packages: hasBin: true dependencies: '@bcherny/json-schema-ref-parser': 10.0.5-fork - '@types/json-schema': 7.0.12 + '@types/json-schema': 7.0.15 '@types/lodash': 4.14.202 '@types/prettier': 2.7.3 cli-color: 2.0.3 @@ -15463,10 +13177,6 @@ packages: jsonify: 0.0.1 dev: true - /json-to-graphql-query@2.2.5: - resolution: {integrity: sha512-5Nom9inkIMrtY992LMBBG1Zaekrc10JaRhyZgprwHBVMDtRgllTvzl0oBbg13wJsVZoSoFNNMaeIVQs0P04vsA==} - dev: false - /json-to-pretty-yaml@1.2.2: resolution: {integrity: sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==} engines: {node: '>= 0.2.0'} @@ -15511,11 +13221,6 @@ packages: resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==} dev: true - /jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - dev: false - /jsonwebtoken@9.0.1: resolution: {integrity: sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==} engines: {node: '>=12', npm: '>=6'} @@ -15530,10 +13235,10 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} dependencies: - array-includes: 3.1.6 - array.prototype.flat: 1.3.1 + array-includes: 3.1.7 + array.prototype.flat: 1.3.2 object.assign: 4.1.4 - object.values: 1.1.6 + object.values: 1.1.7 dev: true /jwa@1.4.1: @@ -15544,14 +13249,6 @@ packages: safe-buffer: 5.2.1 dev: false - /jwa@2.0.0: - resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==} - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - dev: false - /jws@3.2.2: resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} dependencies: @@ -15559,13 +13256,6 @@ packages: safe-buffer: 5.2.1 dev: false - /jws@4.0.0: - resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} - dependencies: - jwa: 2.0.0 - safe-buffer: 5.2.1 - dev: false - /keccak@3.0.4: resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==} engines: {node: '>=10.0.0'} @@ -15656,10 +13346,6 @@ packages: prelude-ls: 1.2.1 type-check: 0.4.0 - /libphonenumber-js@1.10.44: - resolution: {integrity: sha512-svlRdNBI5WgBjRC20GrCfbFiclbF0Cx+sCcQob/C1r57nsoq0xg8r65QbTyVyweQIlB33P+Uahyho6EMYgcOyQ==} - dev: false - /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -15761,6 +13447,7 @@ packages: engines: {node: '>=8'} dependencies: p-locate: 4.1.0 + dev: true /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} @@ -15793,6 +13480,7 @@ packages: /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: false /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -15815,14 +13503,6 @@ packages: chalk: 4.1.2 is-unicode-supported: 0.1.0 - /log-symbols@5.1.0: - resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} - engines: {node: '>=12'} - dependencies: - chalk: 5.3.0 - is-unicode-supported: 1.3.0 - dev: false - /log-update@4.0.0: resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} engines: {node: '>=10'} @@ -15844,17 +13524,6 @@ packages: wrap-ansi: 9.0.0 dev: true - /logform@2.5.1: - resolution: {integrity: sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==} - dependencies: - '@colors/colors': 1.5.0 - '@types/triple-beam': 1.3.5 - fecha: 4.2.3 - ms: 2.1.3 - safe-stable-stringify: 2.4.3 - triple-beam: 1.4.1 - dev: false - /logform@2.6.0: resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} engines: {node: '>= 12.0.0'} @@ -15867,11 +13536,6 @@ packages: triple-beam: 1.4.1 dev: false - /loglevel@1.8.1: - resolution: {integrity: sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==} - engines: {node: '>= 0.6.0'} - dev: false - /long@5.2.3: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} dev: false @@ -15916,11 +13580,6 @@ packages: dependencies: yallist: 4.0.0 - /lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - dev: false - /lru-queue@0.1.0: resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} dependencies: @@ -15951,27 +13610,11 @@ packages: /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - /make-fetch-happen@12.0.0: - resolution: {integrity: sha512-xpuA2kA8Z66uGQjaSXd7rffqJOv60iYpP8X0TsZl3uwXlqxUVmHETImjM71JOPA694TlcX37GhlaCsl6z6fNVg==} - engines: {node: ^16.13.0 || >=18.0.0} - dependencies: - '@npmcli/agent': 1.1.0 - cacache: 17.1.3 - http-cache-semantics: 4.1.1 - is-lambda: 1.0.1 - minipass: 7.0.4 - minipass-fetch: 3.0.3 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - negotiator: 0.6.3 - promise-retry: 2.0.1 - ssri: 10.0.4 - dev: false - /makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} dependencies: tmpl: 1.0.5 + dev: true /map-cache@0.2.2: resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} @@ -15983,10 +13626,6 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: false - /map-stream@0.1.0: - resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} - dev: true - /markdown-extensions@2.0.0: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} @@ -16292,7 +13931,7 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - /meros@1.3.0(@types/node@20.11.6): + /meros@1.3.0(@types/node@20.11.17): resolution: {integrity: sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==} engines: {node: '>=13'} peerDependencies: @@ -16301,7 +13940,7 @@ packages: '@types/node': optional: true dependencies: - '@types/node': 20.11.6 + '@types/node': 20.11.17 dev: true /methods@1.1.2: @@ -16693,12 +14332,6 @@ packages: hasBin: true dev: false - /mime@3.0.0: - resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} - engines: {node: '>=10.0.0'} - hasBin: true - dev: false - /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -16759,13 +14392,6 @@ packages: brace-expansion: 2.0.1 dev: false - /minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - dependencies: - brace-expansion: 2.0.1 - dev: false - /minimatch@9.0.3: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} @@ -16775,66 +14401,17 @@ packages: /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - /minipass-collect@1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - dev: false - - /minipass-fetch@3.0.3: - resolution: {integrity: sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - minipass: 5.0.0 - minipass-sized: 1.0.3 - minizlib: 2.1.2 - optionalDependencies: - encoding: 0.1.13 - dev: false - - /minipass-flush@1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - dev: false - - /minipass-json-stream@1.0.1: - resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} - dependencies: - jsonparse: 1.3.1 - minipass: 3.3.6 - dev: false - - /minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.6 - dev: false - - /minipass-sized@1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.6 - dev: false - /minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} dependencies: yallist: 4.0.0 + dev: true /minipass@5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} - - /minipass@7.0.2: - resolution: {integrity: sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA==} - engines: {node: '>=16 || 14 >=14.17'} - dev: false + dev: true /minipass@7.0.4: resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} @@ -16846,6 +14423,7 @@ packages: dependencies: minipass: 3.3.6 yallist: 4.0.0 + dev: true /mkdirp@0.3.0: resolution: {integrity: sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==} @@ -16856,18 +14434,7 @@ packages: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true - - /mkdirp@2.1.6: - resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} - engines: {node: '>=10'} - hasBin: true - dev: false - - /mkdirp@3.0.1: - resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} - engines: {node: '>=10'} - hasBin: true - dev: false + dev: true /mnemonist@0.38.5: resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==} @@ -16963,11 +14530,6 @@ packages: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} dev: true - /mute-stream@1.0.0: - resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: false - /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -17072,13 +14634,10 @@ packages: resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} dev: false - /node-cleanup@2.1.2: - resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} - dev: true - /node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} + dev: true /node-emoji@2.1.3: resolution: {integrity: sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==} @@ -17108,6 +14667,7 @@ packages: data-uri-to-buffer: 4.0.1 fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 + dev: true /node-forge@1.3.1: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} @@ -17121,6 +14681,7 @@ packages: /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + dev: true /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} @@ -17145,7 +14706,7 @@ packages: engines: {node: ^16.14.0 || >=18.0.0} dependencies: hosted-git-info: 7.0.1 - is-core-module: 2.13.0 + is-core-module: 2.13.1 semver: 7.5.4 validate-npm-package-license: 3.0.4 dev: false @@ -17180,29 +14741,6 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dev: true - /npm-package-arg@10.1.0: - resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - hosted-git-info: 6.1.1 - proc-log: 3.0.0 - semver: 7.5.4 - validate-npm-package-name: 5.0.0 - dev: false - - /npm-registry-fetch@15.0.0: - resolution: {integrity: sha512-CMFzk0HMDQ3fmFZ4v62C05g6eBwoU3PxpzFf4QiE360vfmtKZJkj+iCpgLx+I4oJT6Kx8g67Coyk729Q27M2JQ==} - engines: {node: ^16.13.0 || >=18.0.0} - dependencies: - make-fetch-happen: 12.0.0 - minipass: 7.0.2 - minipass-fetch: 3.0.3 - minipass-json-stream: 1.0.1 - minizlib: 2.1.2 - npm-package-arg: 10.1.0 - proc-log: 3.0.0 - dev: false - /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -17248,8 +14786,8 @@ packages: resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 dev: true /object-keys@1.1.1: @@ -17260,8 +14798,8 @@ packages: resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 @@ -17269,17 +14807,8 @@ packages: resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.22.1 - dev: true - - /object.fromentries@2.0.6: - resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 dev: true @@ -17287,33 +14816,24 @@ packages: resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 dev: true /object.groupby@1.0.1: resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 dev: true /object.hasown@1.1.2: resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} dependencies: - define-properties: 1.2.0 - es-abstract: 1.22.1 - dev: true - - /object.values@1.1.6: - resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + define-properties: 1.2.1 es-abstract: 1.22.1 dev: true @@ -17321,8 +14841,8 @@ packages: resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 dev: true @@ -17441,37 +14961,23 @@ packages: log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 - - /ora@7.0.1: - resolution: {integrity: sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==} - engines: {node: '>=16'} - dependencies: - chalk: 5.3.0 - cli-cursor: 4.0.0 - cli-spinners: 2.9.0 - is-interactive: 2.0.0 - is-unicode-supported: 1.3.0 - log-symbols: 5.1.0 - stdin-discarder: 0.1.0 - string-width: 6.1.0 - strip-ansi: 7.1.0 - dev: false + dev: true /os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} - /oss-directory@0.0.7(ts-node@10.9.1)(typescript@5.2.2): + /oss-directory@0.0.7(ts-node@10.9.1)(typescript@5.3.3): resolution: {integrity: sha512-xIgkdK8IiI2ho5BX12HnspYDYO+c1CluASJdlIXGyaIWPHVftu6U3l3CeyZq+mMbPQFpvGgJCHCSoC+Ajo8dbw==} engines: {node: '>=16'} hasBin: true dependencies: - '@ethereum-attestation-service/eas-sdk': 1.4.0(ts-node@10.9.1)(typescript@5.2.2) + '@ethereum-attestation-service/eas-sdk': 1.4.0(ts-node@10.9.1)(typescript@5.3.3) ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) chalk: 5.3.0 dayjs: 1.11.10 - dotenv: 16.3.1 + dotenv: 16.4.1 ethers: 6.10.0 glob: 10.3.10 lodash: 4.17.21 @@ -17489,12 +14995,12 @@ packages: - utf-8-validate dev: false - /oss-directory@0.0.9(ts-node@10.9.1)(typescript@5.2.2): + /oss-directory@0.0.9(ts-node@10.9.1)(typescript@5.3.3): resolution: {integrity: sha512-ttPufmtzcGpgJYAmJDuSAYPFpCqQV6cSFADZgLRKKoxQPruttmKtrRutyZVn22WHRhyLu94xyMfvWy1xdt59ag==} engines: {node: '>=16'} hasBin: true dependencies: - '@ethereum-attestation-service/eas-sdk': 1.4.0(ts-node@10.9.1)(typescript@5.2.2) + '@ethereum-attestation-service/eas-sdk': 1.4.0(ts-node@10.9.1)(typescript@5.3.3) ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) chalk: 5.3.0 @@ -17567,6 +15073,7 @@ packages: engines: {node: '>=8'} dependencies: p-limit: 2.3.0 + dev: true /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} @@ -17624,10 +15131,6 @@ packages: semver: 7.5.4 dev: false - /packet-reader@1.0.0: - resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} - dev: false - /pad-left@2.1.0: resolution: {integrity: sha512-HJxs9K9AztdIQIAIa/OIazRAUW/L6B9hbQDxO4X07roW3eo9XqZc2ur9bn1StH9CnbbI9EgvejHQX7CBpCF1QA==} engines: {node: '>=0.10.0'} @@ -17694,12 +15197,6 @@ packages: resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==} dev: false - /parse5-htmlparser2-tree-adapter@6.0.1: - resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} - dependencies: - parse5: 6.0.1 - dev: false - /parse5-htmlparser2-tree-adapter@7.0.0: resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} dependencies: @@ -17707,14 +15204,6 @@ packages: parse5: 7.1.2 dev: false - /parse5@5.1.1: - resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} - dev: false - - /parse5@6.0.1: - resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - dev: false - /parse5@7.1.2: resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} dependencies: @@ -17818,12 +15307,6 @@ packages: engines: {node: '>=12'} dev: false - /pause-stream@0.0.11: - resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} - dependencies: - through: 2.3.8 - dev: true - /pbkdf2@3.1.2: resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} engines: {node: '>=0.12'} @@ -17842,70 +15325,6 @@ packages: estree-walker: 3.0.3 is-reference: 3.0.2 - /pg-cloudflare@1.1.1: - resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} - requiresBuild: true - dev: false - optional: true - - /pg-connection-string@2.6.2: - resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==} - dev: false - - /pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} - dev: false - - /pg-pool@3.6.1(pg@8.11.3): - resolution: {integrity: sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==} - peerDependencies: - pg: '>=8.0' - dependencies: - pg: 8.11.3 - dev: false - - /pg-protocol@1.6.0: - resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==} - dev: false - - /pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} - dependencies: - pg-int8: 1.0.1 - postgres-array: 2.0.0 - postgres-bytea: 1.0.0 - postgres-date: 1.0.7 - postgres-interval: 1.2.0 - dev: false - - /pg@8.11.3: - resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==} - engines: {node: '>= 8.0.0'} - peerDependencies: - pg-native: '>=3.0.1' - peerDependenciesMeta: - pg-native: - optional: true - dependencies: - buffer-writer: 2.0.0 - packet-reader: 1.0.0 - pg-connection-string: 2.6.2 - pg-pool: 3.6.1(pg@8.11.3) - pg-protocol: 1.6.0 - pg-types: 2.2.0 - pgpass: 1.0.5 - optionalDependencies: - pg-cloudflare: 1.1.1 - dev: false - - /pgpass@1.0.5: - resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} - dependencies: - split2: 4.2.0 - dev: false - /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} @@ -18399,7 +15818,6 @@ packages: nanoid: 3.3.7 picocolors: 1.0.0 source-map-js: 1.0.2 - dev: false /postcss@8.4.33: resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} @@ -18409,28 +15827,6 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 - /postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} - dev: false - - /postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} - dev: false - - /postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} - dev: false - - /postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} - dependencies: - xtend: 4.0.2 - dev: false - /preact@10.18.1: resolution: {integrity: sha512-mKUD7RRkQQM6s7Rkmi7IFkoEHjuFqRQUaXamO61E6Nn7vqF/bo7EZCmSyrUnp2UWHw0O7XjZ2eeXis+m7tf4lg==} dev: false @@ -18467,15 +15863,6 @@ packages: react-is: 17.0.2 dev: true - /pretty-format@29.6.2: - resolution: {integrity: sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.0 - ansi-styles: 5.2.0 - react-is: 18.2.0 - dev: true - /pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -18483,6 +15870,7 @@ packages: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.2.0 + dev: true /pretty-time@1.1.0: resolution: {integrity: sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==} @@ -18504,23 +15892,10 @@ packages: engines: {node: '>=6'} dev: false - /proc-log@3.0.0: - resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: false - /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: false - /promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - dependencies: - err-code: 2.0.3 - retry: 0.12.0 - dev: false - /promise@7.3.1: resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} dependencies: @@ -18579,14 +15954,6 @@ packages: ipaddr.js: 1.9.1 dev: false - /ps-tree@1.2.0: - resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} - engines: {node: '>= 0.10'} - hasBin: true - dependencies: - event-stream: 3.3.4 - dev: true - /psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} dev: true @@ -19137,34 +16504,19 @@ packages: strip-indent: 3.0.0 dev: true - /redis@4.6.11: - resolution: {integrity: sha512-kg1Lt4NZLYkAjPOj/WcyIGWfZfnyfKo1Wg9YKVSlzhFwxpFIl3LYI8BWy1Ab963LLDsTz2+OwdsesHKljB3WMQ==} - dependencies: - '@redis/bloom': 1.2.0(@redis/client@1.5.12) - '@redis/client': 1.5.12 - '@redis/graph': 1.1.1(@redis/client@1.5.12) - '@redis/json': 1.0.6(@redis/client@1.5.12) - '@redis/search': 1.1.6(@redis/client@1.5.12) - '@redis/time-series': 1.0.5(@redis/client@1.5.12) - dev: false - /reduce-flatten@2.0.0: resolution: {integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==} engines: {node: '>=6'} dev: false - /reflect-metadata@0.1.13: - resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} - dev: false - /reflect.getprototypeof@1.0.4: resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 define-properties: 1.2.1 es-abstract: 1.22.1 - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 globalthis: 1.0.3 which-builtin-type: 1.1.3 dev: true @@ -19180,10 +16532,6 @@ packages: resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} dev: false - /regenerator-runtime@0.14.0: - resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} - dev: false - /regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -19197,8 +16545,8 @@ packages: resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 functions-have-names: 1.2.3 dev: true @@ -19437,6 +16785,7 @@ packages: /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + dev: true /resolve-pathname@3.0.0: resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==} @@ -19469,7 +16818,7 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true dependencies: - is-core-module: 2.13.0 + is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: true @@ -19492,6 +16841,7 @@ packages: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 + dev: true /restore-cursor@4.0.0: resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} @@ -19499,34 +16849,7 @@ packages: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - - /retry-request@6.0.0: - resolution: {integrity: sha512-24kaFMd3wCnT3n4uPnsQh90ZSV8OISpfTFXJ00Wi+/oD2OPrp63EQ8hznk6rhxdlpwx2QBhQSDz2Fg46ki852g==} - engines: {node: '>=14'} - dependencies: - debug: 4.3.4(supports-color@8.1.1) - extend: 3.0.2 - transitivePeerDependencies: - - supports-color - dev: false - - /retry-request@7.0.1: - resolution: {integrity: sha512-ZI6vJp9rfB71mrZpw+n9p/B6HCsd7QJlSEQftZ+xfJzr3cQ9EPGKw1FF0BnViJ0fYREX6FhymBD2CARpmsFciQ==} - engines: {node: '>=14'} - dependencies: - '@types/request': 2.48.12 - debug: 4.3.4(supports-color@8.1.1) - extend: 3.0.2 - teeny-request: 9.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - dev: false + dev: true /retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} @@ -19558,14 +16881,6 @@ packages: dependencies: glob: 7.2.3 - /rimraf@5.0.1: - resolution: {integrity: sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==} - engines: {node: '>=14'} - hasBin: true - dependencies: - glob: 10.3.3 - dev: false - /ripemd160@2.0.2: resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} dependencies: @@ -19613,11 +16928,6 @@ packages: engines: {node: '>=0.12.0'} dev: true - /run-async@3.0.0: - resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} - engines: {node: '>=0.12.0'} - dev: false - /run-parallel-limit@1.1.0: resolution: {integrity: sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==} dependencies: @@ -19634,18 +16944,9 @@ packages: dev: false /rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - dependencies: - tslib: 2.6.2 - - /safe-array-concat@1.0.0: - resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} - engines: {node: '>=0.4'} + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - has-symbols: 1.0.3 - isarray: 2.0.5 + tslib: 2.6.2 dev: true /safe-array-concat@1.1.0: @@ -19676,8 +16977,8 @@ packages: /safe-regex-test@1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 is-regex: 1.1.4 dev: true @@ -19958,7 +17259,6 @@ packages: get-intrinsic: 1.2.2 gopd: 1.0.1 has-property-descriptors: 1.0.1 - dev: true /set-function-name@2.0.1: resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} @@ -19966,7 +17266,7 @@ packages: dependencies: define-data-property: 1.1.1 functions-have-names: 1.2.3 - has-property-descriptors: 1.0.0 + has-property-descriptors: 1.0.1 dev: true /set-harmonic-interval@1.0.1: @@ -20028,8 +17328,8 @@ packages: /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 object-inspect: 1.12.3 /signal-exit@3.0.7: @@ -20131,11 +17431,6 @@ packages: is-fullwidth-code-point: 5.0.0 dev: true - /smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - dev: false - /snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} dependencies: @@ -20175,14 +17470,6 @@ packages: websocket-driver: 0.7.4 dev: false - /socks@2.7.1: - resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} - engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} - dependencies: - ip: 2.0.0 - smart-buffer: 4.2.0 - dev: false - /solc@0.7.3(debug@4.3.4): resolution: {integrity: sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==} engines: {node: '>=8.0.0'} @@ -20292,17 +17579,6 @@ packages: - supports-color dev: false - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - dev: false - - /split@0.3.3: - resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} - dependencies: - through: 2.3.8 - dev: true - /sponge-case@1.0.1: resolution: {integrity: sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==} dependencies: @@ -20317,13 +17593,6 @@ packages: engines: {node: '>=12'} dev: false - /ssri@10.0.4: - resolution: {integrity: sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - minipass: 5.0.0 - dev: false - /stable@0.1.8: resolution: {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' @@ -20344,6 +17613,7 @@ packages: engines: {node: '>=10'} dependencies: escape-string-regexp: 2.0.0 + dev: true /stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} @@ -20385,13 +17655,6 @@ packages: resolution: {integrity: sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg==} dev: false - /stdin-discarder@0.1.0: - resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - bl: 5.1.0 - dev: false - /stop-iteration-iterator@1.0.0: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} @@ -20399,26 +17662,6 @@ packages: internal-slot: 1.0.5 dev: true - /stream-combiner@0.0.4: - resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} - dependencies: - duplexer: 0.1.2 - dev: true - - /stream-events@1.0.5: - resolution: {integrity: sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==} - dependencies: - stubs: 3.0.0 - dev: false - - /stream-shift@1.0.1: - resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==} - dev: false - - /stream-transform@3.2.10: - resolution: {integrity: sha512-Yu+x7zcWbWdyB0Td8dFzHt2JEyD6694CNq2lqh1rbuEBVxPtjb/GZ7xDnZcdYiU5E/RtufM54ClSEOzZDeWguA==} - dev: false - /streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -20456,15 +17699,6 @@ packages: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - /string-width@6.1.0: - resolution: {integrity: sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==} - engines: {node: '>=16'} - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 10.3.0 - strip-ansi: 7.1.0 - dev: false - /string-width@7.1.0: resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} engines: {node: '>=18'} @@ -20477,10 +17711,10 @@ packages: /string.prototype.matchall@4.0.8: resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 - get-intrinsic: 1.2.1 + get-intrinsic: 1.2.2 has-symbols: 1.0.3 internal-slot: 1.0.5 regexp.prototype.flags: 1.5.0 @@ -20491,24 +17725,24 @@ packages: resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 dev: true /string.prototype.trimend@1.0.6: resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 dev: true /string.prototype.trimstart@1.0.6: resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + call-bind: 1.0.5 + define-properties: 1.2.1 es-abstract: 1.22.1 dev: true @@ -20596,14 +17830,6 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - /strnum@1.0.5: - resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} - dev: false - - /stubs@3.0.0: - resolution: {integrity: sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==} - dev: false - /style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} dependencies: @@ -20833,20 +18059,7 @@ packages: minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 - - /teeny-request@9.0.0: - resolution: {integrity: sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==} - engines: {node: '>=14'} - dependencies: - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - node-fetch: 2.6.12 - stream-events: 1.0.5 - uuid: 9.0.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: false + dev: true /terser-webpack-plugin@5.3.9(webpack@5.89.0): resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} @@ -20888,6 +18101,7 @@ packages: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.2 + dev: true /text-hex@1.0.0: resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} @@ -20971,6 +18185,7 @@ packages: /tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + dev: true /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} @@ -21048,7 +18263,7 @@ packages: resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} dev: false - /ts-essentials@9.4.1(typescript@5.2.2): + /ts-essentials@9.4.1(typescript@5.3.3): resolution: {integrity: sha512-oke0rI2EN9pzHsesdmrOrnqv1eQODmJpd/noJjwj2ZPC3Z4N2wbjrOEqnsEgmvlO2+4fBb0a794DCna2elEVIQ==} peerDependencies: typescript: '>=4.1.0' @@ -21056,7 +18271,7 @@ packages: typescript: optional: true dependencies: - typescript: 5.2.2 + typescript: 5.3.3 dev: false /ts-interface-checker@0.1.13: @@ -21069,80 +18284,10 @@ packages: tslib: 2.6.2 dev: false - /ts-jest-resolver@2.0.1: - resolution: {integrity: sha512-FolE73BqVZCs8/RbLKxC67iaAtKpBWx7PeLKFW2zJQlOf9j851I7JRxSDenri2NFvVH3QP7v3S8q1AmL24Zb9Q==} - dependencies: - jest-resolve: 29.6.2 - dev: true - - /ts-jest@29.1.1(@babel/core@7.23.7)(jest@29.6.2)(typescript@5.2.2): - resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/types': ^29.0.0 - babel-jest: ^29.0.0 - esbuild: '*' - jest: ^29.0.0 - typescript: '>=4.3 <6' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true - dependencies: - '@babel/core': 7.23.7 - bs-logger: 0.2.6 - fast-json-stable-stringify: 2.1.0 - jest: 29.6.2(@types/node@20.6.3)(ts-node@10.9.1) - jest-util: 29.6.2 - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.5.4 - typescript: 5.2.2 - yargs-parser: 21.1.1 - dev: true - /ts-log@2.2.5: resolution: {integrity: sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==} dev: true - /ts-node@10.9.1(@types/node@20.11.17)(typescript@5.2.2): - resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.11.17 - acorn: 8.10.0 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.2.2 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - /ts-node@10.9.1(@types/node@20.11.17)(typescript@5.3.3): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true @@ -21172,51 +18317,6 @@ packages: typescript: 5.3.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - dev: true - - /ts-node@10.9.1(@types/node@20.6.3)(typescript@5.2.2): - resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.6.3 - acorn: 8.10.0 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.2.2 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - - /tsc-watch@6.0.4(typescript@5.2.2): - resolution: {integrity: sha512-cHvbvhjO86w2aGlaHgSCeQRl+Aqw6X6XN4sQMPZKF88GoP30O+oTuh5lRIJr5pgFWrRpF1AgXnJJ2DoFEIPHyg==} - engines: {node: '>=12.12.0'} - hasBin: true - peerDependencies: - typescript: '*' - dependencies: - cross-spawn: 7.0.3 - node-cleanup: 2.1.2 - ps-tree: 1.2.0 - string-argv: 0.3.2 - typescript: 5.2.2 - dev: true /tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -21323,6 +18423,7 @@ packages: /type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} + dev: true /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} @@ -21376,8 +18477,8 @@ packages: resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 + call-bind: 1.0.5 + get-intrinsic: 1.2.2 is-typed-array: 1.1.12 dev: true @@ -21385,7 +18486,7 @@ packages: resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 for-each: 0.3.3 has-proto: 1.0.1 is-typed-array: 1.1.12 @@ -21396,7 +18497,7 @@ packages: engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.5 - call-bind: 1.0.2 + call-bind: 1.0.5 for-each: 0.3.3 has-proto: 1.0.1 is-typed-array: 1.1.12 @@ -21405,7 +18506,7 @@ packages: /typed-array-length@1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 for-each: 0.3.3 is-typed-array: 1.1.12 dev: true @@ -21416,91 +18517,6 @@ packages: is-typedarray: 1.0.0 dev: false - /typeorm@0.3.17(pg@8.11.3)(redis@4.6.11)(ts-node@10.9.1): - resolution: {integrity: sha512-UDjUEwIQalO9tWw9O2A4GU+sT3oyoUXheHJy4ft+RFdnRdQctdQ34L9SqE2p7LdwzafHx1maxT+bqXON+Qnmig==} - engines: {node: '>= 12.9.0'} - hasBin: true - peerDependencies: - '@google-cloud/spanner': ^5.18.0 - '@sap/hana-client': ^2.12.25 - better-sqlite3: ^7.1.2 || ^8.0.0 - hdb-pool: ^0.1.6 - ioredis: ^5.0.4 - mongodb: ^5.2.0 - mssql: ^9.1.1 - mysql2: ^2.2.5 || ^3.0.1 - oracledb: ^5.1.0 - pg: ^8.5.1 - pg-native: ^3.0.0 - pg-query-stream: ^4.0.0 - redis: ^3.1.1 || ^4.0.0 - sql.js: ^1.4.0 - sqlite3: ^5.0.3 - ts-node: ^10.7.0 - typeorm-aurora-data-api-driver: ^2.0.0 - peerDependenciesMeta: - '@google-cloud/spanner': - optional: true - '@sap/hana-client': - optional: true - better-sqlite3: - optional: true - hdb-pool: - optional: true - ioredis: - optional: true - mongodb: - optional: true - mssql: - optional: true - mysql2: - optional: true - oracledb: - optional: true - pg: - optional: true - pg-native: - optional: true - pg-query-stream: - optional: true - redis: - optional: true - sql.js: - optional: true - sqlite3: - optional: true - ts-node: - optional: true - typeorm-aurora-data-api-driver: - optional: true - dependencies: - '@sqltools/formatter': 1.2.5 - app-root-path: 3.1.0 - buffer: 6.0.3 - chalk: 4.1.2 - cli-highlight: 2.1.11 - date-fns: 2.30.0 - debug: 4.3.4(supports-color@8.1.1) - dotenv: 16.4.1 - glob: 8.1.0 - mkdirp: 2.1.6 - pg: 8.11.3 - redis: 4.6.11 - reflect-metadata: 0.1.13 - sha.js: 2.4.11 - ts-node: 10.9.1(@types/node@20.6.3)(typescript@5.2.2) - tslib: 2.6.2 - uuid: 9.0.1 - yargs: 17.7.2 - transitivePeerDependencies: - - supports-color - dev: false - - /typescript@5.2.2: - resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} - engines: {node: '>=14.17'} - hasBin: true - /typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} @@ -21523,7 +18539,7 @@ packages: /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 @@ -21592,20 +18608,6 @@ packages: trough: 2.1.0 vfile: 6.0.1 - /unique-filename@3.0.0: - resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - unique-slug: 4.0.0 - dev: false - - /unique-slug@4.0.0: - resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - imurmurhash: 0.1.4 - dev: false - /unique-string@3.0.0: resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} engines: {node: '>=12'} @@ -21655,7 +18657,7 @@ packages: /universal-github-app-jwt@1.1.1: resolution: {integrity: sha512-G33RTLrIBMFmlDV4u4CBF7dh71eWwykck4XgaxaIVeZKOYZRAAxvcGMRFTUclVY6xoUPQvO4Ne5wKGxYm/Yy9w==} dependencies: - '@types/jsonwebtoken': 9.0.2 + '@types/jsonwebtoken': 9.0.5 jsonwebtoken: 9.0.1 dev: false @@ -21810,11 +18812,6 @@ packages: hasBin: true dev: false - /uuid@9.0.0: - resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} - hasBin: true - dev: false - /uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -21839,18 +18836,6 @@ packages: spdx-expression-parse: 3.0.1 dev: false - /validate-npm-package-name@5.0.0: - resolution: {integrity: sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - builtins: 5.0.1 - dev: false - - /validator@13.11.0: - resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} - engines: {node: '>= 0.10'} - dev: false - /value-equal@1.0.1: resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==} dev: false @@ -21915,6 +18900,7 @@ packages: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: makeerror: 1.0.12 + dev: true /watchpack@2.4.0: resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} @@ -21933,6 +18919,7 @@ packages: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: defaults: 1.0.4 + dev: true /web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -21941,6 +18928,7 @@ packages: /web-streams-polyfill@3.2.1: resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} engines: {node: '>= 8'} + dev: true /webcrypto-core@1.7.7: resolution: {integrity: sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g==} @@ -22198,7 +19186,7 @@ packages: engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.5 - call-bind: 1.0.2 + call-bind: 1.0.5 for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.0 @@ -22248,15 +19236,6 @@ packages: winston: 3.11.0 dev: false - /winston-transport@4.5.0: - resolution: {integrity: sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==} - engines: {node: '>= 6.4.0'} - dependencies: - logform: 2.6.0 - readable-stream: 3.6.2 - triple-beam: 1.4.1 - dev: false - /winston-transport@4.6.0: resolution: {integrity: sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==} engines: {node: '>= 12.0.0'} @@ -22266,23 +19245,6 @@ packages: triple-beam: 1.4.1 dev: false - /winston@3.10.0: - resolution: {integrity: sha512-nT6SIDaE9B7ZRO0u3UvdrimG0HkB7dSTAgInQnNR2SOPJ4bvq5q79+pXLftKmP52lJGW15+H5MCK0nM9D3KB/g==} - engines: {node: '>= 12.0.0'} - dependencies: - '@colors/colors': 1.5.0 - '@dabh/diagnostics': 2.0.3 - async: 3.2.4 - is-stream: 2.0.1 - logform: 2.5.1 - one-time: 1.0.0 - readable-stream: 3.6.2 - safe-stable-stringify: 2.4.3 - stack-trace: 0.0.10 - triple-beam: 1.4.1 - winston-transport: 4.5.0 - dev: false - /winston@3.11.0: resolution: {integrity: sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==} engines: {node: '>= 12.0.0'} @@ -22319,6 +19281,7 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 + dev: true /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} @@ -22371,6 +19334,7 @@ packages: dependencies: imurmurhash: 0.1.4 signal-exit: 3.0.7 + dev: true /write-file-atomic@5.0.1: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} @@ -22483,11 +19447,6 @@ packages: engines: {node: '>=0.4.0'} dev: false - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - dev: false - /y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} dev: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 84753da76..52689b355 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,7 +1,6 @@ packages: - - "cloudquery/oss-directory" - - "cloudquery/github-resolve-repos" - - "docs" - - "frontend" - - "hasura" - - "indexer" + - "warehouse/cloudquery-oss-directory" + - "warehouse/cloudquery-github-resolve-repos" + - "apps/docs" + - "apps/frontend" + - "apps/hasura" diff --git a/poetry.lock b/poetry.lock index 9f54beff3..7f03b6859 100644 --- a/poetry.lock +++ b/poetry.lock @@ -776,7 +776,7 @@ cloudquery-plugin-sdk = "^0.1.12" [package.source] type = "directory" -url = "cloudquery/dune-contract-usage" +url = "warehouse/cloudquery-dune-contract-usage" [[package]] name = "example-plugin" @@ -792,7 +792,7 @@ cloudquery-plugin-sdk = "^0.1.12" [package.source] type = "directory" -url = "cloudquery/example-plugin" +url = "warehouse/cloudquery-example-plugin" [[package]] name = "exceptiongroup" @@ -1038,18 +1038,18 @@ grpc = ["grpcio (>=1.38.0,<2.0dev)", "grpcio-status (>=1.38.0,<2.0.dev0)"] [[package]] name = "google-cloud-dataproc" -version = "5.9.2" +version = "5.9.3" description = "Google Cloud Dataproc API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-cloud-dataproc-5.9.2.tar.gz", hash = "sha256:1352e3cc4e146db1f09fa4287559248c8b3d9c0cfecea56c25c3f4f63d58e4f8"}, - {file = "google_cloud_dataproc-5.9.2-py2.py3-none-any.whl", hash = "sha256:a728e35db70132dc5d2e6b27627825775f5a0a2cd7024fa1149669d3de4b436d"}, + {file = "google-cloud-dataproc-5.9.3.tar.gz", hash = "sha256:97d647891e7f4cd25f6b839ae57cd355809df2ca396653ed24af62b4ef02f412"}, + {file = "google_cloud_dataproc-5.9.3-py2.py3-none-any.whl", hash = "sha256:034615e8aca7f2b917a29ee1ca317fa65f8fa878018f2b80a2487e09e32b297a"}, ] [package.dependencies] google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} -google-auth = ">=2.14.1,<3.0.0dev" +google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev" grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" @@ -1571,13 +1571,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jinja2-simple-tags" -version = "0.5.0" +version = "0.6.1" description = "Base classes for quick-and-easy template tag development" optional = false python-versions = ">=3.6" files = [ - {file = "jinja2-simple-tags-0.5.0.tar.gz", hash = "sha256:59c27096e337ed735c761a3fbbac57bd44913beaaadafdf0dfd450bcd4f7724e"}, - {file = "jinja2_simple_tags-0.5.0-py2.py3-none-any.whl", hash = "sha256:131cbc95eaed329be390509dcfd9d8d91231a66b27ef2259c76f1d19148cef95"}, + {file = "jinja2-simple-tags-0.6.1.tar.gz", hash = "sha256:54abf83883dcd13f8fd2ea2c42feeea8418df3640907bd5251dec5e25a6af0e3"}, + {file = "jinja2_simple_tags-0.6.1-py2.py3-none-any.whl", hash = "sha256:7b7cfa92f6813a1e0f0b61b9efcab60e6793674753e1f784ff270542e80ae20f"}, ] [package.dependencies] @@ -2628,13 +2628,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyparsing" -version = "3.1.1" +version = "3.1.2" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.6.8" files = [ - {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, - {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, ] [package.extras] @@ -3650,4 +3650,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "8751e32283d001595f65ab967dd7fda57171c0d0ca2bb446d83347c1f9b221a9" +content-hash = "3000eb696467612694ce0789705911c630f2ba3adf2279e9c7a0b419391e5681" diff --git a/pyproject.toml b/pyproject.toml index 089d85a3a..56ff468e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,11 +5,15 @@ description = "Impact measurement for open source software" authors = ["Kariba Labs"] license = "Apache-2.0" readme = "README.md" +packages = [ + { include = "bq2cloudsql", from = "warehouse/bq2cloudsql" }, + { include = "warehouse_common", from = "warehouse/common" }, +] [tool.poetry.dependencies] python = "^3.11" -example-plugin = { path = "cloudquery/example-plugin", develop = true } -dune-contract-usage = { path = "cloudquery/dune-contract-usage", develop = true } +example-plugin = { path = "warehouse/cloudquery-example-plugin", develop = true } +dune-contract-usage = { path = "warehouse/cloudquery-dune-contract-usage", develop = true } google-cloud-bigquery = "^3.17.1" pendulum = "^3.0.0" google-api-python-client = "^2.116.0" @@ -20,7 +24,7 @@ python-dotenv = "^1.0.1" dbt-bigquery = "^1.7.0" [tool.poetry.scripts] -bq2cloudsql = 'oso.bq2cloudsql.script:run' +bq2cloudsql = 'bq2cloudsql.script:run' [tool.poetry.group.dev.dependencies] ipython = "^8.21.0" @@ -43,9 +47,9 @@ indent_unit = "space" tab_space_size = 2 [tool.sqlfluff.templater.jinja] -load_macros_from_path = "dbt/macros/" +load_macros_from_path = "warehouse/dbt/macros/" apply_dbt_builtins = true -library_path = "oso/dbtlintmock" +library_path = "warehouse/common/dbtlintmock" [tool.sqlfluff.templater.dbt] project_dir = "." diff --git a/cloudquery/dune-contract-usage/tests/__init__.py b/warehouse/bq2cloudsql/__init__.py similarity index 100% rename from cloudquery/dune-contract-usage/tests/__init__.py rename to warehouse/bq2cloudsql/__init__.py diff --git a/oso/bq2cloudsql/cloudsql.py b/warehouse/bq2cloudsql/cloudsql.py similarity index 100% rename from oso/bq2cloudsql/cloudsql.py rename to warehouse/bq2cloudsql/cloudsql.py diff --git a/oso/bq2cloudsql/script.py b/warehouse/bq2cloudsql/script.py similarity index 100% rename from oso/bq2cloudsql/script.py rename to warehouse/bq2cloudsql/script.py diff --git a/oso/bq2cloudsql/synchronizer.py b/warehouse/bq2cloudsql/synchronizer.py similarity index 100% rename from oso/bq2cloudsql/synchronizer.py rename to warehouse/bq2cloudsql/synchronizer.py diff --git a/cloudquery/dune-contract-usage/README.md b/warehouse/cloudquery-dune-contract-usage/README.md similarity index 100% rename from cloudquery/dune-contract-usage/README.md rename to warehouse/cloudquery-dune-contract-usage/README.md diff --git a/cloudquery/dune-contract-usage/dune_contract_usage/__init__.py b/warehouse/cloudquery-dune-contract-usage/dune_contract_usage/__init__.py similarity index 100% rename from cloudquery/dune-contract-usage/dune_contract_usage/__init__.py rename to warehouse/cloudquery-dune-contract-usage/dune_contract_usage/__init__.py diff --git a/cloudquery/dune-contract-usage/dune_contract_usage/client/__init__.py b/warehouse/cloudquery-dune-contract-usage/dune_contract_usage/client/__init__.py similarity index 100% rename from cloudquery/dune-contract-usage/dune_contract_usage/client/__init__.py rename to warehouse/cloudquery-dune-contract-usage/dune_contract_usage/client/__init__.py diff --git a/cloudquery/dune-contract-usage/dune_contract_usage/client/client.py b/warehouse/cloudquery-dune-contract-usage/dune_contract_usage/client/client.py similarity index 100% rename from cloudquery/dune-contract-usage/dune_contract_usage/client/client.py rename to warehouse/cloudquery-dune-contract-usage/dune_contract_usage/client/client.py diff --git a/cloudquery/dune-contract-usage/dune_contract_usage/load_csvs.py b/warehouse/cloudquery-dune-contract-usage/dune_contract_usage/load_csvs.py similarity index 100% rename from cloudquery/dune-contract-usage/dune_contract_usage/load_csvs.py rename to warehouse/cloudquery-dune-contract-usage/dune_contract_usage/load_csvs.py diff --git a/cloudquery/example-plugin/tests/__init__.py b/warehouse/cloudquery-dune-contract-usage/dune_contract_usage/load_from_api.py similarity index 100% rename from cloudquery/example-plugin/tests/__init__.py rename to warehouse/cloudquery-dune-contract-usage/dune_contract_usage/load_from_api.py diff --git a/cloudquery/dune-contract-usage/dune_contract_usage/parse.py b/warehouse/cloudquery-dune-contract-usage/dune_contract_usage/parse.py similarity index 100% rename from cloudquery/dune-contract-usage/dune_contract_usage/parse.py rename to warehouse/cloudquery-dune-contract-usage/dune_contract_usage/parse.py diff --git a/cloudquery/dune-contract-usage/dune_contract_usage/plugin.py b/warehouse/cloudquery-dune-contract-usage/dune_contract_usage/plugin.py similarity index 100% rename from cloudquery/dune-contract-usage/dune_contract_usage/plugin.py rename to warehouse/cloudquery-dune-contract-usage/dune_contract_usage/plugin.py diff --git a/cloudquery/dune-contract-usage/dune_contract_usage/serve.py b/warehouse/cloudquery-dune-contract-usage/dune_contract_usage/serve.py similarity index 100% rename from cloudquery/dune-contract-usage/dune_contract_usage/serve.py rename to warehouse/cloudquery-dune-contract-usage/dune_contract_usage/serve.py diff --git a/cloudquery/dune-contract-usage/dune_contract_usage/tables/__init__.py b/warehouse/cloudquery-dune-contract-usage/dune_contract_usage/tables/__init__.py similarity index 100% rename from cloudquery/dune-contract-usage/dune_contract_usage/tables/__init__.py rename to warehouse/cloudquery-dune-contract-usage/dune_contract_usage/tables/__init__.py diff --git a/cloudquery/dune-contract-usage/dune_contract_usage/tables/table.py b/warehouse/cloudquery-dune-contract-usage/dune_contract_usage/tables/table.py similarity index 100% rename from cloudquery/dune-contract-usage/dune_contract_usage/tables/table.py rename to warehouse/cloudquery-dune-contract-usage/dune_contract_usage/tables/table.py diff --git a/cloudquery/dune-contract-usage/pyproject.toml b/warehouse/cloudquery-dune-contract-usage/pyproject.toml similarity index 100% rename from cloudquery/dune-contract-usage/pyproject.toml rename to warehouse/cloudquery-dune-contract-usage/pyproject.toml diff --git a/warehouse/cloudquery-dune-contract-usage/test.yml b/warehouse/cloudquery-dune-contract-usage/test.yml new file mode 100644 index 000000000..a0f8c2901 --- /dev/null +++ b/warehouse/cloudquery-dune-contract-usage/test.yml @@ -0,0 +1,24 @@ +kind: source +spec: + name: "contract-usage" + registry: "grpc" + path: "localhost:7777" + version: "v0.0.1" + tables: + ["*"] + destinations: + - "bigquery" + spec: + csv_folder_path: "/home/raven/optimism_all_contracts_20240305/" + chain: optimism +--- +kind: destination +spec: + name: bigquery + path: cloudquery/bigquery + registry: cloudquery + version: "v3.3.13" + write_mode: "append" + spec: + project_id: opensource-observer + dataset_id: oso diff --git a/cloudquery/dune-contract-usage/testdata/config.yml b/warehouse/cloudquery-dune-contract-usage/testdata/config.yml similarity index 100% rename from cloudquery/dune-contract-usage/testdata/config.yml rename to warehouse/cloudquery-dune-contract-usage/testdata/config.yml diff --git a/indexer/src/__init__.py b/warehouse/cloudquery-dune-contract-usage/tests/__init__.py similarity index 100% rename from indexer/src/__init__.py rename to warehouse/cloudquery-dune-contract-usage/tests/__init__.py diff --git a/cloudquery/example-plugin/README.md b/warehouse/cloudquery-example-plugin/README.md similarity index 100% rename from cloudquery/example-plugin/README.md rename to warehouse/cloudquery-example-plugin/README.md diff --git a/cloudquery/example-plugin/example_plugin/__init__.py b/warehouse/cloudquery-example-plugin/example_plugin/__init__.py similarity index 100% rename from cloudquery/example-plugin/example_plugin/__init__.py rename to warehouse/cloudquery-example-plugin/example_plugin/__init__.py diff --git a/cloudquery/example-plugin/example_plugin/client/__init__.py b/warehouse/cloudquery-example-plugin/example_plugin/client/__init__.py similarity index 100% rename from cloudquery/example-plugin/example_plugin/client/__init__.py rename to warehouse/cloudquery-example-plugin/example_plugin/client/__init__.py diff --git a/cloudquery/example-plugin/example_plugin/client/client.py b/warehouse/cloudquery-example-plugin/example_plugin/client/client.py similarity index 100% rename from cloudquery/example-plugin/example_plugin/client/client.py rename to warehouse/cloudquery-example-plugin/example_plugin/client/client.py diff --git a/cloudquery/example-plugin/example_plugin/plugin.py b/warehouse/cloudquery-example-plugin/example_plugin/plugin.py similarity index 100% rename from cloudquery/example-plugin/example_plugin/plugin.py rename to warehouse/cloudquery-example-plugin/example_plugin/plugin.py diff --git a/cloudquery/example-plugin/example_plugin/serve.py b/warehouse/cloudquery-example-plugin/example_plugin/serve.py similarity index 100% rename from cloudquery/example-plugin/example_plugin/serve.py rename to warehouse/cloudquery-example-plugin/example_plugin/serve.py diff --git a/cloudquery/example-plugin/example_plugin/tables/__init__.py b/warehouse/cloudquery-example-plugin/example_plugin/tables/__init__.py similarity index 100% rename from cloudquery/example-plugin/example_plugin/tables/__init__.py rename to warehouse/cloudquery-example-plugin/example_plugin/tables/__init__.py diff --git a/cloudquery/example-plugin/example_plugin/tables/table.py b/warehouse/cloudquery-example-plugin/example_plugin/tables/table.py similarity index 100% rename from cloudquery/example-plugin/example_plugin/tables/table.py rename to warehouse/cloudquery-example-plugin/example_plugin/tables/table.py diff --git a/cloudquery/example-plugin/pyproject.toml b/warehouse/cloudquery-example-plugin/pyproject.toml similarity index 100% rename from cloudquery/example-plugin/pyproject.toml rename to warehouse/cloudquery-example-plugin/pyproject.toml diff --git a/cloudquery/example-plugin/testdata/config.yml b/warehouse/cloudquery-example-plugin/testdata/config.yml similarity index 100% rename from cloudquery/example-plugin/testdata/config.yml rename to warehouse/cloudquery-example-plugin/testdata/config.yml diff --git a/indexer/src/events/__init__.py b/warehouse/cloudquery-example-plugin/tests/__init__.py similarity index 100% rename from indexer/src/events/__init__.py rename to warehouse/cloudquery-example-plugin/tests/__init__.py diff --git a/cloudquery/oss-directory/.eslintrc.json b/warehouse/cloudquery-github-resolve-repos/.eslintrc.json similarity index 76% rename from cloudquery/oss-directory/.eslintrc.json rename to warehouse/cloudquery-github-resolve-repos/.eslintrc.json index b287e32ba..b908f7638 100644 --- a/cloudquery/oss-directory/.eslintrc.json +++ b/warehouse/cloudquery-github-resolve-repos/.eslintrc.json @@ -7,7 +7,7 @@ }, "ecmaVersion": "latest", "sourceType": "module", - "project": ["./cloudquery/oss-directory/tsconfig.json"] + "project": ["./warehouse/cloudquery-github-resolve-repos/tsconfig.json"] }, "rules": { "@next/next/no-html-link-for-pages": "off" diff --git a/cloudquery/github-resolve-repos/README.md b/warehouse/cloudquery-github-resolve-repos/README.md similarity index 100% rename from cloudquery/github-resolve-repos/README.md rename to warehouse/cloudquery-github-resolve-repos/README.md diff --git a/cloudquery/github-resolve-repos/config.test.json b/warehouse/cloudquery-github-resolve-repos/config.test.json similarity index 100% rename from cloudquery/github-resolve-repos/config.test.json rename to warehouse/cloudquery-github-resolve-repos/config.test.json diff --git a/cloudquery/github-resolve-repos/package.json b/warehouse/cloudquery-github-resolve-repos/package.json similarity index 100% rename from cloudquery/github-resolve-repos/package.json rename to warehouse/cloudquery-github-resolve-repos/package.json diff --git a/cloudquery/github-resolve-repos/src/github/get-org-repos.ts b/warehouse/cloudquery-github-resolve-repos/src/github/get-org-repos.ts similarity index 100% rename from cloudquery/github-resolve-repos/src/github/get-org-repos.ts rename to warehouse/cloudquery-github-resolve-repos/src/github/get-org-repos.ts diff --git a/cloudquery/github-resolve-repos/src/github/getPath.ts b/warehouse/cloudquery-github-resolve-repos/src/github/getPath.ts similarity index 100% rename from cloudquery/github-resolve-repos/src/github/getPath.ts rename to warehouse/cloudquery-github-resolve-repos/src/github/getPath.ts diff --git a/cloudquery/github-resolve-repos/src/github/repositories.ts b/warehouse/cloudquery-github-resolve-repos/src/github/repositories.ts similarity index 100% rename from cloudquery/github-resolve-repos/src/github/repositories.ts rename to warehouse/cloudquery-github-resolve-repos/src/github/repositories.ts diff --git a/cloudquery/github-resolve-repos/src/github/unpaginate.ts b/warehouse/cloudquery-github-resolve-repos/src/github/unpaginate.ts similarity index 100% rename from cloudquery/github-resolve-repos/src/github/unpaginate.ts rename to warehouse/cloudquery-github-resolve-repos/src/github/unpaginate.ts diff --git a/cloudquery/github-resolve-repos/src/main.ts b/warehouse/cloudquery-github-resolve-repos/src/main.ts similarity index 100% rename from cloudquery/github-resolve-repos/src/main.ts rename to warehouse/cloudquery-github-resolve-repos/src/main.ts diff --git a/cloudquery/github-resolve-repos/src/plugin.ts b/warehouse/cloudquery-github-resolve-repos/src/plugin.ts similarity index 100% rename from cloudquery/github-resolve-repos/src/plugin.ts rename to warehouse/cloudquery-github-resolve-repos/src/plugin.ts diff --git a/cloudquery/github-resolve-repos/src/spec.ts b/warehouse/cloudquery-github-resolve-repos/src/spec.ts similarity index 100% rename from cloudquery/github-resolve-repos/src/spec.ts rename to warehouse/cloudquery-github-resolve-repos/src/spec.ts diff --git a/cloudquery/github-resolve-repos/src/tables.ts b/warehouse/cloudquery-github-resolve-repos/src/tables.ts similarity index 100% rename from cloudquery/github-resolve-repos/src/tables.ts rename to warehouse/cloudquery-github-resolve-repos/src/tables.ts diff --git a/cloudquery/github-resolve-repos/test.yml b/warehouse/cloudquery-github-resolve-repos/test.yml similarity index 100% rename from cloudquery/github-resolve-repos/test.yml rename to warehouse/cloudquery-github-resolve-repos/test.yml diff --git a/cloudquery/github-resolve-repos/tsconfig.json b/warehouse/cloudquery-github-resolve-repos/tsconfig.json similarity index 100% rename from cloudquery/github-resolve-repos/tsconfig.json rename to warehouse/cloudquery-github-resolve-repos/tsconfig.json diff --git a/cloudquery/github-resolve-repos/.eslintrc.json b/warehouse/cloudquery-oss-directory/.eslintrc.json similarity index 78% rename from cloudquery/github-resolve-repos/.eslintrc.json rename to warehouse/cloudquery-oss-directory/.eslintrc.json index 4df3d760a..4bd6d105c 100644 --- a/cloudquery/github-resolve-repos/.eslintrc.json +++ b/warehouse/cloudquery-oss-directory/.eslintrc.json @@ -7,7 +7,7 @@ }, "ecmaVersion": "latest", "sourceType": "module", - "project": ["./cloudquery/github-resolve-repos/tsconfig.json"] + "project": ["./warehouse/cloudquery-oss-directory/tsconfig.json"] }, "rules": { "@next/next/no-html-link-for-pages": "off" diff --git a/cloudquery/oss-directory/README.md b/warehouse/cloudquery-oss-directory/README.md similarity index 100% rename from cloudquery/oss-directory/README.md rename to warehouse/cloudquery-oss-directory/README.md diff --git a/cloudquery/oss-directory/config.test.json b/warehouse/cloudquery-oss-directory/config.test.json similarity index 100% rename from cloudquery/oss-directory/config.test.json rename to warehouse/cloudquery-oss-directory/config.test.json diff --git a/cloudquery/oss-directory/package.json b/warehouse/cloudquery-oss-directory/package.json similarity index 100% rename from cloudquery/oss-directory/package.json rename to warehouse/cloudquery-oss-directory/package.json diff --git a/cloudquery/oss-directory/src/main.ts b/warehouse/cloudquery-oss-directory/src/main.ts similarity index 100% rename from cloudquery/oss-directory/src/main.ts rename to warehouse/cloudquery-oss-directory/src/main.ts diff --git a/cloudquery/oss-directory/src/plugin.ts b/warehouse/cloudquery-oss-directory/src/plugin.ts similarity index 100% rename from cloudquery/oss-directory/src/plugin.ts rename to warehouse/cloudquery-oss-directory/src/plugin.ts diff --git a/cloudquery/oss-directory/src/spec.ts b/warehouse/cloudquery-oss-directory/src/spec.ts similarity index 100% rename from cloudquery/oss-directory/src/spec.ts rename to warehouse/cloudquery-oss-directory/src/spec.ts diff --git a/cloudquery/oss-directory/src/tables.ts b/warehouse/cloudquery-oss-directory/src/tables.ts similarity index 100% rename from cloudquery/oss-directory/src/tables.ts rename to warehouse/cloudquery-oss-directory/src/tables.ts diff --git a/cloudquery/oss-directory/test.yml b/warehouse/cloudquery-oss-directory/test.yml similarity index 100% rename from cloudquery/oss-directory/test.yml rename to warehouse/cloudquery-oss-directory/test.yml diff --git a/cloudquery/oss-directory/tsconfig.json b/warehouse/cloudquery-oss-directory/tsconfig.json similarity index 100% rename from cloudquery/oss-directory/tsconfig.json rename to warehouse/cloudquery-oss-directory/tsconfig.json diff --git a/oso/bq2cloudsql/__init__.py b/warehouse/common/__init__.py similarity index 100% rename from oso/bq2cloudsql/__init__.py rename to warehouse/common/__init__.py diff --git a/oso/dbtlintmock/README.md b/warehouse/common/dbtlintmock/README.md similarity index 100% rename from oso/dbtlintmock/README.md rename to warehouse/common/dbtlintmock/README.md diff --git a/oso/dbtlintmock/__init__.py b/warehouse/common/dbtlintmock/__init__.py similarity index 100% rename from oso/dbtlintmock/__init__.py rename to warehouse/common/dbtlintmock/__init__.py diff --git a/dbt/.gitignore b/warehouse/dbt/.gitignore similarity index 100% rename from dbt/.gitignore rename to warehouse/dbt/.gitignore diff --git a/dbt/README.md b/warehouse/dbt/README.md similarity index 100% rename from dbt/README.md rename to warehouse/dbt/README.md diff --git a/dbt/analyses/.gitkeep b/warehouse/dbt/analyses/.gitkeep similarity index 100% rename from dbt/analyses/.gitkeep rename to warehouse/dbt/analyses/.gitkeep diff --git a/dbt/macros/.gitkeep b/warehouse/dbt/macros/.gitkeep similarity index 100% rename from dbt/macros/.gitkeep rename to warehouse/dbt/macros/.gitkeep diff --git a/dbt/macros/models/dune_contract_invocation.sql b/warehouse/dbt/macros/models/dune_contract_invocation.sql similarity index 100% rename from dbt/macros/models/dune_contract_invocation.sql rename to warehouse/dbt/macros/models/dune_contract_invocation.sql diff --git a/dbt/macros/oso_artifact_id.sql b/warehouse/dbt/macros/oso_artifact_id.sql similarity index 100% rename from dbt/macros/oso_artifact_id.sql rename to warehouse/dbt/macros/oso_artifact_id.sql diff --git a/dbt/macros/oso_id.sql b/warehouse/dbt/macros/oso_id.sql similarity index 100% rename from dbt/macros/oso_id.sql rename to warehouse/dbt/macros/oso_id.sql diff --git a/dbt/macros/oso_source.sql b/warehouse/dbt/macros/oso_source.sql similarity index 100% rename from dbt/macros/oso_source.sql rename to warehouse/dbt/macros/oso_source.sql diff --git a/dbt/macros/urlsafe_base64.sql b/warehouse/dbt/macros/urlsafe_base64.sql similarity index 100% rename from dbt/macros/urlsafe_base64.sql rename to warehouse/dbt/macros/urlsafe_base64.sql diff --git a/dbt/models/dune_sources.yml b/warehouse/dbt/models/dune_sources.yml similarity index 100% rename from dbt/models/dune_sources.yml rename to warehouse/dbt/models/dune_sources.yml diff --git a/dbt/models/github_sources.yml b/warehouse/dbt/models/github_sources.yml similarity index 100% rename from dbt/models/github_sources.yml rename to warehouse/dbt/models/github_sources.yml diff --git a/dbt/models/intermediate/contributors/int_devs.sql b/warehouse/dbt/models/intermediate/contributors/int_devs.sql similarity index 100% rename from dbt/models/intermediate/contributors/int_devs.sql rename to warehouse/dbt/models/intermediate/contributors/int_devs.sql diff --git a/dbt/models/intermediate/contributors/int_users.sql b/warehouse/dbt/models/intermediate/contributors/int_users.sql similarity index 100% rename from dbt/models/intermediate/contributors/int_users.sql rename to warehouse/dbt/models/intermediate/contributors/int_users.sql diff --git a/dbt/models/intermediate/directory/int_artifacts.sql b/warehouse/dbt/models/intermediate/directory/int_artifacts.sql similarity index 100% rename from dbt/models/intermediate/directory/int_artifacts.sql rename to warehouse/dbt/models/intermediate/directory/int_artifacts.sql diff --git a/dbt/models/intermediate/directory/int_projects_by_collection.sql b/warehouse/dbt/models/intermediate/directory/int_projects_by_collection.sql similarity index 100% rename from dbt/models/intermediate/directory/int_projects_by_collection.sql rename to warehouse/dbt/models/intermediate/directory/int_projects_by_collection.sql diff --git a/dbt/models/intermediate/events/int_events.sql b/warehouse/dbt/models/intermediate/events/int_events.sql similarity index 100% rename from dbt/models/intermediate/events/int_events.sql rename to warehouse/dbt/models/intermediate/events/int_events.sql diff --git a/dbt/models/intermediate/events/int_events_from_collection.sql b/warehouse/dbt/models/intermediate/events/int_events_from_collection.sql similarity index 100% rename from dbt/models/intermediate/events/int_events_from_collection.sql rename to warehouse/dbt/models/intermediate/events/int_events_from_collection.sql diff --git a/dbt/models/intermediate/events/int_events_from_project.sql b/warehouse/dbt/models/intermediate/events/int_events_from_project.sql similarity index 100% rename from dbt/models/intermediate/events/int_events_from_project.sql rename to warehouse/dbt/models/intermediate/events/int_events_from_project.sql diff --git a/dbt/models/intermediate/events/int_events_to_collection.sql b/warehouse/dbt/models/intermediate/events/int_events_to_collection.sql similarity index 100% rename from dbt/models/intermediate/events/int_events_to_collection.sql rename to warehouse/dbt/models/intermediate/events/int_events_to_collection.sql diff --git a/dbt/models/intermediate/events/int_events_to_project.sql b/warehouse/dbt/models/intermediate/events/int_events_to_project.sql similarity index 100% rename from dbt/models/intermediate/events/int_events_to_project.sql rename to warehouse/dbt/models/intermediate/events/int_events_to_project.sql diff --git a/dbt/models/intermediate/events/int_events_with_artifact_id.sql b/warehouse/dbt/models/intermediate/events/int_events_with_artifact_id.sql similarity index 100% rename from dbt/models/intermediate/events/int_events_with_artifact_id.sql rename to warehouse/dbt/models/intermediate/events/int_events_with_artifact_id.sql diff --git a/dbt/models/marts/code_metrics/code_metrics_by_collection.sql b/warehouse/dbt/models/marts/code_metrics/code_metrics_by_collection.sql similarity index 100% rename from dbt/models/marts/code_metrics/code_metrics_by_collection.sql rename to warehouse/dbt/models/marts/code_metrics/code_metrics_by_collection.sql diff --git a/dbt/models/marts/code_metrics/code_metrics_by_project.sql b/warehouse/dbt/models/marts/code_metrics/code_metrics_by_project.sql similarity index 100% rename from dbt/models/marts/code_metrics/code_metrics_by_project.sql rename to warehouse/dbt/models/marts/code_metrics/code_metrics_by_project.sql diff --git a/dbt/models/marts/directory/artifacts.sql b/warehouse/dbt/models/marts/directory/artifacts.sql similarity index 100% rename from dbt/models/marts/directory/artifacts.sql rename to warehouse/dbt/models/marts/directory/artifacts.sql diff --git a/dbt/models/marts/directory/artifacts_by_project.sql b/warehouse/dbt/models/marts/directory/artifacts_by_project.sql similarity index 100% rename from dbt/models/marts/directory/artifacts_by_project.sql rename to warehouse/dbt/models/marts/directory/artifacts_by_project.sql diff --git a/dbt/models/marts/directory/collections.sql b/warehouse/dbt/models/marts/directory/collections.sql similarity index 100% rename from dbt/models/marts/directory/collections.sql rename to warehouse/dbt/models/marts/directory/collections.sql diff --git a/dbt/models/marts/directory/projects.sql b/warehouse/dbt/models/marts/directory/projects.sql similarity index 100% rename from dbt/models/marts/directory/projects.sql rename to warehouse/dbt/models/marts/directory/projects.sql diff --git a/dbt/models/marts/directory/projects_by_collection.sql b/warehouse/dbt/models/marts/directory/projects_by_collection.sql similarity index 100% rename from dbt/models/marts/directory/projects_by_collection.sql rename to warehouse/dbt/models/marts/directory/projects_by_collection.sql diff --git a/dbt/models/marts/events/daily/events_daily_from_artifact.sql b/warehouse/dbt/models/marts/events/daily/events_daily_from_artifact.sql similarity index 100% rename from dbt/models/marts/events/daily/events_daily_from_artifact.sql rename to warehouse/dbt/models/marts/events/daily/events_daily_from_artifact.sql diff --git a/dbt/models/marts/events/daily/events_daily_from_collection.sql b/warehouse/dbt/models/marts/events/daily/events_daily_from_collection.sql similarity index 100% rename from dbt/models/marts/events/daily/events_daily_from_collection.sql rename to warehouse/dbt/models/marts/events/daily/events_daily_from_collection.sql diff --git a/dbt/models/marts/events/daily/events_daily_from_project.sql b/warehouse/dbt/models/marts/events/daily/events_daily_from_project.sql similarity index 100% rename from dbt/models/marts/events/daily/events_daily_from_project.sql rename to warehouse/dbt/models/marts/events/daily/events_daily_from_project.sql diff --git a/dbt/models/marts/events/daily/events_daily_to_artifact.sql b/warehouse/dbt/models/marts/events/daily/events_daily_to_artifact.sql similarity index 100% rename from dbt/models/marts/events/daily/events_daily_to_artifact.sql rename to warehouse/dbt/models/marts/events/daily/events_daily_to_artifact.sql diff --git a/dbt/models/marts/events/daily/events_daily_to_collection.sql b/warehouse/dbt/models/marts/events/daily/events_daily_to_collection.sql similarity index 100% rename from dbt/models/marts/events/daily/events_daily_to_collection.sql rename to warehouse/dbt/models/marts/events/daily/events_daily_to_collection.sql diff --git a/dbt/models/marts/events/daily/events_daily_to_project.sql b/warehouse/dbt/models/marts/events/daily/events_daily_to_project.sql similarity index 100% rename from dbt/models/marts/events/daily/events_daily_to_project.sql rename to warehouse/dbt/models/marts/events/daily/events_daily_to_project.sql diff --git a/dbt/models/marts/events/event_types.sql b/warehouse/dbt/models/marts/events/event_types.sql similarity index 100% rename from dbt/models/marts/events/event_types.sql rename to warehouse/dbt/models/marts/events/event_types.sql diff --git a/dbt/models/marts/events/first_contribution_to_project.sql b/warehouse/dbt/models/marts/events/first_contribution_to_project.sql similarity index 100% rename from dbt/models/marts/events/first_contribution_to_project.sql rename to warehouse/dbt/models/marts/events/first_contribution_to_project.sql diff --git a/dbt/models/marts/events/last_contribution_to_project.sql b/warehouse/dbt/models/marts/events/last_contribution_to_project.sql similarity index 100% rename from dbt/models/marts/events/last_contribution_to_project.sql rename to warehouse/dbt/models/marts/events/last_contribution_to_project.sql diff --git a/dbt/models/marts/events/monthly/events_monthly_from_artifact.sql b/warehouse/dbt/models/marts/events/monthly/events_monthly_from_artifact.sql similarity index 100% rename from dbt/models/marts/events/monthly/events_monthly_from_artifact.sql rename to warehouse/dbt/models/marts/events/monthly/events_monthly_from_artifact.sql diff --git a/dbt/models/marts/events/monthly/events_monthly_from_collection.sql b/warehouse/dbt/models/marts/events/monthly/events_monthly_from_collection.sql similarity index 100% rename from dbt/models/marts/events/monthly/events_monthly_from_collection.sql rename to warehouse/dbt/models/marts/events/monthly/events_monthly_from_collection.sql diff --git a/dbt/models/marts/events/monthly/events_monthly_from_project.sql b/warehouse/dbt/models/marts/events/monthly/events_monthly_from_project.sql similarity index 100% rename from dbt/models/marts/events/monthly/events_monthly_from_project.sql rename to warehouse/dbt/models/marts/events/monthly/events_monthly_from_project.sql diff --git a/dbt/models/marts/events/monthly/events_monthly_to_artifact.sql b/warehouse/dbt/models/marts/events/monthly/events_monthly_to_artifact.sql similarity index 100% rename from dbt/models/marts/events/monthly/events_monthly_to_artifact.sql rename to warehouse/dbt/models/marts/events/monthly/events_monthly_to_artifact.sql diff --git a/dbt/models/marts/events/monthly/events_monthly_to_collection.sql b/warehouse/dbt/models/marts/events/monthly/events_monthly_to_collection.sql similarity index 100% rename from dbt/models/marts/events/monthly/events_monthly_to_collection.sql rename to warehouse/dbt/models/marts/events/monthly/events_monthly_to_collection.sql diff --git a/dbt/models/marts/events/monthly/events_monthly_to_project.sql b/warehouse/dbt/models/marts/events/monthly/events_monthly_to_project.sql similarity index 100% rename from dbt/models/marts/events/monthly/events_monthly_to_project.sql rename to warehouse/dbt/models/marts/events/monthly/events_monthly_to_project.sql diff --git a/dbt/models/marts/events/users_monthly_to_project.sql b/warehouse/dbt/models/marts/events/users_monthly_to_project.sql similarity index 100% rename from dbt/models/marts/events/users_monthly_to_project.sql rename to warehouse/dbt/models/marts/events/users_monthly_to_project.sql diff --git a/dbt/models/marts/events/weekly/events_weekly_from_artifact.sql b/warehouse/dbt/models/marts/events/weekly/events_weekly_from_artifact.sql similarity index 100% rename from dbt/models/marts/events/weekly/events_weekly_from_artifact.sql rename to warehouse/dbt/models/marts/events/weekly/events_weekly_from_artifact.sql diff --git a/dbt/models/marts/events/weekly/events_weekly_from_collection.sql b/warehouse/dbt/models/marts/events/weekly/events_weekly_from_collection.sql similarity index 100% rename from dbt/models/marts/events/weekly/events_weekly_from_collection.sql rename to warehouse/dbt/models/marts/events/weekly/events_weekly_from_collection.sql diff --git a/dbt/models/marts/events/weekly/events_weekly_from_project.sql b/warehouse/dbt/models/marts/events/weekly/events_weekly_from_project.sql similarity index 100% rename from dbt/models/marts/events/weekly/events_weekly_from_project.sql rename to warehouse/dbt/models/marts/events/weekly/events_weekly_from_project.sql diff --git a/dbt/models/marts/events/weekly/events_weekly_to_artifact.sql b/warehouse/dbt/models/marts/events/weekly/events_weekly_to_artifact.sql similarity index 100% rename from dbt/models/marts/events/weekly/events_weekly_to_artifact.sql rename to warehouse/dbt/models/marts/events/weekly/events_weekly_to_artifact.sql diff --git a/dbt/models/marts/events/weekly/events_weekly_to_collection.sql b/warehouse/dbt/models/marts/events/weekly/events_weekly_to_collection.sql similarity index 100% rename from dbt/models/marts/events/weekly/events_weekly_to_collection.sql rename to warehouse/dbt/models/marts/events/weekly/events_weekly_to_collection.sql diff --git a/dbt/models/marts/events/weekly/events_weekly_to_project.sql b/warehouse/dbt/models/marts/events/weekly/events_weekly_to_project.sql similarity index 100% rename from dbt/models/marts/events/weekly/events_weekly_to_project.sql rename to warehouse/dbt/models/marts/events/weekly/events_weekly_to_project.sql diff --git a/dbt/models/marts/onchain_metrics/onchain_metrics_by_collection.sql b/warehouse/dbt/models/marts/onchain_metrics/onchain_metrics_by_collection.sql similarity index 100% rename from dbt/models/marts/onchain_metrics/onchain_metrics_by_collection.sql rename to warehouse/dbt/models/marts/onchain_metrics/onchain_metrics_by_collection.sql diff --git a/dbt/models/marts/onchain_metrics/onchain_metrics_by_project.sql b/warehouse/dbt/models/marts/onchain_metrics/onchain_metrics_by_project.sql similarity index 100% rename from dbt/models/marts/onchain_metrics/onchain_metrics_by_project.sql rename to warehouse/dbt/models/marts/onchain_metrics/onchain_metrics_by_project.sql diff --git a/dbt/models/ossd_source.yml b/warehouse/dbt/models/ossd_source.yml similarity index 100% rename from dbt/models/ossd_source.yml rename to warehouse/dbt/models/ossd_source.yml diff --git a/dbt/models/playground/README.md b/warehouse/dbt/models/playground/README.md similarity index 100% rename from dbt/models/playground/README.md rename to warehouse/dbt/models/playground/README.md diff --git a/dbt/models/playground/dune__arbitrum_contract_usage.sql b/warehouse/dbt/models/playground/dune__arbitrum_contract_usage.sql similarity index 100% rename from dbt/models/playground/dune__arbitrum_contract_usage.sql rename to warehouse/dbt/models/playground/dune__arbitrum_contract_usage.sql diff --git a/dbt/models/playground/dune__optimism_contract_usage.sql b/warehouse/dbt/models/playground/dune__optimism_contract_usage.sql similarity index 100% rename from dbt/models/playground/dune__optimism_contract_usage.sql rename to warehouse/dbt/models/playground/dune__optimism_contract_usage.sql diff --git a/dbt/models/playground/ossd__collections.sql b/warehouse/dbt/models/playground/ossd__collections.sql similarity index 100% rename from dbt/models/playground/ossd__collections.sql rename to warehouse/dbt/models/playground/ossd__collections.sql diff --git a/dbt/models/playground/ossd__projects.sql b/warehouse/dbt/models/playground/ossd__projects.sql similarity index 100% rename from dbt/models/playground/ossd__projects.sql rename to warehouse/dbt/models/playground/ossd__projects.sql diff --git a/dbt/models/playground/ossd__repositories.sql b/warehouse/dbt/models/playground/ossd__repositories.sql similarity index 100% rename from dbt/models/playground/ossd__repositories.sql rename to warehouse/dbt/models/playground/ossd__repositories.sql diff --git a/dbt/models/staging/dune/stg_dune__arbitrum_contract_invocation.sql b/warehouse/dbt/models/staging/dune/stg_dune__arbitrum_contract_invocation.sql similarity index 100% rename from dbt/models/staging/dune/stg_dune__arbitrum_contract_invocation.sql rename to warehouse/dbt/models/staging/dune/stg_dune__arbitrum_contract_invocation.sql diff --git a/dbt/models/staging/dune/stg_dune__contract_invocation.sql b/warehouse/dbt/models/staging/dune/stg_dune__contract_invocation.sql similarity index 100% rename from dbt/models/staging/dune/stg_dune__contract_invocation.sql rename to warehouse/dbt/models/staging/dune/stg_dune__contract_invocation.sql diff --git a/dbt/models/staging/dune/stg_dune__optimism_contract_invocation.sql b/warehouse/dbt/models/staging/dune/stg_dune__optimism_contract_invocation.sql similarity index 100% rename from dbt/models/staging/dune/stg_dune__optimism_contract_invocation.sql rename to warehouse/dbt/models/staging/dune/stg_dune__optimism_contract_invocation.sql diff --git a/dbt/models/staging/dune/stg_dune__schema.yml b/warehouse/dbt/models/staging/dune/stg_dune__schema.yml similarity index 100% rename from dbt/models/staging/dune/stg_dune__schema.yml rename to warehouse/dbt/models/staging/dune/stg_dune__schema.yml diff --git a/dbt/models/staging/github/stg_github__commits.sql b/warehouse/dbt/models/staging/github/stg_github__commits.sql similarity index 100% rename from dbt/models/staging/github/stg_github__commits.sql rename to warehouse/dbt/models/staging/github/stg_github__commits.sql diff --git a/dbt/models/staging/github/stg_github__distinct_commits_resolved_mergebot.sql b/warehouse/dbt/models/staging/github/stg_github__distinct_commits_resolved_mergebot.sql similarity index 100% rename from dbt/models/staging/github/stg_github__distinct_commits_resolved_mergebot.sql rename to warehouse/dbt/models/staging/github/stg_github__distinct_commits_resolved_mergebot.sql diff --git a/dbt/models/staging/github/stg_github__distinct_main_commits.sql b/warehouse/dbt/models/staging/github/stg_github__distinct_main_commits.sql similarity index 100% rename from dbt/models/staging/github/stg_github__distinct_main_commits.sql rename to warehouse/dbt/models/staging/github/stg_github__distinct_main_commits.sql diff --git a/dbt/models/staging/github/stg_github__events.sql b/warehouse/dbt/models/staging/github/stg_github__events.sql similarity index 100% rename from dbt/models/staging/github/stg_github__events.sql rename to warehouse/dbt/models/staging/github/stg_github__events.sql diff --git a/dbt/models/staging/github/stg_github__issues.sql b/warehouse/dbt/models/staging/github/stg_github__issues.sql similarity index 100% rename from dbt/models/staging/github/stg_github__issues.sql rename to warehouse/dbt/models/staging/github/stg_github__issues.sql diff --git a/dbt/models/staging/github/stg_github__pull_request_merge_events.sql b/warehouse/dbt/models/staging/github/stg_github__pull_request_merge_events.sql similarity index 100% rename from dbt/models/staging/github/stg_github__pull_request_merge_events.sql rename to warehouse/dbt/models/staging/github/stg_github__pull_request_merge_events.sql diff --git a/dbt/models/staging/github/stg_github__pull_requests.sql b/warehouse/dbt/models/staging/github/stg_github__pull_requests.sql similarity index 100% rename from dbt/models/staging/github/stg_github__pull_requests.sql rename to warehouse/dbt/models/staging/github/stg_github__pull_requests.sql diff --git a/dbt/models/staging/github/stg_github__push_events.sql b/warehouse/dbt/models/staging/github/stg_github__push_events.sql similarity index 100% rename from dbt/models/staging/github/stg_github__push_events.sql rename to warehouse/dbt/models/staging/github/stg_github__push_events.sql diff --git a/dbt/models/staging/github/stg_github__schema.yml b/warehouse/dbt/models/staging/github/stg_github__schema.yml similarity index 100% rename from dbt/models/staging/github/stg_github__schema.yml rename to warehouse/dbt/models/staging/github/stg_github__schema.yml diff --git a/dbt/models/staging/github/stg_github__stars_and_forks.sql b/warehouse/dbt/models/staging/github/stg_github__stars_and_forks.sql similarity index 100% rename from dbt/models/staging/github/stg_github__stars_and_forks.sql rename to warehouse/dbt/models/staging/github/stg_github__stars_and_forks.sql diff --git a/dbt/models/staging/oss-directory/stg_ossd__artifacts_by_project.sql b/warehouse/dbt/models/staging/oss-directory/stg_ossd__artifacts_by_project.sql similarity index 100% rename from dbt/models/staging/oss-directory/stg_ossd__artifacts_by_project.sql rename to warehouse/dbt/models/staging/oss-directory/stg_ossd__artifacts_by_project.sql diff --git a/dbt/models/staging/oss-directory/stg_ossd__current_collections.sql b/warehouse/dbt/models/staging/oss-directory/stg_ossd__current_collections.sql similarity index 100% rename from dbt/models/staging/oss-directory/stg_ossd__current_collections.sql rename to warehouse/dbt/models/staging/oss-directory/stg_ossd__current_collections.sql diff --git a/dbt/models/staging/oss-directory/stg_ossd__current_projects.sql b/warehouse/dbt/models/staging/oss-directory/stg_ossd__current_projects.sql similarity index 100% rename from dbt/models/staging/oss-directory/stg_ossd__current_projects.sql rename to warehouse/dbt/models/staging/oss-directory/stg_ossd__current_projects.sql diff --git a/dbt/models/staging/oss-directory/stg_ossd__current_repositories.sql b/warehouse/dbt/models/staging/oss-directory/stg_ossd__current_repositories.sql similarity index 100% rename from dbt/models/staging/oss-directory/stg_ossd__current_repositories.sql rename to warehouse/dbt/models/staging/oss-directory/stg_ossd__current_repositories.sql diff --git a/dbt/models/staging/oss-directory/stg_ossd__projects_by_collection.sql b/warehouse/dbt/models/staging/oss-directory/stg_ossd__projects_by_collection.sql similarity index 100% rename from dbt/models/staging/oss-directory/stg_ossd__projects_by_collection.sql rename to warehouse/dbt/models/staging/oss-directory/stg_ossd__projects_by_collection.sql diff --git a/dbt/models/staging/oss-directory/stg_ossd__repositories_by_project.sql b/warehouse/dbt/models/staging/oss-directory/stg_ossd__repositories_by_project.sql similarity index 100% rename from dbt/models/staging/oss-directory/stg_ossd__repositories_by_project.sql rename to warehouse/dbt/models/staging/oss-directory/stg_ossd__repositories_by_project.sql diff --git a/dbt/models/staging/oss-directory/stg_ossd__schema.yml b/warehouse/dbt/models/staging/oss-directory/stg_ossd__schema.yml similarity index 100% rename from dbt/models/staging/oss-directory/stg_ossd__schema.yml rename to warehouse/dbt/models/staging/oss-directory/stg_ossd__schema.yml diff --git a/dbt/seeds/.gitkeep b/warehouse/dbt/seeds/.gitkeep similarity index 100% rename from dbt/seeds/.gitkeep rename to warehouse/dbt/seeds/.gitkeep diff --git a/dbt/snapshots/.gitkeep b/warehouse/dbt/snapshots/.gitkeep similarity index 100% rename from dbt/snapshots/.gitkeep rename to warehouse/dbt/snapshots/.gitkeep diff --git a/dbt/tests/.gitkeep b/warehouse/dbt/tests/.gitkeep similarity index 100% rename from dbt/tests/.gitkeep rename to warehouse/dbt/tests/.gitkeep