From b0625ef9e8353b3ecdc4a729c1adbe38a89ed644 Mon Sep 17 00:00:00 2001 From: 0xaptosj <129789810+0xaptosj@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:48:10 -0700 Subject: [PATCH] copy over indexer template --- templates/indexer-template/.gitignore | 3 + templates/indexer-template/LICENSE | 201 + templates/indexer-template/README.md | 10 + .../indexer-template/contracts/.gitignore | 3 + .../indexer-template/contracts/README.MD | 3 + .../contracts/message-board/Move.toml | 15 + .../contracts/message-board/README.MD | 15 + .../message-board/contract_address.txt | 1 + .../scripts/create_2_messages.move | 13 + .../message-board/scripts/update_message.move | 32 + .../message-board/sh_scripts/deploy.sh | 22 + .../message-board/sh_scripts/get_abis.sh | 17 + .../message-board/sh_scripts/init.sh | 9 + .../run_create_2_messages_script.sh | 21 + .../sh_scripts/run_update_message_script.sh | 21 + .../message-board/sh_scripts/test.sh | 8 + .../message-board/sh_scripts/upgrade.sh | 17 + .../message-board/sources/message_board.move | 100 + .../message-board/tests/test_end_to_end.move | 46 + .../indexer/.cargo/config.toml | 32 + templates/indexer-template/indexer/.gitignore | 22 + templates/indexer-template/indexer/Cargo.lock | 3894 +++++++++++++++++ templates/indexer-template/indexer/Cargo.toml | 57 + templates/indexer-template/indexer/Dockerfile | 48 + templates/indexer-template/indexer/README.md | 136 + .../indexer/example.config.yaml | 20 + .../src/config/indexer_processor_config.rs | 62 + .../indexer/src/config/mod.rs | 2 + .../indexer/src/config/processor_config.rs | 74 + .../indexer/src/db_migrations/diesel.toml | 8 + .../down.sql | 6 + .../up.sql | 36 + .../down.sql | 2 + .../up.sql | 8 + .../2024-07-18-202400_test-migration/down.sql | 3 + .../2024-07-18-202400_test-migration/up.sql | 3 + .../2024-08-01-224558_ledger-infos/down.sql | 2 + .../2024-08-01-224558_ledger-infos/up.sql | 2 + .../down.sql | 2 + .../up.sql | 11 + .../indexer/src/db_migrations/schema.rs | 36 + .../indexer/src/db_models/events_models.rs | 134 + .../indexer/src/db_models/ledger_info.rs | 22 + .../indexer/src/db_models/mod.rs | 3 + .../indexer/src/db_models/processor_status.rs | 37 + .../indexer/src/health_check_server.rs | 42 + templates/indexer-template/indexer/src/lib.rs | 8 + .../indexer-template/indexer/src/main.rs | 38 + .../src/processors/events/events_extractor.rs | 85 + .../src/processors/events/events_processor.rs | 99 + .../src/processors/events/events_storer.rs | 80 + .../indexer/src/processors/events/mod.rs | 4 + .../storers/create_message_event_storer.rs | 48 + .../src/processors/events/storers/mod.rs | 2 + .../storers/update_message_event_storer.rs | 92 + .../indexer/src/processors/mod.rs | 1 + .../indexer/src/utils/chain_id.rs | 49 + .../indexer/src/utils/database_connection.rs | 77 + .../indexer/src/utils/database_execution.rs | 106 + .../indexer/src/utils/database_utils.rs | 42 + .../utils/latest_processed_version_tracker.rs | 199 + .../indexer-template/indexer/src/utils/mod.rs | 6 + .../indexer/src/utils/starting_version.rs | 43 + .../indexer-template/next-app/.eslintrc.json | 3 + .../indexer-template/next-app/.example.env | 1 + .../indexer-template/next-app/.gitignore | 37 + templates/indexer-template/next-app/README.md | 67 + .../indexer-template/next-app/components.json | 20 + .../indexer-template/next-app/next.config.mjs | 4 + .../indexer-template/next-app/package.json | 54 + .../next-app/postcss.config.mjs | 8 + .../indexer-template/next-app/public/next.svg | 1 + .../next-app/public/vercel.svg | 1 + .../next-app/src/app/actions.ts | 30 + .../next-app/src/app/favicon.ico | Bin 0 -> 25931 bytes .../next-app/src/app/globals.css | 92 + .../next-app/src/app/layout.tsx | 56 + .../src/app/message/[messageObjAddr]/page.tsx | 11 + .../next-app/src/app/page.tsx | 11 + .../next-app/src/components/ExplorerLink.tsx | 40 + .../next-app/src/components/IndexerStatus.tsx | 78 + .../src/components/LabelValueGrid.tsx | 50 + .../next-app/src/components/Message.tsx | 96 + .../next-app/src/components/MessageBoard.tsx | 16 + .../src/components/PostMessageWithSurf.tsx | 121 + .../next-app/src/components/RootHeader.tsx | 28 + .../src/components/SendTransaction.tsx | 10 + .../next-app/src/components/ThemeToggle.tsx | 40 + .../src/components/WrongNetworkAlert.tsx | 33 + .../src/components/message-board/columns.tsx | 41 + .../data-table-column-header.tsx | 68 + .../message-board/data-table-pagination.tsx | 98 + .../message-board/data-table-row-actions.tsx | 46 + .../components/message-board/data-table.tsx | 155 + .../components/providers/QueryProvider.tsx | 12 + .../components/providers/ThemeProvider.tsx | 9 + .../components/providers/WalletProvider.tsx | 32 + .../next-app/src/components/ui/alert.tsx | 61 + .../next-app/src/components/ui/button.tsx | 56 + .../next-app/src/components/ui/card.tsx | 86 + .../next-app/src/components/ui/checkbox.tsx | 30 + .../src/components/ui/collapsible.tsx | 11 + .../next-app/src/components/ui/dialog.tsx | 122 + .../src/components/ui/dropdown-menu.tsx | 200 + .../next-app/src/components/ui/form.tsx | 177 + .../next-app/src/components/ui/input.tsx | 23 + .../next-app/src/components/ui/label.tsx | 26 + .../src/components/ui/radio-group.tsx | 44 + .../next-app/src/components/ui/select.tsx | 164 + .../next-app/src/components/ui/switch.tsx | 29 + .../next-app/src/components/ui/table.tsx | 117 + .../next-app/src/components/ui/toast.tsx | 129 + .../next-app/src/components/ui/toaster.tsx | 35 + .../next-app/src/components/ui/tooltip.tsx | 30 + .../next-app/src/components/ui/use-toast.ts | 191 + .../components/wallet/WalletConnection.tsx | 158 + .../src/components/wallet/WalletSelector.tsx | 219 + .../next-app/src/db/getLastSuccessVersion.ts | 14 + .../next-app/src/db/getMessage.ts | 28 + .../next-app/src/db/getMessages.ts | 35 + .../next-app/src/lib/abi/message_board_abi.ts | 80 + .../next-app/src/lib/aptos.ts | 18 + .../next-app/src/lib/type/indexer_status.ts | 6 + .../next-app/src/lib/type/message.ts | 21 + .../next-app/src/lib/utils.ts | 6 + .../next-app/tailwind.config.ts | 98 + .../indexer-template/next-app/tsconfig.json | 26 + .../node-scripts/.example.env | 1 + .../indexer-template/node-scripts/.gitignore | 37 + .../indexer-template/node-scripts/README.MD | 5 + .../node-scripts/package.json | 22 + .../src/lib/abi/message_board_abi.ts | 80 + .../node-scripts/src/lib/utils.ts | 51 + .../node-scripts/src/script/create_message.ts | 13 + .../src/script/get_last_message_from_db.ts | 18 + .../node-scripts/src/script/get_message.ts | 14 + .../node-scripts/src/script/update_message.ts | 16 + .../node-scripts/tsconfig.json | 17 + 138 files changed, 10123 insertions(+) create mode 100644 templates/indexer-template/.gitignore create mode 100644 templates/indexer-template/LICENSE create mode 100644 templates/indexer-template/README.md create mode 100644 templates/indexer-template/contracts/.gitignore create mode 100644 templates/indexer-template/contracts/README.MD create mode 100644 templates/indexer-template/contracts/message-board/Move.toml create mode 100644 templates/indexer-template/contracts/message-board/README.MD create mode 100644 templates/indexer-template/contracts/message-board/contract_address.txt create mode 100644 templates/indexer-template/contracts/message-board/scripts/create_2_messages.move create mode 100644 templates/indexer-template/contracts/message-board/scripts/update_message.move create mode 100755 templates/indexer-template/contracts/message-board/sh_scripts/deploy.sh create mode 100755 templates/indexer-template/contracts/message-board/sh_scripts/get_abis.sh create mode 100755 templates/indexer-template/contracts/message-board/sh_scripts/init.sh create mode 100755 templates/indexer-template/contracts/message-board/sh_scripts/run_create_2_messages_script.sh create mode 100755 templates/indexer-template/contracts/message-board/sh_scripts/run_update_message_script.sh create mode 100755 templates/indexer-template/contracts/message-board/sh_scripts/test.sh create mode 100755 templates/indexer-template/contracts/message-board/sh_scripts/upgrade.sh create mode 100644 templates/indexer-template/contracts/message-board/sources/message_board.move create mode 100644 templates/indexer-template/contracts/message-board/tests/test_end_to_end.move create mode 100644 templates/indexer-template/indexer/.cargo/config.toml create mode 100644 templates/indexer-template/indexer/.gitignore create mode 100644 templates/indexer-template/indexer/Cargo.lock create mode 100644 templates/indexer-template/indexer/Cargo.toml create mode 100644 templates/indexer-template/indexer/Dockerfile create mode 100644 templates/indexer-template/indexer/README.md create mode 100644 templates/indexer-template/indexer/example.config.yaml create mode 100644 templates/indexer-template/indexer/src/config/indexer_processor_config.rs create mode 100644 templates/indexer-template/indexer/src/config/mod.rs create mode 100644 templates/indexer-template/indexer/src/config/processor_config.rs create mode 100644 templates/indexer-template/indexer/src/db_migrations/diesel.toml create mode 100644 templates/indexer-template/indexer/src/db_migrations/migrations/00000000000000_diesel_initial_setup/down.sql create mode 100644 templates/indexer-template/indexer/src/db_migrations/migrations/00000000000000_diesel_initial_setup/up.sql create mode 100644 templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-194547_create-processor-status/down.sql create mode 100644 templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-194547_create-processor-status/up.sql create mode 100644 templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-202400_test-migration/down.sql create mode 100644 templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-202400_test-migration/up.sql create mode 100644 templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-01-224558_ledger-infos/down.sql create mode 100644 templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-01-224558_ledger-infos/up.sql create mode 100644 templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-25-233538_create-message-table/down.sql create mode 100644 templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-25-233538_create-message-table/up.sql create mode 100644 templates/indexer-template/indexer/src/db_migrations/schema.rs create mode 100644 templates/indexer-template/indexer/src/db_models/events_models.rs create mode 100644 templates/indexer-template/indexer/src/db_models/ledger_info.rs create mode 100644 templates/indexer-template/indexer/src/db_models/mod.rs create mode 100644 templates/indexer-template/indexer/src/db_models/processor_status.rs create mode 100644 templates/indexer-template/indexer/src/health_check_server.rs create mode 100644 templates/indexer-template/indexer/src/lib.rs create mode 100644 templates/indexer-template/indexer/src/main.rs create mode 100644 templates/indexer-template/indexer/src/processors/events/events_extractor.rs create mode 100644 templates/indexer-template/indexer/src/processors/events/events_processor.rs create mode 100644 templates/indexer-template/indexer/src/processors/events/events_storer.rs create mode 100644 templates/indexer-template/indexer/src/processors/events/mod.rs create mode 100644 templates/indexer-template/indexer/src/processors/events/storers/create_message_event_storer.rs create mode 100644 templates/indexer-template/indexer/src/processors/events/storers/mod.rs create mode 100644 templates/indexer-template/indexer/src/processors/events/storers/update_message_event_storer.rs create mode 100644 templates/indexer-template/indexer/src/processors/mod.rs create mode 100644 templates/indexer-template/indexer/src/utils/chain_id.rs create mode 100644 templates/indexer-template/indexer/src/utils/database_connection.rs create mode 100644 templates/indexer-template/indexer/src/utils/database_execution.rs create mode 100644 templates/indexer-template/indexer/src/utils/database_utils.rs create mode 100644 templates/indexer-template/indexer/src/utils/latest_processed_version_tracker.rs create mode 100644 templates/indexer-template/indexer/src/utils/mod.rs create mode 100644 templates/indexer-template/indexer/src/utils/starting_version.rs create mode 100644 templates/indexer-template/next-app/.eslintrc.json create mode 100644 templates/indexer-template/next-app/.example.env create mode 100644 templates/indexer-template/next-app/.gitignore create mode 100644 templates/indexer-template/next-app/README.md create mode 100644 templates/indexer-template/next-app/components.json create mode 100644 templates/indexer-template/next-app/next.config.mjs create mode 100644 templates/indexer-template/next-app/package.json create mode 100644 templates/indexer-template/next-app/postcss.config.mjs create mode 100644 templates/indexer-template/next-app/public/next.svg create mode 100644 templates/indexer-template/next-app/public/vercel.svg create mode 100644 templates/indexer-template/next-app/src/app/actions.ts create mode 100644 templates/indexer-template/next-app/src/app/favicon.ico create mode 100644 templates/indexer-template/next-app/src/app/globals.css create mode 100644 templates/indexer-template/next-app/src/app/layout.tsx create mode 100644 templates/indexer-template/next-app/src/app/message/[messageObjAddr]/page.tsx create mode 100644 templates/indexer-template/next-app/src/app/page.tsx create mode 100644 templates/indexer-template/next-app/src/components/ExplorerLink.tsx create mode 100644 templates/indexer-template/next-app/src/components/IndexerStatus.tsx create mode 100644 templates/indexer-template/next-app/src/components/LabelValueGrid.tsx create mode 100644 templates/indexer-template/next-app/src/components/Message.tsx create mode 100644 templates/indexer-template/next-app/src/components/MessageBoard.tsx create mode 100644 templates/indexer-template/next-app/src/components/PostMessageWithSurf.tsx create mode 100644 templates/indexer-template/next-app/src/components/RootHeader.tsx create mode 100644 templates/indexer-template/next-app/src/components/SendTransaction.tsx create mode 100644 templates/indexer-template/next-app/src/components/ThemeToggle.tsx create mode 100644 templates/indexer-template/next-app/src/components/WrongNetworkAlert.tsx create mode 100644 templates/indexer-template/next-app/src/components/message-board/columns.tsx create mode 100644 templates/indexer-template/next-app/src/components/message-board/data-table-column-header.tsx create mode 100644 templates/indexer-template/next-app/src/components/message-board/data-table-pagination.tsx create mode 100644 templates/indexer-template/next-app/src/components/message-board/data-table-row-actions.tsx create mode 100644 templates/indexer-template/next-app/src/components/message-board/data-table.tsx create mode 100644 templates/indexer-template/next-app/src/components/providers/QueryProvider.tsx create mode 100644 templates/indexer-template/next-app/src/components/providers/ThemeProvider.tsx create mode 100644 templates/indexer-template/next-app/src/components/providers/WalletProvider.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/alert.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/button.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/card.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/checkbox.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/collapsible.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/dialog.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/dropdown-menu.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/form.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/input.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/label.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/radio-group.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/select.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/switch.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/table.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/toast.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/toaster.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/tooltip.tsx create mode 100644 templates/indexer-template/next-app/src/components/ui/use-toast.ts create mode 100644 templates/indexer-template/next-app/src/components/wallet/WalletConnection.tsx create mode 100644 templates/indexer-template/next-app/src/components/wallet/WalletSelector.tsx create mode 100644 templates/indexer-template/next-app/src/db/getLastSuccessVersion.ts create mode 100644 templates/indexer-template/next-app/src/db/getMessage.ts create mode 100644 templates/indexer-template/next-app/src/db/getMessages.ts create mode 100644 templates/indexer-template/next-app/src/lib/abi/message_board_abi.ts create mode 100644 templates/indexer-template/next-app/src/lib/aptos.ts create mode 100644 templates/indexer-template/next-app/src/lib/type/indexer_status.ts create mode 100644 templates/indexer-template/next-app/src/lib/type/message.ts create mode 100644 templates/indexer-template/next-app/src/lib/utils.ts create mode 100644 templates/indexer-template/next-app/tailwind.config.ts create mode 100644 templates/indexer-template/next-app/tsconfig.json create mode 100644 templates/indexer-template/node-scripts/.example.env create mode 100644 templates/indexer-template/node-scripts/.gitignore create mode 100644 templates/indexer-template/node-scripts/README.MD create mode 100644 templates/indexer-template/node-scripts/package.json create mode 100644 templates/indexer-template/node-scripts/src/lib/abi/message_board_abi.ts create mode 100644 templates/indexer-template/node-scripts/src/lib/utils.ts create mode 100644 templates/indexer-template/node-scripts/src/script/create_message.ts create mode 100644 templates/indexer-template/node-scripts/src/script/get_last_message_from_db.ts create mode 100644 templates/indexer-template/node-scripts/src/script/get_message.ts create mode 100644 templates/indexer-template/node-scripts/src/script/update_message.ts create mode 100644 templates/indexer-template/node-scripts/tsconfig.json diff --git a/templates/indexer-template/.gitignore b/templates/indexer-template/.gitignore new file mode 100644 index 00000000..4a3cce43 --- /dev/null +++ b/templates/indexer-template/.gitignore @@ -0,0 +1,3 @@ +.idea +.DS_Store +*.pem diff --git a/templates/indexer-template/LICENSE b/templates/indexer-template/LICENSE new file mode 100644 index 00000000..a123aa87 --- /dev/null +++ b/templates/indexer-template/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Copyright 2023 Aptos Foundation + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/templates/indexer-template/README.md b/templates/indexer-template/README.md new file mode 100644 index 00000000..b9c89409 --- /dev/null +++ b/templates/indexer-template/README.md @@ -0,0 +1,10 @@ +# A template to build a full stack app on Aptos + +Please read each directory's readme carefully to understand how to use the template. + +This template is an alternative template to [CAD (create-aptos-dapp)](https://aptos.dev/en/build/create-aptos-dapp). Some notable differences are: + +- This template uses more experimental features, such as [indexer-sdk](https://github.com/aptos-labs/aptos-indexer-processor-sdk). +- This template uses Next.js instead of Vite. +- This templates doesn't support Windows. +- CAD provides more production ready templates that have been audited like token minting, NFT minting, etc. diff --git a/templates/indexer-template/contracts/.gitignore b/templates/indexer-template/contracts/.gitignore new file mode 100644 index 00000000..0f577f58 --- /dev/null +++ b/templates/indexer-template/contracts/.gitignore @@ -0,0 +1,3 @@ +# move +.aptos +build diff --git a/templates/indexer-template/contracts/README.MD b/templates/indexer-template/contracts/README.MD new file mode 100644 index 00000000..0fa41d47 --- /dev/null +++ b/templates/indexer-template/contracts/README.MD @@ -0,0 +1,3 @@ +# Contracts + +This directory contains all contracts used by the app. If you want to add a new contract, just create a new folder with the contract name and a `Move.toml` file inside it. diff --git a/templates/indexer-template/contracts/message-board/Move.toml b/templates/indexer-template/contracts/message-board/Move.toml new file mode 100644 index 00000000..7cfb8b81 --- /dev/null +++ b/templates/indexer-template/contracts/message-board/Move.toml @@ -0,0 +1,15 @@ +[package] +name = "message-board" +version = "1.0.0" +authors = [] + +[addresses] +message_board_addr = "_" + +[dev-addresses] +message_board_addr = "0x999" + +[dependencies] +AptosFramework = { git = "https://github.com/aptos-labs/aptos-core.git", rev = "mainnet", subdir = "aptos-move/framework/aptos-framework" } + +[dev-dependencies] diff --git a/templates/indexer-template/contracts/message-board/README.MD b/templates/indexer-template/contracts/message-board/README.MD new file mode 100644 index 00000000..094c61ae --- /dev/null +++ b/templates/indexer-template/contracts/message-board/README.MD @@ -0,0 +1,15 @@ +# Message board contract + +This is a simple message board contract that allows users to post messages on-chain and read messages from the chain. For both `create` and `update` endpoints, contract emits Create event and Update event respectively, then indexer will parse these events and store the message in the database for frontend to query efficiently. + +- `scripts` directory contains Move scripts that can batch multiple contract calls in 1 transaction. +- `tests` directory contains Move unit tests. +- `sources` directory contains the main contract code. +- `sh_scripts` directory contains shell scripts that can be used to test, publish, upgrade the contract, run Move scripts and generate TypeScript ABI. You can run them in this order: + - `./sh_scripts/init.sh`: create a new wallet + - `./sh_scripts/test.sh`: test the contract + - `./sh_scripts/deploy.sh`: deploy the contract + - `./sh_scripts/upgrade.sh`: upgrade the contract, you can only run this when you make compatible changes, changing existing structs and function signatures are considered incompatible changes. For incompatible changes, you need to deploy a new contract and migrate the data to new contract manually. + - `./sh_scripts/get_abis.sh`: generate TypeScript ABI in the frontend directory and node scripts directory. + - `./sh_scripts/run_create_2_messages_script.sh`: run Move script to create 2 messages in 1 transaction. + - `./sh_scripts/run_update_message_script.sh`: run Move script to update a messages in 1 transaction. diff --git a/templates/indexer-template/contracts/message-board/contract_address.txt b/templates/indexer-template/contracts/message-board/contract_address.txt new file mode 100644 index 00000000..a0228e19 --- /dev/null +++ b/templates/indexer-template/contracts/message-board/contract_address.txt @@ -0,0 +1 @@ +0xda6c2a8c4eae7b4fb7ef1b3319cc201492cc04e49d17b57dd75456e9b85e84b4 diff --git a/templates/indexer-template/contracts/message-board/scripts/create_2_messages.move b/templates/indexer-template/contracts/message-board/scripts/create_2_messages.move new file mode 100644 index 00000000..37898faa --- /dev/null +++ b/templates/indexer-template/contracts/message-board/scripts/create_2_messages.move @@ -0,0 +1,13 @@ +script { + use std::string; + + use message_board_addr::message_board; + + // This Move script runs atomically, i.e. it creates 2 messages in the same transaction. + // Move script is how we batch multiple function calls in 1 tx + // Similar to Solana allows multiple instructions in 1 tx + fun create_2_messages(sender: &signer) { + message_board::create_message(sender, string::utf8(b"hello hhohohoho")); + message_board::create_message(sender, string::utf8(b"hello yeyeeee")); + } +} diff --git a/templates/indexer-template/contracts/message-board/scripts/update_message.move b/templates/indexer-template/contracts/message-board/scripts/update_message.move new file mode 100644 index 00000000..3525a9f1 --- /dev/null +++ b/templates/indexer-template/contracts/message-board/scripts/update_message.move @@ -0,0 +1,32 @@ +script { + use std::string; + + use aptos_framework::object; + + use message_board_addr::message_board; + + // This Move script runs atomically + fun update_message(sender: &signer) { + let message_obj_addr_1 = + @0x4d78c35d85ab22061e25b5ec540a7fabc89e58799dc58462305c9cc318ef6dad; + message_board::update_message( + sender, + object::address_to_object(message_obj_addr_1), + string::utf8(b"updated message 3") + ); + + let message_obj_addr_2 = + @0x663ecbbaaa75dc8f96d257684251e0678d8153f9f4872c61cf67025bdb51c7f9; + message_board::update_message( + sender, + object::address_to_object(message_obj_addr_2), + string::utf8(b"updated message 4") + ); + message_board::update_message( + sender, + object::address_to_object(message_obj_addr_2), + string::utf8(b"updated message 5") + ); + + } +} diff --git a/templates/indexer-template/contracts/message-board/sh_scripts/deploy.sh b/templates/indexer-template/contracts/message-board/sh_scripts/deploy.sh new file mode 100755 index 00000000..1eb68b3a --- /dev/null +++ b/templates/indexer-template/contracts/message-board/sh_scripts/deploy.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -e + +echo "##### Deploy module under a new object #####" + +# Profile is the account you used to execute transaction +# Run "aptos init" to create the profile, then get the profile name from .aptos/config.yaml +PUBLISHER_PROFILE=testnet-profile-1 + +PUBLISHER_ADDR=0x$(aptos config show-profiles --profile=$PUBLISHER_PROFILE | grep 'account' | sed -n 's/.*"account": \"\(.*\)\".*/\1/p') + +OUTPUT=$(aptos move create-object-and-publish-package \ + --address-name message_board_addr \ + --named-addresses message_board_addr=$PUBLISHER_ADDR \ + --profile $PUBLISHER_PROFILE \ + --assume-yes) + +# Extract the published contract address and save it to a file +echo "$OUTPUT" | grep "Code was successfully deployed to object address" | awk '{print $NF}' | sed 's/\.$//' > contract_address.txt +echo "Contract published to address: $(cat contract_address.txt)" +echo "Contract address saved to contract_address.txt" diff --git a/templates/indexer-template/contracts/message-board/sh_scripts/get_abis.sh b/templates/indexer-template/contracts/message-board/sh_scripts/get_abis.sh new file mode 100755 index 00000000..8033c58d --- /dev/null +++ b/templates/indexer-template/contracts/message-board/sh_scripts/get_abis.sh @@ -0,0 +1,17 @@ +#! /bin/bash + +NETWORK=testnet + +CONTRACT_ADDRESS=$(cat ./contract_address.txt) + +MODULE_NAME=message_board + +ABI="export const ABI = $(curl https://fullnode.$NETWORK.aptoslabs.com/v1/accounts/$CONTRACT_ADDRESS/module/$MODULE_NAME | sed -n 's/.*"abi":\({.*}\).*}$/\1/p') as const" + +NEXT_APP_ABI_DIR="../../next-app/src/lib/abi" +mkdir -p $NEXT_APP_ABI_DIR +echo $ABI > $NEXT_APP_ABI_DIR/${MODULE_NAME}_abi.ts + +NODE_SCRIPTS_ABI_DIR="../../node-scripts/src/lib/abi" +mkdir -p $NODE_SCRIPTS_ABI_DIR +echo $ABI > $NODE_SCRIPTS_ABI_DIR/${MODULE_NAME}_abi.ts diff --git a/templates/indexer-template/contracts/message-board/sh_scripts/init.sh b/templates/indexer-template/contracts/message-board/sh_scripts/init.sh new file mode 100755 index 00000000..6deea426 --- /dev/null +++ b/templates/indexer-template/contracts/message-board/sh_scripts/init.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -e + +echo "##### Creating a new Aptos account #####" + +aptos init \ + --network testnet \ + --profile testnet-profile-1 diff --git a/templates/indexer-template/contracts/message-board/sh_scripts/run_create_2_messages_script.sh b/templates/indexer-template/contracts/message-board/sh_scripts/run_create_2_messages_script.sh new file mode 100755 index 00000000..4d915ee5 --- /dev/null +++ b/templates/indexer-template/contracts/message-board/sh_scripts/run_create_2_messages_script.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e + +echo "##### Running move script to create 2 messages in 1 tx #####" + +CONTRACT_ADDRESS=$(cat contract_address.txt) + +# Need to compile the package first +aptos move compile \ + --named-addresses message_board_addr=$CONTRACT_ADDRESS + +# Profile is the account you used to execute transaction +# Run "aptos init" to create the profile, then get the profile name from .aptos/config.yaml +SENDER_PROFILE=testnet-profile-1 + +# Run the script +aptos move run-script \ + --assume-yes \ + --profile $SENDER_PROFILE \ + --compiled-script-path build/message-board/bytecode_scripts/create_2_messages.mv diff --git a/templates/indexer-template/contracts/message-board/sh_scripts/run_update_message_script.sh b/templates/indexer-template/contracts/message-board/sh_scripts/run_update_message_script.sh new file mode 100755 index 00000000..f4b2b51a --- /dev/null +++ b/templates/indexer-template/contracts/message-board/sh_scripts/run_update_message_script.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e + +echo "##### Running move script to update some messages in 1 tx #####" + +CONTRACT_ADDRESS=$(cat contract_address.txt) + +# Need to compile the package first +aptos move compile \ + --named-addresses message_board_addr=$CONTRACT_ADDRESS + +# Profile is the account you used to execute transaction +# Run "aptos init" to create the profile, then get the profile name from .aptos/config.yaml +SENDER_PROFILE=testnet-profile-1 + +# Run the script +aptos move run-script \ + --assume-yes \ + --profile $SENDER_PROFILE \ + --compiled-script-path build/message-board/bytecode_scripts/update_message.mv diff --git a/templates/indexer-template/contracts/message-board/sh_scripts/test.sh b/templates/indexer-template/contracts/message-board/sh_scripts/test.sh new file mode 100755 index 00000000..3f067691 --- /dev/null +++ b/templates/indexer-template/contracts/message-board/sh_scripts/test.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +set -e + +echo "##### Running tests #####" + +aptos move test \ + --dev diff --git a/templates/indexer-template/contracts/message-board/sh_scripts/upgrade.sh b/templates/indexer-template/contracts/message-board/sh_scripts/upgrade.sh new file mode 100755 index 00000000..9e88d176 --- /dev/null +++ b/templates/indexer-template/contracts/message-board/sh_scripts/upgrade.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e + +echo "##### Upgrade module #####" + +# Profile is the account you used to execute transaction +# Run "aptos init" to create the profile, then get the profile name from .aptos/config.yaml +PUBLISHER_PROFILE=testnet-profile-1 + +CONTRACT_ADDRESS=$(cat contract_address.txt) + +aptos move upgrade-object-package \ + --object-address $CONTRACT_ADDRESS \ + --named-addresses message_board_addr=$CONTRACT_ADDRESS \ + --profile $PUBLISHER_PROFILE \ + --assume-yes diff --git a/templates/indexer-template/contracts/message-board/sources/message_board.move b/templates/indexer-template/contracts/message-board/sources/message_board.move new file mode 100644 index 00000000..824632c2 --- /dev/null +++ b/templates/indexer-template/contracts/message-board/sources/message_board.move @@ -0,0 +1,100 @@ +module message_board_addr::message_board { + use std::signer; + use std::string::String; + + use aptos_framework::event; + use aptos_framework::object::{Self, Object}; + use aptos_framework::timestamp; + + /// Only the message creator can update the message content + const ERR_ONLY_MESSAGE_CREATOR_CAN_UPDATE: u64 = 1; + + struct Message has copy, drop, key, store { + creator: address, + content: String, + creation_timestamp: u64, + last_update_timestamp: u64, + } + + #[event] + struct CreateMessageEvent has drop, store { + message_obj_addr: address, + message: Message, + } + + #[event] + struct UpdateMessageEvent has drop, store { + message_obj_addr: address, + message: Message, + } + + // This function is only called once when the module is published for the first time. + // init_module is optional, you can also have an entry function as the initializer. + fun init_module(_sender: &signer) {} + + // ======================== Write functions ======================== + + /// Create a new message + public entry fun create_message(sender: &signer, content: String) { + let message_obj_constructor_ref = &object::create_object(@message_board_addr); + let message_obj_signer = &object::generate_signer(message_obj_constructor_ref); + let time_now = timestamp::now_seconds(); + let message = Message { + creator: signer::address_of(sender), + content, + creation_timestamp: time_now, + last_update_timestamp:time_now, + }; + move_to(message_obj_signer, message); + + event::emit(CreateMessageEvent { + message_obj_addr: object::address_from_constructor_ref(message_obj_constructor_ref), + message, + }); + } + + /// Update the content of an existing message, only message creator can call + public entry fun update_message(sender: &signer, message_obj: Object, new_content: String) acquires Message { + let message = borrow_global_mut(object::object_address(&message_obj)); + assert!(message.creator == signer::address_of(sender), ERR_ONLY_MESSAGE_CREATOR_CAN_UPDATE); + message.content = new_content; + message.last_update_timestamp = timestamp::now_seconds(); + + event::emit(UpdateMessageEvent { + message_obj_addr: object::object_address(&message_obj), + message: *message, + }); + } + + // ======================== Read Functions ======================== + + #[view] + /// Get the content of a message + public fun get_message_content(message_obj: Object): (String, address, u64, u64) acquires Message { + let message = borrow_global(object::object_address(&message_obj)); + ( + message.content, + message.creator, + message.creation_timestamp, + message.last_update_timestamp, + ) + } + + // ================================= Uint Tests Helper ================================== // + + #[test_only] + public fun init_module_for_test(aptos_framework: &signer, sender: &signer) { + timestamp::set_time_has_started_for_testing(aptos_framework); + init_module(sender); + } + + #[test_only] + public fun get_message_obj_from_create_message_event(event: &CreateMessageEvent): Object { + object::address_to_object(event.message_obj_addr) + } + + #[test_only] + public fun get_message_obj_from_update_message_event(event: &UpdateMessageEvent): Object { + object::address_to_object(event.message_obj_addr) + } +} diff --git a/templates/indexer-template/contracts/message-board/tests/test_end_to_end.move b/templates/indexer-template/contracts/message-board/tests/test_end_to_end.move new file mode 100644 index 00000000..676ae02d --- /dev/null +++ b/templates/indexer-template/contracts/message-board/tests/test_end_to_end.move @@ -0,0 +1,46 @@ +#[test_only] +module message_board_addr::test_end_to_end { + use std::signer; + use std::string; + use std::vector; + + use aptos_framework::event; + + use message_board_addr::message_board; + + #[test(aptos_framework = @aptos_framework, deployer = @message_board_addr, sender = @0x100)] + fun test_end_to_end(aptos_framework: &signer, deployer: &signer, sender: &signer) { + let sender_addr = signer::address_of(sender); + + message_board::init_module_for_test(aptos_framework, deployer); + + message_board::create_message(sender, string::utf8(b"hello world")); + let events = event::emitted_events(); + let message_obj = message_board::get_message_obj_from_create_message_event(vector::borrow(&events, 0)); + let (content, creator, _, _) = message_board::get_message_content(message_obj); + assert!(content == string::utf8(b"hello world"), 1); + assert!(creator == sender_addr, 1); + + message_board::update_message(sender, message_obj, string::utf8(b"hello move")); + let events = event::emitted_events(); + // Since we force event type to be UpdateMessageEvent when calling get_message_obj_from_update_message_event() + // This will filter out other events (e.g. CreateMessageEvent) when calling event::emitted_events() + let message_obj = message_board::get_message_obj_from_update_message_event(vector::borrow(&events, 0)); + let (content, creator, _, _) = message_board::get_message_content(message_obj); + assert!(content == string::utf8(b"hello move"), 2); + assert!(creator == sender_addr, 2); + } + + #[test(aptos_framework = @aptos_framework, deployer = @message_board_addr, sender1 = @0x100, sender2 = @0x101)] + #[expected_failure(abort_code = 1, location = message_board_addr::message_board)] + fun test_only_creator_can_update(aptos_framework: &signer, deployer: &signer, sender1: &signer, sender2: &signer) { + message_board::init_module_for_test(aptos_framework, deployer); + + message_board::create_message(sender1, string::utf8(b"hello world")); + + let events = event::emitted_events(); + let message_obj = message_board::get_message_obj_from_create_message_event(vector::borrow(&events, 0)); + + message_board::update_message(sender2, message_obj, string::utf8(b"hello move")); + } +} diff --git a/templates/indexer-template/indexer/.cargo/config.toml b/templates/indexer-template/indexer/.cargo/config.toml new file mode 100644 index 00000000..fdbff2a9 --- /dev/null +++ b/templates/indexer-template/indexer/.cargo/config.toml @@ -0,0 +1,32 @@ +[alias] +xclippy = [ + "clippy", + "--workspace", + "--all-targets", + "--", + "-Dwarnings", + "-Wclippy::all", + "-Aclippy::upper_case_acronyms", + "-Aclippy::enum-variant-names", + "-Aclippy::result-large-err", + "-Aclippy::mutable-key-type", + "-Aclippy::map_identity", # We temporarily ignore this due to: https://github.com/rust-lang/rust-clippy/issues/11764 +] + +[build] +rustflags = [ + "--cfg", + "tokio_unstable", + "-C", + "force-frame-pointers=yes", + "-C", + "force-unwind-tables=yes", +] + +# TODO(grao): Figure out whether we should enable other cpu features, and whether we should use a different way to configure them rather than list every single one here. +#[target.x86_64-unknown-linux-gnu] +#rustflags = ["--cfg", "tokio_unstable", "-C", "link-arg=-fuse-ld=lld", "-C", "force-frame-pointers=yes", "-C", "force-unwind-tables=yes", "-C", "target-feature=+sse4.2"] + +# 64 bit MSVC +#[target.x86_64-pc-windows-msvc] +#rustflags = ["--cfg", "tokio_unstable", "-C", "force-frame-pointers=yes", "-C", "force-unwind-tables=yes", "-C", "link-arg=/STACK:8000000" # Set stack to 8 MB] diff --git a/templates/indexer-template/indexer/.gitignore b/templates/indexer-template/indexer/.gitignore new file mode 100644 index 00000000..183a4059 --- /dev/null +++ b/templates/indexer-template/indexer/.gitignore @@ -0,0 +1,22 @@ +build + +# Rust specific ignores +# Please follow https://help.github.com/en/articles/ignoring-files to create a global +# .gitignore file locally for IDE/Emacs/Vim generated files. +**/target +**/*.rs.bk +.idea/ + +# macOS Specific ignores +# General +.DS_Store +.AppleDouble +.LSOverride + +# VSCode settings +.vscode/ + +# Processor config +local.config.yaml +cloud.config.yaml +config.yaml diff --git a/templates/indexer-template/indexer/Cargo.lock b/templates/indexer-template/indexer/Cargo.lock new file mode 100644 index 00000000..0154dfeb --- /dev/null +++ b/templates/indexer-template/indexer/Cargo.lock @@ -0,0 +1,3894 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "antidote" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "aptos-indexer-processor-sdk" +version = "0.1.0" +source = "git+https://github.com/aptos-labs/aptos-indexer-processor-sdk.git?rev=e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7#e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7" +dependencies = [ + "anyhow", + "aptos-indexer-transaction-stream", + "aptos-protos", + "async-trait", + "bcs", + "bigdecimal", + "chrono", + "derive_builder", + "futures", + "hex", + "instrumented-channel", + "kanal", + "mockall", + "num_cpus", + "once_cell", + "petgraph", + "prometheus", + "prometheus-client", + "serde", + "serde_json", + "thiserror", + "tiny-keccak", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "aptos-indexer-processor-sdk-server-framework" +version = "1.0.0" +source = "git+https://github.com/aptos-labs/aptos-indexer-processor-sdk.git?rev=e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7#e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7" +dependencies = [ + "anyhow", + "aptos-indexer-processor-sdk", + "aptos-system-utils", + "async-trait", + "autometrics", + "axum 0.7.5", + "backtrace", + "clap", + "instrumented-channel", + "prometheus-client", + "serde", + "serde_yaml", + "tempfile", + "tokio", + "toml", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "aptos-indexer-transaction-stream" +version = "0.1.0" +source = "git+https://github.com/aptos-labs/aptos-indexer-processor-sdk.git?rev=e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7#e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7" +dependencies = [ + "anyhow", + "aptos-moving-average", + "aptos-protos", + "chrono", + "futures-util", + "once_cell", + "prometheus", + "prost", + "serde", + "tokio", + "tonic", + "tracing", + "url", +] + +[[package]] +name = "aptos-moving-average" +version = "0.1.0" +source = "git+https://github.com/aptos-labs/aptos-indexer-processor-sdk.git?rev=e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7#e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7" +dependencies = [ + "chrono", +] + +[[package]] +name = "aptos-profiler" +version = "0.1.0" +source = "git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93#4541add3fd29826ec57f22658ca286d2d6134b93" +dependencies = [ + "anyhow", + "backtrace", + "jemalloc-sys", + "jemallocator", + "pprof", + "regex", +] + +[[package]] +name = "aptos-protos" +version = "1.3.1" +source = "git+https://github.com/aptos-labs/aptos-core.git?rev=5c48aee129b5a141be2792ffa3d9bd0a1a61c9cb#5c48aee129b5a141be2792ffa3d9bd0a1a61c9cb" +dependencies = [ + "futures-core", + "pbjson", + "prost", + "serde", + "tonic", +] + +[[package]] +name = "aptos-system-utils" +version = "0.1.0" +source = "git+https://github.com/aptos-labs/aptos-core.git?rev=4541add3fd29826ec57f22658ca286d2d6134b93#4541add3fd29826ec57f22658ca286d2d6134b93" +dependencies = [ + "anyhow", + "aptos-profiler", + "async-mutex", + "http 0.2.12", + "hyper 0.14.30", + "lazy_static", + "mime", + "pprof", + "regex", + "rstack-self", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "autometrics" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10eaae539e7319a3813dc8cd53776a7128bdd6d82067275c12586f5a0fce9137" +dependencies = [ + "autometrics-macros", + "cfg_aliases 0.1.1", + "http 1.1.0", + "linkme", + "metrics-exporter-prometheus", + "once_cell", + "opentelemetry-prometheus", + "opentelemetry_sdk", + "prometheus", + "prometheus-client", + "spez", + "thiserror", +] + +[[package]] +name = "autometrics-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf7c9ebfee6425011c65788c746adf80fac99ba38957ba1cdb824b593cfc993" +dependencies = [ + "percent-encoding", + "proc-macro2", + "quote", + "regex", + "syn 2.0.74", +] + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core 0.3.4", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 0.1.2", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core 0.4.3", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bb8" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b10cf871f3ff2ce56432fddc2615ac7acc3aa22ca321f8fea800846fbb32f188" +dependencies = [ + "async-trait", + "futures-util", + "parking_lot", + "tokio", +] + +[[package]] +name = "bcs" +version = "0.1.4" +source = "git+https://github.com/aptos-labs/bcs.git?rev=d31fab9d81748e2594be5cd5cdf845786a30562d#d31fab9d81748e2594be5cd5cdf845786a30562d" +dependencies = [ + "serde", + "thiserror", +] + +[[package]] +name = "bigdecimal" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d712318a27c7150326677b321a5fa91b55f6d9034ffd67f20319e147d40cee" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "cc" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb8dd288a69fc53a1996d7ecfbf4a20d59065bff137ce7e56bbd620de191189" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "clap" +version = "4.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpp_demangle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.74", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "delegate" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e018fccbeeb50ff26562ece792ed06659b9c2dae79ece77c4456bb10d9bf79b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "derive_builder" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" +dependencies = [ + "derive_builder_core", + "syn 2.0.74", +] + +[[package]] +name = "diesel" +version = "2.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" +dependencies = [ + "bigdecimal", + "bitflags 2.6.0", + "byteorder", + "chrono", + "diesel_derives", + "itoa", + "num-bigint", + "num-integer", + "num-traits", + "serde_json", +] + +[[package]] +name = "diesel-async" +version = "0.4.1" +source = "git+https://github.com/weiznich/diesel_async.git?rev=d02798c67065d763154d7272dd0c09b39757d0f2#d02798c67065d763154d7272dd0c09b39757d0f2" +dependencies = [ + "async-trait", + "bb8", + "diesel", + "futures-util", + "scoped-futures", + "tokio", + "tokio-postgres", +] + +[[package]] +name = "diesel_derives" +version = "2.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" +dependencies = [ + "diesel_table_macro_syntax", + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "diesel_migrations" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" +dependencies = [ + "diesel", + "migrations_internals", + "migrations_macros", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +dependencies = [ + "syn 2.0.74", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dw" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef0ed82b765c2ab79fb48e4bf2c95bd583202f4078a702bc714cc6e6f3ca80c3" +dependencies = [ + "dw-sys", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "dw-sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14eb35c87ff6626cd1021bb32bc7d9a5372ea72547e1eaf0343a841d9d55a973" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "field_count" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284d5f85dd574cf01094bca24aefa69a43539dbfc72b1326f038d540b2daadc7" +dependencies = [ + "field_count_derive", +] + +[[package]] +name = "field_count_derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1320970ff3b1c1cacc6a38e8cdb1aced955f29627697cd992c5ded82eb646a8" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.4.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.4.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.7", + "bytes", + "headers-core", + "http 1.1.0", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http 1.1.0", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper 0.14.30", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-util" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexer" +version = "0.1.0" +dependencies = [ + "ahash", + "anyhow", + "aptos-indexer-processor-sdk", + "aptos-indexer-processor-sdk-server-framework", + "async-trait", + "chrono", + "clap", + "diesel", + "diesel-async", + "diesel_migrations", + "field_count", + "futures-util", + "jemallocator", + "native-tls", + "num_cpus", + "poem", + "postgres-native-tls", + "rayon", + "serde", + "serde_json", + "strum", + "tokio", + "tokio-postgres", + "tracing", + "url", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[package]] +name = "inferno" +version = "0.11.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" +dependencies = [ + "ahash", + "indexmap 2.4.0", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml", + "rgb", + "str_stack", +] + +[[package]] +name = "instrumented-channel" +version = "0.1.0" +source = "git+https://github.com/aptos-labs/aptos-indexer-processor-sdk.git?rev=e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7#e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7" +dependencies = [ + "delegate", + "derive_builder", + "kanal", + "once_cell", + "prometheus", + "prometheus-client", +] + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jemalloc-sys" +version = "0.5.4+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "jemallocator" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0de374a9f8e63150e6f5e8a60cc14c668226d7a347d8aee1a45766e3c4dd3bc" +dependencies = [ + "jemalloc-sys", + "libc", +] + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kanal" +version = "0.1.0-pre8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05d55519627edaf7fd0f29981f6dc03fb52df3f5b257130eb8d0bf2801ea1d7" +dependencies = [ + "futures-core", + "lock_api", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linkme" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c943daedff228392b791b33bba32e75737756e80a613e32e246c6ce9cbab20a" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26336e6dc7cc76e7927d2c9e7e3bb376d7af65a6f56a0b16c47d18a9b1abc5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "metrics" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde3af1a009ed76a778cb84fdef9e7dbbdf5775ae3e4cc1f434a6a307f6f76c5" +dependencies = [ + "ahash", + "metrics-macros", + "portable-atomic", +] + +[[package]] +name = "metrics-exporter-prometheus" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d4fa7ce7c4862db464a37b0b31d89bca874562f034bd7993895572783d02950" +dependencies = [ + "base64 0.21.7", + "indexmap 1.9.3", + "metrics", + "metrics-util", + "quanta", + "thiserror", +] + +[[package]] +name = "metrics-macros" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b4faf00617defe497754acde3024865bc143d44a86799b24e191ecff91354f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "metrics-util" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de2ed6e491ed114b40b732e4d1659a9d53992ebd87490c44a6ffe23739d973e" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", + "hashbrown 0.13.1", + "metrics", + "num_cpus", + "quanta", + "sketches-ddsketch", +] + +[[package]] +name = "migrations_internals" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "migrations_macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "mockall" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases 0.2.1", + "libc", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "object" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "opentelemetry" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" +dependencies = [ + "futures-core", + "futures-sink", + "indexmap 2.4.0", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", + "urlencoding", +] + +[[package]] +name = "opentelemetry-prometheus" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8f082da115b0dcb250829e3ed0b8792b8f963a1ad42466e48422fbe6a079bd" +dependencies = [ + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "prometheus", + "protobuf", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f16aec8a98a457a52664d69e0091bac3a0abd18ead9b641cb00202ba4e0efe4" +dependencies = [ + "async-trait", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "once_cell", + "opentelemetry", + "ordered-float", + "thiserror", +] + +[[package]] +name = "ordered-float" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a91171844676f8c7990ce64959210cd2eaef32c2612c50f9fae9f8aaa6065a6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.3", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pbjson" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048f9ac93c1eab514f9470c4bc8d97ca2a0a236b84f45cc19d69a59fc11467f6" +dependencies = [ + "base64 0.13.1", + "serde", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.4.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "poem" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5419c612a492fce4961c521dca0c2249b5c48dc46eb5c8048063843f37a711d" +dependencies = [ + "anyhow", + "bytes", + "futures-util", + "headers", + "http 1.1.0", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "mime", + "nix 0.29.0", + "parking_lot", + "percent-encoding", + "pin-project-lite", + "poem-derive", + "regex", + "rfc7239", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "sync_wrapper 1.0.1", + "thiserror", + "tokio", + "tokio-util", + "tracing", + "wildmatch", +] + +[[package]] +name = "poem-derive" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdfed15c1102d2a9a51b9f1aba945628c72ccb52fc5d3e4ad4ffbbd222e11821" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "portable-atomic" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" + +[[package]] +name = "postgres-native-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d442770e2b1e244bb5eb03b31c79b65bb2568f413b899eaba850fa945a65954" +dependencies = [ + "futures", + "native-tls", + "tokio", + "tokio-native-tls", + "tokio-postgres", +] + +[[package]] +name = "postgres-protocol" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" +dependencies = [ + "base64 0.22.1", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand", + "sha2", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02048d9e032fb3cc3413bbf7b83a15d84a5d419778e2628751896d856498eee9" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", +] + +[[package]] +name = "pprof" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196ded5d4be535690899a4631cc9f18cdc41b7ebf24a79400f46f48e49a11059" +dependencies = [ + "backtrace", + "cfg-if", + "findshlibs", + "inferno", + "libc", + "log", + "nix 0.26.4", + "once_cell", + "parking_lot", + "protobuf", + "protobuf-codegen-pure", + "smallvec", + "symbolic-demangle", + "tempfile", + "thiserror", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit 0.22.20", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot", + "protobuf", + "thiserror", +] + +[[package]] +name = "prometheus-client" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504ee9ff529add891127c4827eb481bd69dc0ebc72e9a682e187db4caa60c3ca" +dependencies = [ + "dtoa", + "itoa", + "parking_lot", + "prometheus-client-derive-encode", +] + +[[package]] +name = "prometheus-client-derive-encode" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + +[[package]] +name = "protobuf-codegen" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033460afb75cf755fcfc16dfaed20b86468082a2ea24e05ac35ab4a099a017d6" +dependencies = [ + "protobuf", +] + +[[package]] +name = "protobuf-codegen-pure" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a29399fc94bcd3eeaa951c715f7bea69409b2445356b00519740bcd6ddd865" +dependencies = [ + "protobuf", + "protobuf-codegen", +] + +[[package]] +name = "quanta" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" +dependencies = [ + "crossbeam-utils", + "libc", + "mach2", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rfc7239" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b106a85eeb5b0336d16d6a20eab857f92861d4fbb1eb9a239866fb98fb6a1063" +dependencies = [ + "uncased", +] + +[[package]] +name = "rgb" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rstack" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7df9d3ebd4f17b52e6134efe2fa20021c80688cbe823d481a729a993b730493" +dependencies = [ + "cfg-if", + "dw", + "lazy_static", + "libc", + "log", +] + +[[package]] +name = "rstack-self" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd5030da3aba0ec731502f74ec38e63798eea6bc8b8ba5972129afe3eababd2" +dependencies = [ + "antidote", + "backtrace", + "bincode", + "lazy_static", + "libc", + "rstack", + "serde", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.102.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scoped-futures" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1473e24c637950c9bd38763220bea91ec3e095a89f672bbd7a10d03e77ba467" +dependencies = [ + "cfg-if", + "pin-utils", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.207" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.207" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "serde_json" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" +dependencies = [ + "indexmap 2.4.0", + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap 1.9.3", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "sketches-ddsketch" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85636c14b73d81f541e525f585c0a2109e6744e1565b5c1668e31c70c10ed65c" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spez" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87e960f4dca2788eeb86bbdde8dd246be8948790b7618d656e68f9b720a86e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "str_stack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "symbolic-common" +version = "10.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b55cdc318ede251d0957f07afe5fed912119b8c1bc5a7804151826db999e737" +dependencies = [ + "debugid", + "memmap2", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "10.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79be897be8a483a81fff6a3a4e195b4ac838ef73ca42d348b3f722da9902e489" +dependencies = [ + "cpp_demangle", + "rustc-demangle", + "symbolic-common", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-postgres" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03adcf0147e203b6032c0b2d30be1415ba03bc348901f3ff1cc0df6a733e60c3" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand", + "socket2", + "tokio", + "tokio-util", + "whoami", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.4.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap 2.4.0", + "toml_datetime", + "winnow 0.6.18", +] + +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.6.20", + "base64 0.21.7", + "bytes", + "flate2", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", + "zstd", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.74", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall 0.4.1", + "wasite", + "web-sys", +] + +[[package]] +name = "wildmatch" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3928939971918220fed093266b809d1ee4ec6c1a2d72692ff6876898f3b16c19" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zstd" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "6.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/templates/indexer-template/indexer/Cargo.toml b/templates/indexer-template/indexer/Cargo.toml new file mode 100644 index 00000000..174172e1 --- /dev/null +++ b/templates/indexer-template/indexer/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "indexer" +version = "0.1.0" +edition = "2021" + +[dependencies] + +# aptos dependencies + +aptos-indexer-processor-sdk = { git = "https://github.com/aptos-labs/aptos-indexer-processor-sdk.git", rev = "e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7" } +aptos-indexer-processor-sdk-server-framework = { git = "https://github.com/aptos-labs/aptos-indexer-processor-sdk.git", rev = "e1e1bdd9349f0a68c9fc53b7e2cebda9e2ce92b7" } + +# other dependencies + +ahash = { version = "0.8.7", features = ["serde"] } +anyhow = "1.0.86" +async-trait = "0.1.80" +chrono = { version = "0.4.19", features = ["clock", "serde"] } +clap = { version = "4.3.5", features = ["derive", "unstable-styles"] } +# Do NOT enable the postgres feature here, it is conditionally enabled in a feature +# block in the Cargo.toml file for the processor crate. +# https://github.com/aptos-labs/aptos-indexer-processors/pull/325 +diesel = { version = "2.1", features = [ + "chrono", + "postgres_backend", + "numeric", + "serde_json", +] } +# Use the crate version once this feature gets released on crates.io: +# https://github.com/weiznich/diesel_async/commit/e165e8c96a6c540ebde2d6d7c52df5c5620a4bf1 +diesel-async = { git = "https://github.com/weiznich/diesel_async.git", rev = "d02798c67065d763154d7272dd0c09b39757d0f2", features = [ + "async-connection-wrapper", + "postgres", + "bb8", + "tokio", +] } +diesel_migrations = { version = "2.1.0", features = ["postgres"] } +field_count = "0.1.1" +futures-util = "0.3.21" +jemallocator = { version = "0.5.0", features = [ + "profiling", + "unprefixed_malloc_on_supported_platforms", +] } +num_cpus = "1.16.0" +poem = { version = "3.1.0", features = ["anyhow"] } +rayon = "1.10.0" +serde = { version = "1.0.193", features = ["derive", "rc"] } +serde_json = { version = "1.0.81", features = ["preserve_order"] } +strum = { version = "0.24.1", features = ["derive"] } +tracing = "0.1.34" +tokio = { version = "1.37.0", features = ["full"] } +url = { version = "2.5.1", features = ["serde"] } + +# Postgres SSL support +native-tls = "0.2.11" +postgres-native-tls = "0.5.0" +tokio-postgres = "0.7.10" diff --git a/templates/indexer-template/indexer/Dockerfile b/templates/indexer-template/indexer/Dockerfile new file mode 100644 index 00000000..a8d05966 --- /dev/null +++ b/templates/indexer-template/indexer/Dockerfile @@ -0,0 +1,48 @@ +# Use the official Rust image to build the binary +FROM rust:latest AS builder + +# Install the necessary system dependencies +RUN apt-get update && apt-get install -y \ + libdw-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +# Set the working directory inside the container +WORKDIR /usr/src/app + +# Copy files +COPY Cargo.toml Cargo.lock ./ +COPY .cargo .cargo +COPY src src + +# Add the target for x86_64 (Cloud Run architecture) +RUN rustup target add x86_64-unknown-linux-gnu + +# Build the project for x86_64 architecture +RUN cargo build --release --target=x86_64-unknown-linux-gnu + +# Use a minimal base image to run the binary +FROM debian:12 + +# Install runtime dependencies +RUN apt-get update && apt-get install -y \ + libdw1 \ + ca-certificates \ + openssl \ + libssl3 \ + && rm -rf /var/lib/apt/lists/* + +# Set the working directory inside the container +WORKDIR /usr/src/app + +# Copy the compiled binary from the builder image +COPY --from=builder /usr/src/app/target/x86_64-unknown-linux-gnu/release/indexer . + +# Copy the configuration file +COPY config.yaml /secrets/config + +# Expose the port your application is using +EXPOSE 8080 + +# Set the command to run the application +CMD ["./indexer", "-c", "/secrets/config"] diff --git a/templates/indexer-template/indexer/README.md b/templates/indexer-template/indexer/README.md new file mode 100644 index 00000000..6a6e0ef6 --- /dev/null +++ b/templates/indexer-template/indexer/README.md @@ -0,0 +1,136 @@ +# Overview + +This indexer is created from indexer-sdk, see a more detailed readme in [example indexer repo](https://github.com/aptos-labs/aptos-indexer-processor-example). + +We use the term indexer and processor interchangeably. + +## Pre-requisites + +Create a Vercel account and a Google Cloud account. We use Vercel to host the Postgres DB and Google Cloud to host the indexer. + +Create a new Vercel Postgres DB and a new Google Cloud project. + +Learn more about Vercel Postgres on [their docs](https://vercel.com/docs/storage/vercel-postgres). + +Install diesel cli to run migrations. + +```sh +cargo install diesel_cli --no-default-features --features postgres +``` + +## Running the indexer locally + +**Note: all commends below need to be run in the current indexer directory instead of root directory.** + +### Steps + +Drop the DB if exists. You cannot do this if you are using a cloud DB. Follow the revert migration command below instead. + +```sh +psql postgres://username:password@127.0.0.1:5432/postgres \ + -c 'DROP DATABASE IF EXISTS "example-indexer"' +``` + +Create the DB. + +```sh +psql postgres://username:password@127.0.0.1:5432/postgres \ + -c 'CREATE DATABASE "example-indexer"' +``` + +Create a new migration file. + +```sh +diesel migration generate create-abc-table \ + --config-file="src/db_migrations/diesel.toml" +``` + +Run all pending migrations. + +```sh +diesel migration run \ + --config-file="src/db_migrations/diesel.toml" \ + --database-url="postgresql://username:password@localhost:5432/example-indexer" +``` + +In case you want to revert all migrations. On cloud provider, you cannot drop database, so you need to revert all migrations if you want to reset. + +```sh +diesel migration revert \ + --all \ + --config-file="src/db_migrations/diesel.toml" \ + --database-url="postgresql://username:password@localhost:5432/example-indexer" +``` + +Create a `config.yaml` file from `example.config.yaml` file to point to the correct network, db url, start version, etc. Run the indexer. + +```sh +cargo run --release -- -c config.yaml +``` + +You should see the indexer start to index Aptos blockchain events! + +```sh +"timestamp":"2024-08-15T01:06:35.169217Z","level":"INFO","message":"[Transaction Stream] Received transactions from GRPC.","stream_address":"https://grpc.testnet.aptoslabs.com/","connection_id":"5575cb8c-61fb-498f-aaae-868d1e8773ac","start_version":0,"end_version":4999,"start_txn_timestamp_iso":"1970-01-01T00:00:00.000000000Z","end_txn_timestamp_iso":"2022-09-09T01:49:02.023089000Z","num_of_transactions":5000,"size_in_bytes":5708539,"duration_in_secs":0.310734,"tps":16078,"bytes_per_sec":18371143.80788713,"filename":"/Users/reneetso/.cargo/git/checkouts/aptos-indexer-processor-sdk-2f3940a333c8389d/e1e1bdd/rust/transaction-stream/src/transaction_stream.rs","line_number":400,"threadName":"tokio-runtime-worker","threadId":"ThreadId(6)" +"timestamp":"2024-08-15T01:06:35.257756Z","level":"INFO","message":"Events version [0, 4999] stored successfully","filename":"src/processors/events/events_storer.rs","line_number":75,"threadName":"tokio-runtime-worker","threadId":"ThreadId(10)" +"timestamp":"2024-08-15T01:06:35.257801Z","level":"INFO","message":"Finished processing events from versions [0, 4999]","filename":"src/processors/events/events_processor.rs","line_number":90,"threadName":"tokio-runtime-worker","threadId":"ThreadId(17)" +``` + +## Get ready for cloud deployment + +I'm using GCP Cloud Run and Artifact Registry. + +You can learn more about publishing to Artifact Registry on their docs: + +- https://cloud.google.com/artifact-registry/docs/docker/pushing-and-pulling#pushing +- https://cloud.google.com/artifact-registry/docs/docker/store-docker-container-images + +And deploying to Cloud Run: + +- https://cloud.google.com/run/docs/quickstarts/deploy-container + +### Build the docker image locally and run the container locally + +Build the docker image targeting linux/amd64 because eventually, we will push the image to Artifact Registry and deploy it to Cloud Run. + +```sh +docker build --platform linux/amd64 -t indexer . +``` + +Run the docker container locally. Mac supports linux/amd64 emulation so you can run the container locally. + +```sh +docker run -p 8080:8080 -it indexer +``` + +### Push the locally build docker image to Artifact Registry + +Tag the docker image. + +```sh +docker tag indexer us-west2-docker.pkg.dev/indexer-sdk-demo/indexer-sdk-demo/indexer +``` + +Login to google cloud + +```sh +gcloud auth login +``` + +Push the docker image to the container registry. + +```sh +docker push us-west2-docker.pkg.dev/indexer-sdk-demo/indexer-sdk-demo/indexer +``` + +### Upload the config.yaml file to Secret Manager + +Go to secret manager and create a new secret with the content of the config.yaml file. + +### Run the container on Cloud Run + +Video walkthrough: https://drive.google.com/file/d/1JayWuH2qgnqOgzVuZm9MwKT42hj4z0JN/view + +Go to cloud run dashboard, create a new service, and select the container image from Artifact Registry, also add a volume to ready the config.yaml file from Secret Manager, then mount the volume to the container. + +**NOTE**: always allocate cpu so it always runs instead of only run when there is traffic. Min and max instances should be 1. diff --git a/templates/indexer-template/indexer/example.config.yaml b/templates/indexer-template/indexer/example.config.yaml new file mode 100644 index 00000000..e2459d37 --- /dev/null +++ b/templates/indexer-template/indexer/example.config.yaml @@ -0,0 +1,20 @@ +# This is a template yaml for the aptos-indexer-processor. +health_check_port: 8085 +server_config: + processor_config: + type: "events_processor" + transaction_stream_config: + indexer_grpc_data_service_address: "https://grpc.testnet.aptoslabs.com:443" + starting_version: 5936597868 + # request_ending_version: 10000 + auth_token: "auth_token_you_can_get_from_aptos_api_gateway" + request_name_header: "events-processor" + db_config: + # do not include the ?sslmode=require in the connection string, Use the native TLS implementation in rust + # don't know why it's not working with the sslmode=require + postgres_connection_string: "postgresql://username:@localhost:5432/example-indexer" + # we set db_pool_size to a lower number on cloud because we use a free plan + # see limitation on vercel docs https://vercel.com/docs/storage/vercel-postgres/faq + db_pool_size: 25 + contract_config: + contract_address: "your_contract_address" diff --git a/templates/indexer-template/indexer/src/config/indexer_processor_config.rs b/templates/indexer-template/indexer/src/config/indexer_processor_config.rs new file mode 100644 index 00000000..eb616252 --- /dev/null +++ b/templates/indexer-template/indexer/src/config/indexer_processor_config.rs @@ -0,0 +1,62 @@ +use super::processor_config::ProcessorConfig; +use crate::processors::events::events_processor::EventsProcessor; +use anyhow::Result; +use aptos_indexer_processor_sdk::aptos_indexer_transaction_stream::TransactionStreamConfig; +use aptos_indexer_processor_sdk_server_framework::RunnableConfig; +use serde::{Deserialize, Serialize}; + +pub const QUERY_DEFAULT_RETRIES: u32 = 5; +pub const QUERY_DEFAULT_RETRY_DELAY_MS: u64 = 500; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub struct IndexerProcessorConfig { + pub processor_config: ProcessorConfig, + pub transaction_stream_config: TransactionStreamConfig, + pub db_config: DbConfig, + pub contract_config: ContractConfig, +} + +#[async_trait::async_trait] +impl RunnableConfig for IndexerProcessorConfig { + async fn run(&self) -> Result<()> { + match self.processor_config { + ProcessorConfig::EventsProcessor => { + let events_processor = EventsProcessor::new(self.clone()).await?; + events_processor.run_processor().await + } + } + } + + fn get_server_name(&self) -> String { + // Get the part before the first _ and trim to 12 characters. + let before_underscore = self + .processor_config + .name() + .split('_') + .next() + .unwrap_or("unknown"); + before_underscore[..before_underscore.len().min(12)].to_string() + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub struct DbConfig { + pub postgres_connection_string: String, + // Size of the pool for writes/reads to the DB. Limits maximum number of queries in flight + #[serde(default = "DbConfig::default_db_pool_size")] + pub db_pool_size: u32, +} + +impl DbConfig { + pub const fn default_db_pool_size() -> u32 { + 50 + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub struct ContractConfig { + pub contract_address: String, +} diff --git a/templates/indexer-template/indexer/src/config/mod.rs b/templates/indexer-template/indexer/src/config/mod.rs new file mode 100644 index 00000000..d26328bd --- /dev/null +++ b/templates/indexer-template/indexer/src/config/mod.rs @@ -0,0 +1,2 @@ +pub mod indexer_processor_config; +pub mod processor_config; diff --git a/templates/indexer-template/indexer/src/config/processor_config.rs b/templates/indexer-template/indexer/src/config/processor_config.rs new file mode 100644 index 00000000..fd16b8b8 --- /dev/null +++ b/templates/indexer-template/indexer/src/config/processor_config.rs @@ -0,0 +1,74 @@ +use serde::{Deserialize, Serialize}; + +/// This enum captures the configs for all the different processors that are defined. +/// The configs for each processor should only contain configuration specific to that +/// processor. For configuration that is common to all processors, put it in +/// IndexerGrpcProcessorConfig. +#[derive(Clone, Debug, Deserialize, Serialize, strum::IntoStaticStr, strum::EnumDiscriminants)] +#[serde(tag = "type", rename_all = "snake_case")] +// What is all this strum stuff? Let me explain. +// +// Previously we had consts called NAME in each module and a function called `name` on +// the ProcessorTrait. As such it was possible for this name to not match the snake case +// representation of the struct name. By using strum we can have a single source for +// processor names derived from the enum variants themselves. +// +// That's what this strum_discriminants stuff is, it uses macro magic to generate the +// ProcessorName enum based on ProcessorConfig. The rest of the derives configure this +// generation logic, e.g. to make sure we use snake_case. +#[strum(serialize_all = "snake_case")] +#[strum_discriminants( + derive( + Deserialize, + Serialize, + strum::EnumVariantNames, + strum::IntoStaticStr, + strum::Display, + clap::ValueEnum + ), + name(ProcessorName), + clap(rename_all = "snake_case"), + serde(rename_all = "snake_case"), + strum(serialize_all = "snake_case") +)] +pub enum ProcessorConfig { + EventsProcessor, +} + +impl ProcessorConfig { + /// Get the name of the processor config as a static str. This is a convenience + /// method to access the derived functionality implemented by strum::IntoStaticStr. + pub fn name(&self) -> &'static str { + self.into() + } +} +#[derive(Debug)] +// To ensure that the variants of ProcessorConfig and Processor line up, in the testing +// build path we derive EnumDiscriminants on this enum as well and make sure the two +// sets of variants match up in `test_processor_names_complete`. +#[cfg_attr( + test, + derive(strum::EnumDiscriminants), + strum_discriminants( + derive(strum::EnumVariantNames), + name(ProcessorDiscriminants), + strum(serialize_all = "snake_case") + ) +)] +pub enum Processor { + EventsProcessor, +} + +#[cfg(test)] +mod test { + use super::*; + use strum::VariantNames; + + /// This test exists to make sure that when a new processor is added, it is added + /// to both Processor and ProcessorConfig. To make sure this passes, make sure the + /// variants are in the same order (lexicographical) and the names match. + #[test] + fn test_processor_names_complete() { + assert_eq!(ProcessorName::VARIANTS, ProcessorDiscriminants::VARIANTS); + } +} diff --git a/templates/indexer-template/indexer/src/db_migrations/diesel.toml b/templates/indexer-template/indexer/src/db_migrations/diesel.toml new file mode 100644 index 00000000..9a59970b --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/diesel.toml @@ -0,0 +1,8 @@ +# For documentation on how to configure this file, +# see https://diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "schema.rs" + +[migrations_directory] +dir = "migrations" diff --git a/templates/indexer-template/indexer/src/db_migrations/migrations/00000000000000_diesel_initial_setup/down.sql b/templates/indexer-template/indexer/src/db_migrations/migrations/00000000000000_diesel_initial_setup/down.sql new file mode 100644 index 00000000..a9f52609 --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/migrations/00000000000000_diesel_initial_setup/down.sql @@ -0,0 +1,6 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + +DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); +DROP FUNCTION IF EXISTS diesel_set_updated_at(); diff --git a/templates/indexer-template/indexer/src/db_migrations/migrations/00000000000000_diesel_initial_setup/up.sql b/templates/indexer-template/indexer/src/db_migrations/migrations/00000000000000_diesel_initial_setup/up.sql new file mode 100644 index 00000000..d68895b1 --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/migrations/00000000000000_diesel_initial_setup/up.sql @@ -0,0 +1,36 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + + + + +-- Sets up a trigger for the given table to automatically set a column called +-- `updated_at` whenever the row is modified (unless `updated_at` was included +-- in the modified columns) +-- +-- # Example +-- +-- ```sql +-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); +-- +-- SELECT diesel_manage_updated_at('users'); +-- ``` +CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ +BEGIN + EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s + FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ +BEGIN + IF ( + NEW IS DISTINCT FROM OLD AND + NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at + ) THEN + NEW.updated_at := current_timestamp; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; diff --git a/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-194547_create-processor-status/down.sql b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-194547_create-processor-status/down.sql new file mode 100644 index 00000000..9eb504e3 --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-194547_create-processor-status/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql`d +DROP TABLE IF EXISTS processor_status; \ No newline at end of file diff --git a/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-194547_create-processor-status/up.sql b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-194547_create-processor-status/up.sql new file mode 100644 index 00000000..d34baf1b --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-194547_create-processor-status/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here +CREATE TABLE processor_status ( + processor VARCHAR(50) NOT NULL, + last_success_version BIGINT NOT NULL, + last_updated TIMESTAMP NOT NULL, + last_transaction_timestamp TIMESTAMP NULL, + PRIMARY KEY (processor) +); \ No newline at end of file diff --git a/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-202400_test-migration/down.sql b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-202400_test-migration/down.sql new file mode 100644 index 00000000..26a34c80 --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-202400_test-migration/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE IF EXISTS events ALTER COLUMN inserted_at DROP DEFAULT; +ALTER TABLE IF EXISTS processor_status ALTER COLUMN last_updated DROP DEFAULT; \ No newline at end of file diff --git a/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-202400_test-migration/up.sql b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-202400_test-migration/up.sql new file mode 100644 index 00000000..e8e2a5e6 --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-07-18-202400_test-migration/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +ALTER TABLE IF EXISTS events ALTER COLUMN inserted_at SET DEFAULT NOW(); +ALTER TABLE IF EXISTS processor_status ALTER COLUMN last_updated SET DEFAULT NOW(); \ No newline at end of file diff --git a/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-01-224558_ledger-infos/down.sql b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-01-224558_ledger-infos/down.sql new file mode 100644 index 00000000..e54762a5 --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-01-224558_ledger-infos/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE IF EXISTS ledger_infos; \ No newline at end of file diff --git a/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-01-224558_ledger-infos/up.sql b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-01-224558_ledger-infos/up.sql new file mode 100644 index 00000000..6f9dafee --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-01-224558_ledger-infos/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +CREATE TABLE ledger_infos (chain_id BIGINT UNIQUE PRIMARY KEY NOT NULL); \ No newline at end of file diff --git a/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-25-233538_create-message-table/down.sql b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-25-233538_create-message-table/down.sql new file mode 100644 index 00000000..4941c7e0 --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-25-233538_create-message-table/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE IF EXISTS messages; \ No newline at end of file diff --git a/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-25-233538_create-message-table/up.sql b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-25-233538_create-message-table/up.sql new file mode 100644 index 00000000..b6002654 --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/migrations/2024-08-25-233538_create-message-table/up.sql @@ -0,0 +1,11 @@ +-- Your SQL goes here +CREATE TABLE messages ( + message_obj_addr VARCHAR(300) NOT NULL UNIQUE PRIMARY KEY, + creator_addr VARCHAR(300) NOT NULL, + creation_timestamp BIGINT NOT NULL, + last_update_timestamp BIGINT NOT NULL, + -- we store the event index so when we update in batch, + -- we ignore when the event index is less than the last update event index + last_update_event_idx BIGINT NOT NULL, + content TEXT NOT NULL +); \ No newline at end of file diff --git a/templates/indexer-template/indexer/src/db_migrations/schema.rs b/templates/indexer-template/indexer/src/db_migrations/schema.rs new file mode 100644 index 00000000..c1d69579 --- /dev/null +++ b/templates/indexer-template/indexer/src/db_migrations/schema.rs @@ -0,0 +1,36 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + ledger_infos (chain_id) { + chain_id -> Int8, + } +} + +diesel::table! { + messages (message_obj_addr) { + #[max_length = 300] + message_obj_addr -> Varchar, + #[max_length = 300] + creator_addr -> Varchar, + creation_timestamp -> Int8, + last_update_timestamp -> Int8, + last_update_event_idx -> Int8, + content -> Text, + } +} + +diesel::table! { + processor_status (processor) { + #[max_length = 50] + processor -> Varchar, + last_success_version -> Int8, + last_updated -> Timestamp, + last_transaction_timestamp -> Nullable, + } +} + +diesel::allow_tables_to_appear_in_same_query!( + ledger_infos, + messages, + processor_status, +); diff --git a/templates/indexer-template/indexer/src/db_models/events_models.rs b/templates/indexer-template/indexer/src/db_models/events_models.rs new file mode 100644 index 00000000..37c30535 --- /dev/null +++ b/templates/indexer-template/indexer/src/db_models/events_models.rs @@ -0,0 +1,134 @@ +use crate::schema::messages; +use aptos_indexer_processor_sdk::{ + aptos_protos::transaction::v1::Event as EventPB, utils::convert::standardize_address, +}; +use diesel::{AsChangeset, Insertable}; +use field_count::FieldCount; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Serialize)] +/// On-chain representation of a message +pub struct MessageOnChain { + pub creator: String, + pub content: String, + pub creation_timestamp: String, + pub last_update_timestamp: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +/// On-chain representation of a message creation event +pub struct CreateMessageEventOnChain { + pub message_obj_addr: String, + pub message: MessageOnChain, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +/// On-chain representation of a message update event +pub struct UpdateMessageEventOnChain { + pub message_obj_addr: String, + pub message: MessageOnChain, +} + +#[derive(AsChangeset, Clone, Debug, Deserialize, FieldCount, Insertable, Serialize)] +#[diesel(table_name = messages)] +/// Database representation of a message +pub struct Message { + pub message_obj_addr: String, + pub creator_addr: String, + pub creation_timestamp: i64, + pub last_update_timestamp: i64, + pub last_update_event_idx: i64, + pub content: String, +} + +#[derive(Debug, Clone)] +pub enum ContractEvent { + CreateMessageEvent(Message), + UpdateMessageEvent(Message), +} + +impl ContractEvent { + pub fn from_event(contract_address: &str, event_idx: usize, event: &EventPB) -> Option { + let t: &str = event.type_str.as_ref(); + let should_include = t.starts_with(contract_address); + + if should_include { + if t.starts_with( + format!("{}::message_board::CreateMessageEvent", contract_address).as_str(), + ) { + println!("CreateMessageEvent {}", event.data.as_str()); + let create_message_event_on_chain: CreateMessageEventOnChain = + serde_json::from_str(event.data.as_str()).expect( + format!( + "Failed to parse CreateMessageEvent, {}", + event.data.as_str() + ) + .as_str(), + ); + let creation_timestamp = create_message_event_on_chain + .message + .creation_timestamp + .parse() + .unwrap(); + let message = Message { + message_obj_addr: standardize_address( + &create_message_event_on_chain.message_obj_addr, + ), + creator_addr: standardize_address( + create_message_event_on_chain.message.creator.as_str(), + ), + creation_timestamp, + content: create_message_event_on_chain.message.content, + last_update_timestamp: creation_timestamp, + last_update_event_idx: 0, + }; + Some(ContractEvent::CreateMessageEvent(message)) + } else if t.starts_with( + format!("{}::message_board::UpdateMessageEvent", contract_address).as_str(), + ) { + println!("UpdateMessageEvent {}", event.data.as_str()); + let update_message_event_on_chain: UpdateMessageEventOnChain = + serde_json::from_str(event.data.as_str()).expect( + format!( + "Failed to parse UpdateMessageEvent, {}", + event.data.as_str() + ) + .as_str(), + ); + let message = Message { + message_obj_addr: standardize_address( + &update_message_event_on_chain.message_obj_addr, + ), + content: update_message_event_on_chain.message.content, + creator_addr: standardize_address( + update_message_event_on_chain.message.creator.as_str(), + ), + creation_timestamp: update_message_event_on_chain + .message + .creation_timestamp + .parse() + .unwrap(), + last_update_timestamp: update_message_event_on_chain + .message + .last_update_timestamp + .parse() + .unwrap(), + last_update_event_idx: event_idx as i64, + }; + Some(ContractEvent::UpdateMessageEvent(message)) + } else { + None + } + } else { + None + } + } + + pub fn from_events(contract_address: &str, events: &[EventPB]) -> Vec { + events + .iter() + .enumerate() + .filter_map(|(idx, event)| Self::from_event(contract_address, idx, event)) + .collect() + } +} diff --git a/templates/indexer-template/indexer/src/db_models/ledger_info.rs b/templates/indexer-template/indexer/src/db_models/ledger_info.rs new file mode 100644 index 00000000..ece08a7a --- /dev/null +++ b/templates/indexer-template/indexer/src/db_models/ledger_info.rs @@ -0,0 +1,22 @@ + +use diesel::{Identifiable, Insertable, OptionalExtension, QueryDsl, Queryable}; +use diesel_async::RunQueryDsl; + +use crate::{schema::ledger_infos, utils::database_utils::DbPoolConnection}; + +#[derive(Debug, Identifiable, Insertable, Queryable)] +#[diesel(table_name = ledger_infos)] +#[diesel(primary_key(chain_id))] +pub struct LedgerInfo { + pub chain_id: i64, +} + +impl LedgerInfo { + pub async fn get(conn: &mut DbPoolConnection<'_>) -> diesel::QueryResult> { + ledger_infos::table + .select(ledger_infos::all_columns) + .first::(conn) + .await + .optional() + } +} diff --git a/templates/indexer-template/indexer/src/db_models/mod.rs b/templates/indexer-template/indexer/src/db_models/mod.rs new file mode 100644 index 00000000..b5db64fa --- /dev/null +++ b/templates/indexer-template/indexer/src/db_models/mod.rs @@ -0,0 +1,3 @@ +pub mod events_models; +pub mod ledger_info; +pub mod processor_status; diff --git a/templates/indexer-template/indexer/src/db_models/processor_status.rs b/templates/indexer-template/indexer/src/db_models/processor_status.rs new file mode 100644 index 00000000..fad2e91c --- /dev/null +++ b/templates/indexer-template/indexer/src/db_models/processor_status.rs @@ -0,0 +1,37 @@ + +use diesel::{AsChangeset, ExpressionMethods, Insertable, OptionalExtension, QueryDsl, Queryable}; +use diesel_async::RunQueryDsl; + +use crate::{schema::processor_status, utils::database_utils::DbPoolConnection}; + +#[derive(AsChangeset, Debug, Insertable)] +#[diesel(table_name = processor_status)] +/// Only tracking the latest version successfully processed +pub struct ProcessorStatus { + pub processor: String, + pub last_success_version: i64, + pub last_transaction_timestamp: Option, +} + +#[derive(AsChangeset, Debug, Queryable)] +#[diesel(table_name = processor_status)] +/// Only tracking the latest version successfully processed +pub struct ProcessorStatusQuery { + pub processor: String, + pub last_success_version: i64, + pub last_updated: chrono::NaiveDateTime, + pub last_transaction_timestamp: Option, +} + +impl ProcessorStatusQuery { + pub async fn get_by_processor( + processor_name: &str, + conn: &mut DbPoolConnection<'_>, + ) -> diesel::QueryResult> { + processor_status::table + .filter(processor_status::processor.eq(processor_name)) + .first::(conn) + .await + .optional() + } +} diff --git a/templates/indexer-template/indexer/src/health_check_server.rs b/templates/indexer-template/indexer/src/health_check_server.rs new file mode 100644 index 00000000..84e7eb43 --- /dev/null +++ b/templates/indexer-template/indexer/src/health_check_server.rs @@ -0,0 +1,42 @@ +//! This contains the health server, a basic server that for now always returns 200. +//! This is necessary to run the processor in Cloud Run, which expects to be able to +//! query a HTTP server to check for liveness. + +use anyhow::{Context, Result}; +use poem::{ + get, handler, http::Method, listener::TcpListener, middleware::Cors, EndpointExt, Route, Server, +}; +use serde::{Deserialize, Serialize}; +use std::net::{Ipv4Addr, SocketAddrV4}; +use tracing::info; + +/// This configures the health server. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(default)] +pub struct HealthServerConfig { + pub listen_address: SocketAddrV4, +} + +impl Default for HealthServerConfig { + fn default() -> Self { + Self { + listen_address: SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 8080), + } + } +} + +pub async fn run(config: HealthServerConfig) -> Result<()> { + info!("Health server starting at {}", config.listen_address); + let cors = Cors::new().allow_methods(vec![Method::GET, Method::POST]); + let route = Route::new().nest("/", get(root)).with(cors); + Server::new(TcpListener::bind(config.listen_address)) + .name("health-server") + .run(route) + .await + .context("Health server stopped running unexpectedly") +} + +#[handler] +async fn root() -> String { + "Hello from the root!!".to_string() +} diff --git a/templates/indexer-template/indexer/src/lib.rs b/templates/indexer-template/indexer/src/lib.rs new file mode 100644 index 00000000..c1de0cad --- /dev/null +++ b/templates/indexer-template/indexer/src/lib.rs @@ -0,0 +1,8 @@ +pub mod config; +pub mod db_models; +pub mod health_check_server; +pub mod processors; +pub mod utils; + +#[path = "db_migrations/schema.rs"] +pub mod schema; diff --git a/templates/indexer-template/indexer/src/main.rs b/templates/indexer-template/indexer/src/main.rs new file mode 100644 index 00000000..ef78f98a --- /dev/null +++ b/templates/indexer-template/indexer/src/main.rs @@ -0,0 +1,38 @@ +use anyhow::Result; +use aptos_indexer_processor_sdk_server_framework::ServerArgs; +use clap::Parser; +use indexer::{ + config::indexer_processor_config::IndexerProcessorConfig, + health_check_server::{self, HealthServerConfig}, +}; + +#[cfg(unix)] +#[global_allocator] +static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; + +async fn run_health_server() -> Result<()> { + health_check_server::run(HealthServerConfig::default()).await +} + +async fn run_indexer() -> Result<()> { + ServerArgs::parse() + .run::(tokio::runtime::Handle::current()) + .await +} + +fn main() -> Result<()> { + let num_cpus = num_cpus::get(); + let worker_threads = (num_cpus).max(16); + + let mut builder = tokio::runtime::Builder::new_multi_thread(); + builder + .disable_lifo_slot() + .enable_all() + .worker_threads(worker_threads) + .build() + .unwrap() + .block_on(async { + tokio::try_join!(run_health_server(), run_indexer())?; + Ok(()) + }) +} diff --git a/templates/indexer-template/indexer/src/processors/events/events_extractor.rs b/templates/indexer-template/indexer/src/processors/events/events_extractor.rs new file mode 100644 index 00000000..36258253 --- /dev/null +++ b/templates/indexer-template/indexer/src/processors/events/events_extractor.rs @@ -0,0 +1,85 @@ +use crate::db_models::events_models::ContractEvent; +use anyhow::Result; +use aptos_indexer_processor_sdk::{ + aptos_protos::transaction::v1::{transaction::TxnData, Transaction}, + traits::{async_step::AsyncRunType, AsyncStep, NamedStep, Processable}, + types::transaction_context::TransactionContext, + utils::errors::ProcessorError, +}; +use async_trait::async_trait; +use rayon::prelude::*; +use tracing::warn; + +/// EventsExtractor is a step that extracts events and their metadata from transactions. +pub struct EventsExtractor +where + Self: Sized + Send + 'static, +{ + contract_address: String, +} + +impl EventsExtractor { + pub fn new(contract_address: String) -> Self { + Self { contract_address } + } +} + +#[async_trait] +impl Processable for EventsExtractor { + type Input = Transaction; + type Output = ContractEvent; + type RunType = AsyncRunType; + + async fn process( + &mut self, + item: TransactionContext, + ) -> Result>, ProcessorError> { + let events = item + .data + .par_iter() + .map(|txn| { + let mut events = vec![]; + let txn_version = txn.version as i64; + let txn_data = match txn.txn_data.as_ref() { + Some(data) => data, + None => { + warn!( + transaction_version = txn_version, + "Transaction data doesn't exist" + ); + return vec![]; + } + }; + let default = vec![]; + let raw_events = match txn_data { + TxnData::BlockMetadata(tx_inner) => &tx_inner.events, + TxnData::Genesis(tx_inner) => &tx_inner.events, + TxnData::User(tx_inner) => &tx_inner.events, + _ => &default, + }; + + let txn_events = + ContractEvent::from_events(self.contract_address.as_str(), raw_events); + events.extend(txn_events); + events + }) + .flatten() + .collect::>(); + Ok(Some(TransactionContext { + data: events, + start_version: item.start_version, + end_version: item.end_version, + start_transaction_timestamp: item.start_transaction_timestamp, + end_transaction_timestamp: item.end_transaction_timestamp, + total_size_in_bytes: item.total_size_in_bytes, + })) + } +} + +impl AsyncStep for EventsExtractor {} + +impl NamedStep for EventsExtractor { + fn name(&self) -> String { + "EventsExtractor".to_string() + } +} diff --git a/templates/indexer-template/indexer/src/processors/events/events_processor.rs b/templates/indexer-template/indexer/src/processors/events/events_processor.rs new file mode 100644 index 00000000..e789bbf7 --- /dev/null +++ b/templates/indexer-template/indexer/src/processors/events/events_processor.rs @@ -0,0 +1,99 @@ +use anyhow::Result; +use aptos_indexer_processor_sdk::{ + aptos_indexer_transaction_stream::{TransactionStream, TransactionStreamConfig}, + builder::ProcessorBuilder, + common_steps::TransactionStreamStep, + traits::IntoRunnableStep, +}; +use tracing::info; + +use super::{events_extractor::EventsExtractor, events_storer::EventsStorer}; +use crate::{ + config::indexer_processor_config::IndexerProcessorConfig, + utils::{ + chain_id::check_or_update_chain_id, database_connection::new_db_pool, + database_utils::ArcDbPool, latest_processed_version_tracker::LatestVersionProcessedTracker, + starting_version::get_starting_version, + }, +}; + +pub struct EventsProcessor { + pub config: IndexerProcessorConfig, + pub db_pool: ArcDbPool, +} + +impl EventsProcessor { + pub async fn new(config: IndexerProcessorConfig) -> Result { + let conn_pool = new_db_pool( + &config.db_config.postgres_connection_string, + config.db_config.db_pool_size, + ) + .await + .expect("Failed to create connection pool"); + + Ok(Self { + config, + db_pool: conn_pool, + }) + } + + pub async fn run_processor(self) -> Result<()> { + // Merge the starting version from config and the latest processed version from the DB + let starting_version = get_starting_version(&self.config, self.db_pool.clone()).await?; + + info!( + "Starting events processor with starting version: {:?}", + starting_version + ); + + // Check and update the ledger chain id to ensure we're indexing the correct chain + let grpc_chain_id = TransactionStream::new(self.config.transaction_stream_config.clone()) + .await? + .get_chain_id() + .await?; + check_or_update_chain_id(grpc_chain_id as i64, self.db_pool.clone()).await?; + + // Define processor steps + let transaction_stream = TransactionStreamStep::new(TransactionStreamConfig { + starting_version: Some(starting_version), + ..self.config.transaction_stream_config + }) + .await?; + let events_extractor = EventsExtractor::new(self.config.contract_config.contract_address); + let events_storer = EventsStorer::new(self.db_pool.clone()); + let version_tracker = LatestVersionProcessedTracker::new( + self.config.db_config, + starting_version, + self.config.processor_config.name().to_string(), + ) + .await?; + + // Connect processor steps together + let (_, buffer_receiver) = ProcessorBuilder::new_with_inputless_first_step( + transaction_stream.into_runnable_step(), + ) + .connect_to(events_extractor.into_runnable_step(), 10) + .connect_to(events_storer.into_runnable_step(), 10) + .connect_to(version_tracker.into_runnable_step(), 10) + .end_and_return_output_receiver(10); + + // (Optional) Parse the results + loop { + match buffer_receiver.recv().await { + Ok(txn_context) => { + if txn_context.data.is_empty() { + continue; + } + info!( + "Finished processing events from versions [{:?}, {:?}]", + txn_context.start_version, txn_context.end_version, + ); + } + Err(_) => { + info!("Channel is closed"); + return Ok(()); + } + } + } + } +} diff --git a/templates/indexer-template/indexer/src/processors/events/events_storer.rs b/templates/indexer-template/indexer/src/processors/events/events_storer.rs new file mode 100644 index 00000000..7f6bfe8e --- /dev/null +++ b/templates/indexer-template/indexer/src/processors/events/events_storer.rs @@ -0,0 +1,80 @@ +use ahash::AHashMap; +use anyhow::Result; +use aptos_indexer_processor_sdk::{ + traits::{async_step::AsyncRunType, AsyncStep, NamedStep, Processable}, + types::transaction_context::TransactionContext, + utils::errors::ProcessorError, +}; +use async_trait::async_trait; + +use super::storers::{ + create_message_event_storer::process_create_message_events, + update_message_event_storer::process_update_message_events, +}; +use crate::{db_models::events_models::ContractEvent, utils::database_utils::ArcDbPool}; + +/// EventsStorer is a step that inserts events in the database. +pub struct EventsStorer +where + Self: Sized + Send + 'static, +{ + pool: ArcDbPool, +} + +impl AsyncStep for EventsStorer {} + +impl NamedStep for EventsStorer { + fn name(&self) -> String { + "EventsStorer".to_string() + } +} + +impl EventsStorer { + pub fn new(pool: ArcDbPool) -> Self { + Self { pool } + } +} + +#[async_trait] +impl Processable for EventsStorer { + type Input = ContractEvent; + type Output = ContractEvent; + type RunType = AsyncRunType; + + async fn process( + &mut self, + events: TransactionContext, + ) -> Result>, ProcessorError> { + let per_table_chunk_sizes: AHashMap = AHashMap::new(); + let (create_events, update_events) = events.clone().data.into_iter().fold( + (vec![], vec![]), + |(mut create_events, mut update_events), event| { + match event { + ContractEvent::CreateMessageEvent(message) => { + create_events.push(message); + } + ContractEvent::UpdateMessageEvent(message) => { + update_events.push(message); + } + } + (create_events, update_events) + }, + ); + + process_create_message_events( + self.pool.clone(), + per_table_chunk_sizes.clone(), + create_events, + ) + .await?; + + process_update_message_events( + self.pool.clone(), + per_table_chunk_sizes.clone(), + update_events, + ) + .await?; + + Ok(Some(events)) + } +} diff --git a/templates/indexer-template/indexer/src/processors/events/mod.rs b/templates/indexer-template/indexer/src/processors/events/mod.rs new file mode 100644 index 00000000..e42c4b61 --- /dev/null +++ b/templates/indexer-template/indexer/src/processors/events/mod.rs @@ -0,0 +1,4 @@ +pub mod events_extractor; +pub mod events_processor; +pub mod events_storer; +pub mod storers; diff --git a/templates/indexer-template/indexer/src/processors/events/storers/create_message_event_storer.rs b/templates/indexer-template/indexer/src/processors/events/storers/create_message_event_storer.rs new file mode 100644 index 00000000..20b3c7ae --- /dev/null +++ b/templates/indexer-template/indexer/src/processors/events/storers/create_message_event_storer.rs @@ -0,0 +1,48 @@ +use ahash::AHashMap; +use anyhow::Result; +use aptos_indexer_processor_sdk::utils::errors::ProcessorError; +use diesel::{pg::Pg, query_builder::QueryFragment}; +use tracing::error; + +use crate::{ + db_models::events_models::Message, + schema::messages, + utils::{ + database_execution::execute_in_chunks, + database_utils::{get_config_table_chunk_size, ArcDbPool}, + }, +}; + +fn create_message_events_sql( + items_to_insert: Vec, +) -> Vec + diesel::query_builder::QueryId + Send> { + let query = diesel::insert_into(messages::table) + .values(items_to_insert) + .on_conflict(messages::message_obj_addr) + .do_nothing(); + vec![query] +} + +pub async fn process_create_message_events( + pool: ArcDbPool, + per_table_chunk_sizes: AHashMap, + create_events: Vec, +) -> Result<(), ProcessorError> { + let create_result = execute_in_chunks( + pool.clone(), + create_message_events_sql, + &create_events, + get_config_table_chunk_size::("messages", &per_table_chunk_sizes), + ) + .await; + + match create_result { + Ok(_) => Ok(()), + Err(e) => { + error!("Failed to store create message events: {:?}", e); + Err(ProcessorError::ProcessError { + message: e.to_string(), + }) + } + } +} diff --git a/templates/indexer-template/indexer/src/processors/events/storers/mod.rs b/templates/indexer-template/indexer/src/processors/events/storers/mod.rs new file mode 100644 index 00000000..ffe03917 --- /dev/null +++ b/templates/indexer-template/indexer/src/processors/events/storers/mod.rs @@ -0,0 +1,2 @@ +pub mod create_message_event_storer; +pub mod update_message_event_storer; diff --git a/templates/indexer-template/indexer/src/processors/events/storers/update_message_event_storer.rs b/templates/indexer-template/indexer/src/processors/events/storers/update_message_event_storer.rs new file mode 100644 index 00000000..f2719257 --- /dev/null +++ b/templates/indexer-template/indexer/src/processors/events/storers/update_message_event_storer.rs @@ -0,0 +1,92 @@ +use ahash::AHashMap; +use anyhow::Result; +use aptos_indexer_processor_sdk::utils::errors::ProcessorError; +use diesel::{ + pg::Pg, query_builder::QueryFragment, query_dsl::methods::FilterDsl, upsert::excluded, + BoolExpressionMethods, ExpressionMethods, +}; +use tracing::error; + +use crate::{ + db_models::events_models::Message, + schema::messages, + utils::{ + database_execution::execute_in_chunks, + database_utils::{get_config_table_chunk_size, ArcDbPool}, + }, +}; + +fn update_message_events_sql( + items_to_insert: Vec, +) -> Vec + diesel::query_builder::QueryId + Send> { + let query = diesel::insert_into(messages::table) + .values(items_to_insert) + .on_conflict(messages::message_obj_addr) + .do_update() + .set(( + messages::message_obj_addr.eq(excluded(messages::message_obj_addr)), + messages::creator_addr.eq(excluded(messages::creator_addr)), + messages::creation_timestamp.eq(excluded(messages::creation_timestamp)), + messages::last_update_timestamp.eq(excluded(messages::last_update_timestamp)), + messages::last_update_event_idx.eq(excluded(messages::last_update_event_idx)), + messages::content.eq(excluded(messages::content)), + )) + .filter( + // Update only if the last update timestamp is greater than the existing one + // or if the last update timestamp is the same but the event index is greater + messages::last_update_timestamp + .lt(excluded(messages::last_update_timestamp)) + .or(messages::last_update_timestamp + .eq(excluded(messages::last_update_timestamp)) + .and( + messages::last_update_event_idx + .lt(excluded(messages::last_update_event_idx)), + )), + ); + + vec![query] +} + +pub async fn process_update_message_events( + pool: ArcDbPool, + per_table_chunk_sizes: AHashMap, + update_events: Vec, +) -> Result<(), ProcessorError> { + // filter update_events so when there are 2 events updating the same record, only the latest one is sent to DB for update + // because we cannot update one record with 2 different values in the same transaction + let mut filtered_update_events_map: AHashMap = AHashMap::new(); + for message in update_events { + filtered_update_events_map + .entry(message.message_obj_addr.clone()) + .and_modify(|existing| { + if (message.last_update_timestamp, message.last_update_event_idx) + > ( + existing.last_update_timestamp, + existing.last_update_event_idx, + ) + { + *existing = message.clone(); + } + }) + .or_insert(message); + } + let filtered_update_events: Vec = filtered_update_events_map.into_values().collect(); + + let update_result = execute_in_chunks( + pool.clone(), + update_message_events_sql, + &filtered_update_events, + get_config_table_chunk_size::("messages", &per_table_chunk_sizes), + ) + .await; + + match update_result { + Ok(_) => Ok(()), + Err(e) => { + error!("Failed to store update message events: {:?}", e); + Err(ProcessorError::ProcessError { + message: e.to_string(), + }) + } + } +} diff --git a/templates/indexer-template/indexer/src/processors/mod.rs b/templates/indexer-template/indexer/src/processors/mod.rs new file mode 100644 index 00000000..a9970c28 --- /dev/null +++ b/templates/indexer-template/indexer/src/processors/mod.rs @@ -0,0 +1 @@ +pub mod events; diff --git a/templates/indexer-template/indexer/src/utils/chain_id.rs b/templates/indexer-template/indexer/src/utils/chain_id.rs new file mode 100644 index 00000000..371cad29 --- /dev/null +++ b/templates/indexer-template/indexer/src/utils/chain_id.rs @@ -0,0 +1,49 @@ +use anyhow::{Context, Result}; +use tracing::info; + +use super::database_utils::ArcDbPool; +use crate::{ + db_models::ledger_info::LedgerInfo, schema::ledger_infos, + utils::database_execution::execute_with_better_error_conn, +}; + +/// Verify the chain id from GRPC against the database. +pub async fn check_or_update_chain_id(grpc_chain_id: i64, db_pool: ArcDbPool) -> Result { + info!("Checking if chain id is correct"); + + let mut conn = db_pool + .get() + .await + .expect("Failed to get connection from pool while checking or updating chain id"); + + let maybe_existing_chain_id = LedgerInfo::get(&mut conn) + .await + .expect("Failed to get chain id from db while checking or updating chain id") + .map(|li| li.chain_id); + + match maybe_existing_chain_id { + Some(chain_id) => { + anyhow::ensure!(chain_id == grpc_chain_id, "Wrong chain detected! Trying to index chain {} now but existing data is for chain {}", grpc_chain_id, chain_id); + info!( + chain_id = chain_id, + "Chain id matches! Continue to index...", + ); + Ok(chain_id as u64) + } + None => { + info!( + chain_id = grpc_chain_id, + "Adding chain id to db, continue to index..." + ); + let query = diesel::insert_into(ledger_infos::table) + .values(LedgerInfo { + chain_id: grpc_chain_id, + }) + .on_conflict_do_nothing(); + execute_with_better_error_conn(&mut conn, vec![query]) + .await + .context("Error updating chain_id!") + .map(|_| grpc_chain_id as u64) + } + } +} diff --git a/templates/indexer-template/indexer/src/utils/database_connection.rs b/templates/indexer-template/indexer/src/utils/database_connection.rs new file mode 100644 index 00000000..ab983d14 --- /dev/null +++ b/templates/indexer-template/indexer/src/utils/database_connection.rs @@ -0,0 +1,77 @@ +use diesel::ConnectionResult; +use diesel_async::{ + pooled_connection::{bb8::Pool, AsyncDieselConnectionManager, ManagerConfig, PoolError}, + AsyncPgConnection, +}; +use futures_util::{future::BoxFuture, FutureExt}; +use std::sync::Arc; + +use super::database_utils::{ArcDbPool, MyDbConnection}; + +fn establish_connection(database_url: &str) -> BoxFuture> { + use native_tls::{Certificate, TlsConnector}; + use postgres_native_tls::MakeTlsConnector; + + (async move { + let (url, cert_path) = parse_and_clean_db_url(database_url); + let connector = match cert_path { + Some(cert_path) => { + let cert = std::fs::read(cert_path).expect("Could not read certificate"); + + let cert = Certificate::from_pem(&cert).expect("Could not parse certificate"); + let connector = TlsConnector::builder() + .danger_accept_invalid_certs(true) + .add_root_certificate(cert) + .build() + .expect("Could not build TLS connector"); + MakeTlsConnector::new(connector) + } + None => { + let connector = TlsConnector::builder() + .build() + .expect("Could not build default TLS connector"); + MakeTlsConnector::new(connector) + } + }; + let (client, connection) = tokio_postgres::connect(&url, connector) + .await + .expect("Could not connect to database"); + tokio::spawn(async move { + if let Err(e) = connection.await { + eprintln!("connection error: {}", e); + } + }); + AsyncPgConnection::try_from(client).await + }) + .boxed() +} + +fn parse_and_clean_db_url(url: &str) -> (String, Option) { + let mut db_url = url::Url::parse(url).expect("Could not parse database url"); + let mut cert_path = None; + + let mut query = "".to_string(); + db_url.query_pairs().for_each(|(k, v)| { + if k == "sslrootcert" { + cert_path = Some(v.parse().unwrap()); + } else { + query.push_str(&format!("{}={}&", k, v)); + } + }); + db_url.set_query(Some(&query)); + + (db_url.to_string(), cert_path) +} + +pub async fn new_db_pool(database_url: &str, max_pool_size: u32) -> Result { + let mut config = ManagerConfig::::default(); + config.custom_setup = Box::new(|conn| Box::pin(establish_connection(conn))); + let manager = + AsyncDieselConnectionManager::::new_with_config(database_url, config); + + let pool = Pool::builder() + .max_size(max_pool_size) + .build(manager) + .await?; + Ok(Arc::new(pool)) +} diff --git a/templates/indexer-template/indexer/src/utils/database_execution.rs b/templates/indexer-template/indexer/src/utils/database_execution.rs new file mode 100644 index 00000000..09c1ecbd --- /dev/null +++ b/templates/indexer-template/indexer/src/utils/database_execution.rs @@ -0,0 +1,106 @@ +use diesel::{query_builder::QueryFragment, QueryResult}; +use diesel_async::{AsyncConnection, RunQueryDsl}; +use tracing::{debug, warn}; + +use super::database_utils::{clean_data_for_db, ArcDbPool, Backend, MyDbConnection}; + +pub async fn execute_in_chunks( + pool: ArcDbPool, + build_queries: fn(Vec) -> Vec, + items_to_insert: &[T], + chunk_size: usize, +) -> Result<(), diesel::result::Error> +where + U: QueryFragment + diesel::query_builder::QueryId + Send + 'static, + T: serde::Serialize + for<'de> serde::Deserialize<'de> + Clone + Send + 'static, +{ + let tasks = items_to_insert + .chunks(chunk_size) + .map(|chunk| { + let pool = pool.clone(); + let items = chunk.to_vec(); + tokio::spawn(async move { + let queries = build_queries(items.clone()); + execute_or_retry_cleaned(pool, build_queries, items, queries).await + }) + }) + .collect::>(); + + let results = futures_util::future::try_join_all(tasks) + .await + .expect("Task panicked executing in chunks"); + for res in results { + res? + } + + Ok(()) +} + +pub async fn execute_or_retry_cleaned( + pool: ArcDbPool, + build_queries: fn(Vec) -> Vec, + items: Vec, + queries: Vec, +) -> Result<(), diesel::result::Error> +where + U: QueryFragment + diesel::query_builder::QueryId + Send, + T: serde::Serialize + for<'de> serde::Deserialize<'de> + Clone, +{ + match execute_with_better_error(pool.clone(), queries).await { + Ok(_) => {} + Err(_) => { + let cleaned_items = clean_data_for_db(items, true); + let cleaned_queries = build_queries(cleaned_items); + match execute_with_better_error(pool.clone(), cleaned_queries).await { + Ok(_) => {} + Err(e) => { + return Err(e); + } + } + } + } + Ok(()) +} + +pub async fn execute_with_better_error(pool: ArcDbPool, queries: Vec) -> QueryResult<()> +where + U: QueryFragment + diesel::query_builder::QueryId + Send, +{ + let conn = &mut pool.get().await.map_err(|e| { + warn!("Error getting connection from pool: {:?}", e); + diesel::result::Error::DatabaseError( + diesel::result::DatabaseErrorKind::UnableToSendCommand, + Box::new(e.to_string()), + ) + })?; + + execute_with_better_error_conn(conn, queries).await +} + +pub async fn execute_with_better_error_conn( + conn: &mut MyDbConnection, + queries: Vec, +) -> QueryResult<()> +where + U: QueryFragment + diesel::query_builder::QueryId + Send, +{ + let debug_query = queries + .iter() + .map(|q| diesel::debug_query::(q).to_string()) + .collect::>(); + debug!("Executing queries in one DB transaction atomically: {:?}", debug_query); + let res = conn + .transaction(|conn| { + Box::pin(async move { + for q in queries { + q.execute(conn).await?; + } + Ok(()) + }) + }) + .await; + if let Err(ref e) = res { + warn!("Error running query: {:?}\n{:?}", e, debug_query); + } + res +} diff --git a/templates/indexer-template/indexer/src/utils/database_utils.rs b/templates/indexer-template/indexer/src/utils/database_utils.rs new file mode 100644 index 00000000..d188207a --- /dev/null +++ b/templates/indexer-template/indexer/src/utils/database_utils.rs @@ -0,0 +1,42 @@ +use ahash::AHashMap; +use aptos_indexer_processor_sdk::utils::convert::remove_null_bytes; +use diesel_async::{ + pooled_connection::bb8::{Pool, PooledConnection}, + AsyncPgConnection, +}; +use std::sync::Arc; + +pub type Backend = diesel::pg::Pg; +pub type MyDbConnection = AsyncPgConnection; +pub type DbPool = Pool; +pub type ArcDbPool = Arc; +pub type DbPoolConnection<'a> = PooledConnection<'a, MyDbConnection>; + +// the max is actually u16::MAX but we see that when the size is too big we get an overflow error so reducing it a bit +const MAX_DIESEL_PARAM_SIZE: usize = (u16::MAX / 2) as usize; + +/// Returns the entry for the config hashmap, or the default field count for the insert +/// Given diesel has a limit of how many parameters can be inserted in a single operation (u16::MAX), +/// we default to chunk an array of items based on how many columns are in the table. +pub fn get_config_table_chunk_size( + table_name: &str, + per_table_chunk_sizes: &AHashMap, +) -> usize { + per_table_chunk_sizes + .get(table_name) + .copied() + .unwrap_or_else(|| MAX_DIESEL_PARAM_SIZE / T::field_count()) +} + +/// This function will clean the data for postgres. Currently it has support for removing +/// null bytes from strings but in the future we will add more functionality. +pub fn clean_data_for_db serde::Deserialize<'de>>( + items: Vec, + should_remove_null_bytes: bool, +) -> Vec { + if should_remove_null_bytes { + items.iter().map(remove_null_bytes).collect() + } else { + items + } +} diff --git a/templates/indexer-template/indexer/src/utils/latest_processed_version_tracker.rs b/templates/indexer-template/indexer/src/utils/latest_processed_version_tracker.rs new file mode 100644 index 00000000..32105603 --- /dev/null +++ b/templates/indexer-template/indexer/src/utils/latest_processed_version_tracker.rs @@ -0,0 +1,199 @@ +use ahash::AHashMap; +use anyhow::{Context, Result}; +use aptos_indexer_processor_sdk::{ + traits::{NamedStep, PollableAsyncRunType, PollableAsyncStep, Processable}, + types::transaction_context::TransactionContext, + utils::{errors::ProcessorError, time::parse_timestamp}, +}; +use async_trait::async_trait; +use diesel::{query_dsl::methods::FilterDsl, upsert::excluded, ExpressionMethods}; + +use super::{ + database_connection::new_db_pool, database_execution::execute_with_better_error, + database_utils::ArcDbPool, +}; +use crate::{ + config::indexer_processor_config::DbConfig, db_models::processor_status::ProcessorStatus, + schema::processor_status, +}; + +const UPDATE_PROCESSOR_STATUS_SECS: u64 = 1; + +pub struct LatestVersionProcessedTracker +where + Self: Sized + Send + 'static, + T: Send + 'static, +{ + pool: ArcDbPool, + tracker_name: String, + // Next version to process that we expect. + next_version: u64, + // Last successful batch of sequentially processed transactions. Includes metadata to write to storage. + last_success_batch: Option>, + // Tracks all the versions that have been processed out of order. + seen_versions: AHashMap>, +} + +impl LatestVersionProcessedTracker +where + Self: Sized + Send + 'static, + T: Send + 'static, +{ + pub async fn new( + db_config: DbConfig, + starting_version: u64, + tracker_name: String, + ) -> Result { + let pool = new_db_pool( + &db_config.postgres_connection_string, + db_config.db_pool_size, + ) + .await + .context("Failed to create connection pool")?; + Ok(Self { + pool, + tracker_name, + next_version: starting_version, + last_success_batch: None, + seen_versions: AHashMap::new(), + }) + } + + fn update_last_success_batch(&mut self, current_batch: TransactionContext) { + let mut new_prev_batch = current_batch; + // While there are batches in seen_versions that are in order, update the new_prev_batch to the next batch. + while let Some(next_version) = self.seen_versions.remove(&(new_prev_batch.end_version + 1)) + { + new_prev_batch = next_version; + } + self.next_version = new_prev_batch.end_version + 1; + self.last_success_batch = Some(new_prev_batch); + } + + async fn save_processor_status(&mut self) -> Result<(), ProcessorError> { + // Update the processor status + if let Some(last_success_batch) = self.last_success_batch.as_ref() { + let end_timestamp = last_success_batch + .end_transaction_timestamp + .as_ref() + .map(|t| parse_timestamp(t, last_success_batch.end_version as i64)) + .map(|t| t.naive_utc()); + let status = ProcessorStatus { + processor: self.tracker_name.clone(), + last_success_version: last_success_batch.end_version as i64, + last_transaction_timestamp: end_timestamp, + }; + let query = diesel::insert_into(processor_status::table) + .values(&status) + .on_conflict(processor_status::processor) + .do_update() + .set(( + processor_status::last_success_version + .eq(excluded(processor_status::last_success_version)), + processor_status::last_updated.eq(excluded(processor_status::last_updated)), + processor_status::last_transaction_timestamp + .eq(excluded(processor_status::last_transaction_timestamp)), + )) + .filter( + processor_status::last_success_version + .lt(excluded(processor_status::last_success_version)), + ); + execute_with_better_error(self.pool.clone(), vec![query]) + .await + .map_err(|e| ProcessorError::DBStoreError { + message: format!("Failed to update processor status: {}", e), + })?; + } + Ok(()) + } +} + +#[async_trait] +impl Processable for LatestVersionProcessedTracker +where + Self: Sized + Send + 'static, + T: Send + 'static, +{ + type Input = T; + type Output = T; + type RunType = PollableAsyncRunType; + + async fn process( + &mut self, + current_batch: TransactionContext, + ) -> Result>, ProcessorError> { + // If there's a gap in the next_version and current_version, save the current_version to seen_versions for + // later processing. + if self.next_version != current_batch.start_version { + tracing::debug!( + next_version = self.next_version, + step = self.name(), + "Gap detected starting from version: {}", + current_batch.start_version + ); + self.seen_versions.insert( + current_batch.start_version, + TransactionContext { + data: vec![], // No data is needed for tracking. This is to avoid clone. + start_version: current_batch.start_version, + end_version: current_batch.end_version, + start_transaction_timestamp: current_batch.start_transaction_timestamp.clone(), + end_transaction_timestamp: current_batch.end_transaction_timestamp.clone(), + total_size_in_bytes: current_batch.total_size_in_bytes, + }, + ); + } else { + tracing::debug!("No gap detected"); + // If the current_batch is the next expected version, update the last success batch + self.update_last_success_batch(TransactionContext { + data: vec![], // No data is needed for tracking. This is to avoid clone. + start_version: current_batch.start_version, + end_version: current_batch.end_version, + start_transaction_timestamp: current_batch.start_transaction_timestamp.clone(), + end_transaction_timestamp: current_batch.end_transaction_timestamp.clone(), + total_size_in_bytes: current_batch.total_size_in_bytes, + }); + } + // Pass through + Ok(Some(current_batch)) + } + + async fn cleanup( + &mut self, + ) -> Result>>, ProcessorError> { + // If processing or polling ends, save the last successful batch to the database. + self.save_processor_status().await?; + Ok(None) + } +} + +#[async_trait] +impl PollableAsyncStep for LatestVersionProcessedTracker +where + Self: Sized + Send + Sync + 'static, + T: Send + 'static, +{ + fn poll_interval(&self) -> std::time::Duration { + std::time::Duration::from_secs(UPDATE_PROCESSOR_STATUS_SECS) + } + + async fn poll(&mut self) -> Result>>, ProcessorError> { + // TODO: Add metrics for gap count + self.save_processor_status().await?; + // Nothing should be returned + Ok(None) + } +} + +impl NamedStep for LatestVersionProcessedTracker +where + Self: Sized + Send + 'static, + T: Send + 'static, +{ + fn name(&self) -> String { + format!( + "LatestVersionProcessedTracker: {}", + std::any::type_name::() + ) + } +} diff --git a/templates/indexer-template/indexer/src/utils/mod.rs b/templates/indexer-template/indexer/src/utils/mod.rs new file mode 100644 index 00000000..1a5c5031 --- /dev/null +++ b/templates/indexer-template/indexer/src/utils/mod.rs @@ -0,0 +1,6 @@ +pub mod chain_id; +pub mod database_connection; +pub mod database_execution; +pub mod database_utils; +pub mod latest_processed_version_tracker; +pub mod starting_version; diff --git a/templates/indexer-template/indexer/src/utils/starting_version.rs b/templates/indexer-template/indexer/src/utils/starting_version.rs new file mode 100644 index 00000000..784c517b --- /dev/null +++ b/templates/indexer-template/indexer/src/utils/starting_version.rs @@ -0,0 +1,43 @@ +use anyhow::{Context, Result}; + +use super::database_utils::ArcDbPool; +use crate::{ + config::indexer_processor_config::IndexerProcessorConfig, + db_models::processor_status::ProcessorStatusQuery, +}; + +pub async fn get_starting_version( + indexer_processor_config: &IndexerProcessorConfig, + conn_pool: ArcDbPool, +) -> Result { + let starting_version_from_config = indexer_processor_config + .transaction_stream_config + .starting_version + .unwrap_or(0); + + let latest_processed_version_from_db = + get_latest_processed_version_from_db(indexer_processor_config, conn_pool) + .await + .context("Failed to get latest processed version from DB")? + .unwrap_or(0); + + Ok(starting_version_from_config.max(latest_processed_version_from_db)) +} + +/// Gets the start version for the processor. If not found, start from 0. +pub async fn get_latest_processed_version_from_db( + indexer_processor_config: &IndexerProcessorConfig, + conn_pool: ArcDbPool, +) -> Result> { + let mut conn = conn_pool.get().await?; + + match ProcessorStatusQuery::get_by_processor( + indexer_processor_config.processor_config.name(), + &mut conn, + ) + .await? + { + Some(status) => Ok(Some(status.last_success_version as u64 + 1)), + None => Ok(None), + } +} diff --git a/templates/indexer-template/next-app/.eslintrc.json b/templates/indexer-template/next-app/.eslintrc.json new file mode 100644 index 00000000..bffb357a --- /dev/null +++ b/templates/indexer-template/next-app/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/templates/indexer-template/next-app/.example.env b/templates/indexer-template/next-app/.example.env new file mode 100644 index 00000000..280cabbb --- /dev/null +++ b/templates/indexer-template/next-app/.example.env @@ -0,0 +1 @@ +POSTGRES_URL="" diff --git a/templates/indexer-template/next-app/.gitignore b/templates/indexer-template/next-app/.gitignore new file mode 100644 index 00000000..00bba9bb --- /dev/null +++ b/templates/indexer-template/next-app/.gitignore @@ -0,0 +1,37 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local +.env + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/templates/indexer-template/next-app/README.md b/templates/indexer-template/next-app/README.md new file mode 100644 index 00000000..06e951b8 --- /dev/null +++ b/templates/indexer-template/next-app/README.md @@ -0,0 +1,67 @@ +# Aptos full stack template UI + +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Local development + +This template uses `@vercel/postgres` to connect to a Postgres database. When testing locally, it cannot connect to local DB. You have to either use a cloud DB on Vercel or a local DB in Docker. I highly recommend using an independent cloud DB on Vercel. You can learn more on [Vercel docs](https://vercel.com/docs/storage/vercel-postgres/local-development). + +## Create a read only user in DB + +This frontend should only read from the DB, the indexer is the only one that writes to the DB. So, create a read only user in the DB for frontend to use for safety. + +```sql +-- Create a readonly user +-- Please don't use any special characters in the password to avoid @vercel/postgres give invalid connection string error +CREATE USER readonly WITH PASSWORD 'strong_password' +-- Grant readonly user read access to all tables in public schema +GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly; +-- Grant readonly user read access to all future tables in public schema +ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly; +``` + +Some useful SQLs to check the user and schema: + +```sql +-- Get all users +SELECT * FROM pg_user; +-- Get all schemas +SELECT schema_name FROM information_schema.schemata; +``` + +Then fill the `POSTGRES_URL` in `.env` file with the readonly user. + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/templates/indexer-template/next-app/components.json b/templates/indexer-template/next-app/components.json new file mode 100644 index 00000000..15695861 --- /dev/null +++ b/templates/indexer-template/next-app/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} diff --git a/templates/indexer-template/next-app/next.config.mjs b/templates/indexer-template/next-app/next.config.mjs new file mode 100644 index 00000000..4678774e --- /dev/null +++ b/templates/indexer-template/next-app/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/templates/indexer-template/next-app/package.json b/templates/indexer-template/next-app/package.json new file mode 100644 index 00000000..9011684d --- /dev/null +++ b/templates/indexer-template/next-app/package.json @@ -0,0 +1,54 @@ +{ + "name": "next-app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@aptos-labs/ts-sdk": "^1.22.2", + "@aptos-labs/wallet-adapter-react": "^3.5.9", + "@hookform/resolvers": "^3.7.0", + "@radix-ui/react-checkbox": "^1.1.1", + "@radix-ui/react-collapsible": "^1.0.3", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-radio-group": "^1.1.3", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-toast": "^1.1.5", + "@radix-ui/react-tooltip": "^1.1.2", + "@tanstack/react-query": "^5.56.2", + "@tanstack/react-table": "^8.19.2", + "@thalalabs/surf": "1.7.3", + "@vercel/postgres": "^0.9.0", + "antd": "^5.1.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "lucide-react": "^0.383.0", + "next": "14.2.3", + "next-themes": "^0.3.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.52.1", + "tailwind-merge": "^2.5.2", + "tailwindcss-animate": "^1.0.7", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "14.2.5", + "postcss": "^8", + "tailwindcss": "^3.4.1", + "typescript": "^5" + } +} diff --git a/templates/indexer-template/next-app/postcss.config.mjs b/templates/indexer-template/next-app/postcss.config.mjs new file mode 100644 index 00000000..1a69fd2a --- /dev/null +++ b/templates/indexer-template/next-app/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + }, +}; + +export default config; diff --git a/templates/indexer-template/next-app/public/next.svg b/templates/indexer-template/next-app/public/next.svg new file mode 100644 index 00000000..5174b28c --- /dev/null +++ b/templates/indexer-template/next-app/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/indexer-template/next-app/public/vercel.svg b/templates/indexer-template/next-app/public/vercel.svg new file mode 100644 index 00000000..d2f84222 --- /dev/null +++ b/templates/indexer-template/next-app/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/indexer-template/next-app/src/app/actions.ts b/templates/indexer-template/next-app/src/app/actions.ts new file mode 100644 index 00000000..a55fdd0d --- /dev/null +++ b/templates/indexer-template/next-app/src/app/actions.ts @@ -0,0 +1,30 @@ +"use server"; + +import { getLastSuccessVersion } from "@/db/getLastSuccessVersion"; +import { GetMessageProps, getMessage } from "@/db/getMessage"; +import { GetMessagesProps, getMessages } from "@/db/getMessages"; +import { MessageBoardColumns, MessageOnUi } from "@/lib/type/message"; + +export const getMessagesOnServer = async ({ + page, + limit, + sortedBy, + order, +}: GetMessagesProps): Promise<{ + messages: MessageBoardColumns[]; + totalMessages: number; +}> => { + return getMessages({ page, limit, sortedBy, order }); +}; + +export const getMessageOnServer = async ({ + messageObjAddr, +}: GetMessageProps): Promise<{ + message: MessageOnUi; +}> => { + return getMessage({ messageObjAddr }); +}; + +export const getLastVersionOnServer = async (): Promise => { + return getLastSuccessVersion(); +}; diff --git a/templates/indexer-template/next-app/src/app/favicon.ico b/templates/indexer-template/next-app/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 zcmeHv30#a{`}aL_*G&7qml|y<+KVaDM2m#dVr!KsA!#An?kSQM(q<_dDNCpjEux83 zLb9Z^XxbDl(w>%i@8hT6>)&Gu{h#Oeyszu?xtw#Zb1mO{pgX9699l+Qppw7jXaYf~-84xW z)w4x8?=youko|}Vr~(D$UXIbiXABHh`p1?nn8Po~fxRJv}|0e(BPs|G`(TT%kKVJAdg5*Z|x0leQq0 zkdUBvb#>9F()jo|T~kx@OM8$9wzs~t2l;K=woNssA3l6|sx2r3+kdfVW@e^8e*E}v zA1y5{bRi+3Z`uD3{F7LgFJDdvm;nJilkzDku>BwXH(8ItVCXk*-lSJnR?-2UN%hJ){&rlvg`CDTj z)Bzo!3v7Ou#83zEDEFcKt(f1E0~=rqeEbTnMvWR#{+9pg%7G8y>u1OVRUSoox-ovF z2Ydma(;=YuBY(eI|04{hXzZD6_f(v~H;C~y5=DhAC{MMS>2fm~1H_t2$56pc$NH8( z5bH|<)71dV-_oCHIrzrT`2s-5w_+2CM0$95I6X8p^r!gHp+j_gd;9O<1~CEQQGS8) zS9Qh3#p&JM-G8rHekNmKVewU;pJRcTAog68KYo^dRo}(M>36U4Us zfgYWSiHZL3;lpWT=zNAW>Dh#mB!_@Lg%$ms8N-;aPqMn+C2HqZgz&9~Eu z4|Kp<`$q)Uw1R?y(~S>ePdonHxpV1#eSP1B;Ogo+-Pk}6#0GsZZ5!||ev2MGdh}_m z{DeR7?0-1^zVs&`AV6Vt;r3`I`OI_wgs*w=eO%_#7Kepl{B@xiyCANc(l zzIyd4y|c6PXWq9-|KM8(zIk8LPk(>a)zyFWjhT!$HJ$qX1vo@d25W<fvZQ2zUz5WRc(UnFMKHwe1| zWmlB1qdbiA(C0jmnV<}GfbKtmcu^2*P^O?MBLZKt|As~ge8&AAO~2K@zbXelK|4T<{|y4`raF{=72kC2Kn(L4YyenWgrPiv z@^mr$t{#X5VuIMeL!7Ab6_kG$&#&5p*Z{+?5U|TZ`B!7llpVmp@skYz&n^8QfPJzL z0G6K_OJM9x+Wu2gfN45phANGt{7=C>i34CV{Xqlx(fWpeAoj^N0Biu`w+MVcCUyU* zDZuzO0>4Z6fbu^T_arWW5n!E45vX8N=bxTVeFoep_G#VmNlQzAI_KTIc{6>c+04vr zx@W}zE5JNSU>!THJ{J=cqjz+4{L4A{Ob9$ZJ*S1?Ggg3klFp!+Y1@K+pK1DqI|_gq z5ZDXVpge8-cs!o|;K73#YXZ3AShj50wBvuq3NTOZ`M&qtjj#GOFfgExjg8Gn8>Vq5 z`85n+9|!iLCZF5$HJ$Iu($dm?8~-ofu}tEc+-pyke=3!im#6pk_Wo8IA|fJwD&~~F zc16osQ)EBo58U7XDuMexaPRjU@h8tXe%S{fA0NH3vGJFhuyyO!Uyl2^&EOpX{9As0 zWj+P>{@}jxH)8|r;2HdupP!vie{sJ28b&bo!8`D^x}TE$%zXNb^X1p@0PJ86`dZyj z%ce7*{^oo+6%&~I!8hQy-vQ7E)0t0ybH4l%KltWOo~8cO`T=157JqL(oq_rC%ea&4 z2NcTJe-HgFjNg-gZ$6!Y`SMHrlj}Etf7?r!zQTPPSv}{so2e>Fjs1{gzk~LGeesX%r(Lh6rbhSo_n)@@G-FTQy93;l#E)hgP@d_SGvyCp0~o(Y;Ee8{ zdVUDbHm5`2taPUOY^MAGOw*>=s7=Gst=D+p+2yON!0%Hk` zz5mAhyT4lS*T3LS^WSxUy86q&GnoHxzQ6vm8)VS}_zuqG?+3td68_x;etQAdu@sc6 zQJ&5|4(I?~3d-QOAODHpZ=hlSg(lBZ!JZWCtHHSj`0Wh93-Uk)_S%zsJ~aD>{`A0~ z9{AG(e|q3g5B%wYKRxiL2Y$8(4w6bzchKuloQW#e&S3n+P- z8!ds-%f;TJ1>)v)##>gd{PdS2Oc3VaR`fr=`O8QIO(6(N!A?pr5C#6fc~Ge@N%Vvu zaoAX2&(a6eWy_q&UwOhU)|P3J0Qc%OdhzW=F4D|pt0E4osw;%<%Dn58hAWD^XnZD= z>9~H(3bmLtxpF?a7su6J7M*x1By7YSUbxGi)Ot0P77`}P3{)&5Un{KD?`-e?r21!4vTTnN(4Y6Lin?UkSM z`MXCTC1@4A4~mvz%Rh2&EwY))LeoT=*`tMoqcEXI>TZU9WTP#l?uFv+@Dn~b(>xh2 z;>B?;Tz2SR&KVb>vGiBSB`@U7VIWFSo=LDSb9F{GF^DbmWAfpms8Sx9OX4CnBJca3 zlj9(x!dIjN?OG1X4l*imJNvRCk}F%!?SOfiOq5y^mZW)jFL@a|r-@d#f7 z2gmU8L3IZq0ynIws=}~m^#@&C%J6QFo~Mo4V`>v7MI-_!EBMMtb%_M&kvAaN)@ZVw z+`toz&WG#HkWDjnZE!6nk{e-oFdL^$YnbOCN}JC&{$#$O27@|Tn-skXr)2ml2~O!5 zX+gYoxhoc7qoU?C^3~&!U?kRFtnSEecWuH0B0OvLodgUAi}8p1 zrO6RSXHH}DMc$&|?D004DiOVMHV8kXCP@7NKB zgaZq^^O<7PoKEp72kby@W0Z!Y*Ay{&vfg#C&gG@YVR9g?FEocMUi1gSN$+V+ayF45{a zuDZDTN}mS|;BO%gEf}pjBfN2-gIrU#G5~cucA;dokXW89%>AyXJJI z9X4UlIWA|ZYHgbI z5?oFk@A=Ik7lrEQPDH!H+b`7_Y~aDb_qa=B2^Y&Ow41cU=4WDd40dp5(QS-WMN-=Y z9g;6_-JdNU;|6cPwf$ak*aJIcwL@1n$#l~zi{c{EW?T;DaW*E8DYq?Umtz{nJ&w-M zEMyTDrC&9K$d|kZe2#ws6)L=7K+{ zQw{XnV6UC$6-rW0emqm8wJoeZK)wJIcV?dST}Z;G0Arq{dVDu0&4kd%N!3F1*;*pW zR&qUiFzK=@44#QGw7k1`3t_d8&*kBV->O##t|tonFc2YWrL7_eqg+=+k;!F-`^b8> z#KWCE8%u4k@EprxqiV$VmmtiWxDLgnGu$Vs<8rppV5EajBXL4nyyZM$SWVm!wnCj-B!Wjqj5-5dNXukI2$$|Bu3Lrw}z65Lc=1G z^-#WuQOj$hwNGG?*CM_TO8Bg-1+qc>J7k5c51U8g?ZU5n?HYor;~JIjoWH-G>AoUP ztrWWLbRNqIjW#RT*WqZgPJXU7C)VaW5}MiijYbABmzoru6EmQ*N8cVK7a3|aOB#O& zBl8JY2WKfmj;h#Q!pN%9o@VNLv{OUL?rixHwOZuvX7{IJ{(EdPpuVFoQqIOa7giLVkBOKL@^smUA!tZ1CKRK}#SSM)iQHk)*R~?M!qkCruaS!#oIL1c z?J;U~&FfH#*98^G?i}pA{ z9Jg36t4=%6mhY(quYq*vSxptes9qy|7xSlH?G=S@>u>Ebe;|LVhs~@+06N<4CViBk zUiY$thvX;>Tby6z9Y1edAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJwb z_^v8bbg`SAn{I*4bH$u(RZ6*xUhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=pC^S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk($?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvhCL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397*_cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111aH}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*IcmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU&68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-=A=yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v#ix45EVrcEhr>!NMhprl$InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~&^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7<4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}scZlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2`1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M=hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S(O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/templates/indexer-template/next-app/src/app/globals.css b/templates/indexer-template/next-app/src/app/globals.css new file mode 100644 index 00000000..1ee84c0c --- /dev/null +++ b/templates/indexer-template/next-app/src/app/globals.css @@ -0,0 +1,92 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + + --radius: 0.5rem; + + --chart-1: 12 76% 61%; + + --chart-2: 173 58% 39%; + + --chart-3: 197 37% 24%; + + --chart-4: 43 74% 66%; + + --chart-5: 27 87% 67%; + } + + .dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; + + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; + + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} + +@layer base { + * { + @apply border-border; + } + + body { + @apply bg-background text-foreground; + } +} diff --git a/templates/indexer-template/next-app/src/app/layout.tsx b/templates/indexer-template/next-app/src/app/layout.tsx new file mode 100644 index 00000000..692dbee5 --- /dev/null +++ b/templates/indexer-template/next-app/src/app/layout.tsx @@ -0,0 +1,56 @@ +import "./globals.css"; + +import { ThemeProvider } from "@/components/providers/ThemeProvider"; +import { WalletProvider } from "@/components/providers/WalletProvider"; +import { Toaster } from "@/components/ui/toaster"; +import { cn } from "@/lib/utils"; +import type { Metadata } from "next"; +import { Inter as FontSans } from "next/font/google"; +import { PropsWithChildren } from "react"; +import { RootHeader } from "@/components/RootHeader"; +import { WrongNetworkAlert } from "@/components/WrongNetworkAlert"; +import { QueryProvider } from "@/components/providers/QueryProvider"; + +const fontSans = FontSans({ + subsets: ["latin"], + variable: "--font-sans", +}); + +export const metadata: Metadata = { + title: "Aptos Wallet Adapter Example", + description: + "An example of how to use Aptos Wallet Adapter with React and Next.js.", +}; + +const RootLayout = ({ children }: PropsWithChildren) => { + return ( + + + + + +
+ + + {children} + +
+
+
+
+ + + ); +}; + +export default RootLayout; diff --git a/templates/indexer-template/next-app/src/app/message/[messageObjAddr]/page.tsx b/templates/indexer-template/next-app/src/app/message/[messageObjAddr]/page.tsx new file mode 100644 index 00000000..bd72750e --- /dev/null +++ b/templates/indexer-template/next-app/src/app/message/[messageObjAddr]/page.tsx @@ -0,0 +1,11 @@ +import { Message } from "@/components/Message"; + +export default function MessagePage({ + params, +}: { + params: { messageObjAddr: `0x${string}` }; +}) { + const { messageObjAddr } = params; + + return ; +} diff --git a/templates/indexer-template/next-app/src/app/page.tsx b/templates/indexer-template/next-app/src/app/page.tsx new file mode 100644 index 00000000..15a3a34e --- /dev/null +++ b/templates/indexer-template/next-app/src/app/page.tsx @@ -0,0 +1,11 @@ +import { MessageBoard } from "@/components/MessageBoard"; +import { SendTransaction } from "@/components/SendTransaction"; + +export default function HomePage() { + return ( + <> + + + + ); +} diff --git a/templates/indexer-template/next-app/src/components/ExplorerLink.tsx b/templates/indexer-template/next-app/src/components/ExplorerLink.tsx new file mode 100644 index 00000000..68975b37 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/ExplorerLink.tsx @@ -0,0 +1,40 @@ +import { NETWORK } from "@/lib/aptos"; + +export interface TransactionOnExplorerProps { + hash: string; +} + +export function TransactionOnExplorer({ hash }: TransactionOnExplorerProps) { + const explorerLink = `https://explorer.aptoslabs.com/txn/${hash}${`?network=${NETWORK}`}`; + return ( + <> + View on Explorer:{" "} + + {explorerLink} + + + ); +} + +export interface ObjectOnExplorerProps { + address: string; +} + +export function ObjectOnExplorer({ address }: ObjectOnExplorerProps) { + const explorerLink = `https://explorer.aptoslabs.com/object/${address}${`?network=${NETWORK}`}`; + return ( + + View on Explorer + + ); +} diff --git a/templates/indexer-template/next-app/src/components/IndexerStatus.tsx b/templates/indexer-template/next-app/src/components/IndexerStatus.tsx new file mode 100644 index 00000000..4978bfe0 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/IndexerStatus.tsx @@ -0,0 +1,78 @@ +"use client"; + +import { useQuery } from "@tanstack/react-query"; + +import { getLastVersionOnServer } from "@/app/actions"; +import { getAptosClient } from "@/lib/aptos"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; + +export const IndexerStatus = () => { + const fetchData = async () => { + const indexerLastVersion = await getLastVersionOnServer(); + const latestBlockHeight = await getAptosClient().view<[number]>({ + payload: { + function: "0x1::block::get_current_block_height", + typeArguments: [], + functionArguments: [], + }, + }); + const block = await getAptosClient().getBlockByHeight({ + blockHeight: latestBlockHeight[0], + }); + const onChainLastVersion = parseInt(block.last_version); + return { + indexerLastVersion, + onChainLastVersion, + }; + }; + + const { data, isLoading, isError, error } = useQuery({ + queryKey: ["last-version"], + queryFn: fetchData, + refetchInterval: 3000, + }); + + if (isLoading || !data) { + return
Loading last indexer version
; + } + + if (isError) { + return
Error getting last indexer version: {error.message}
; + } + + const versionDiff = data.onChainLastVersion - data.indexerLastVersion; + const isHealthy = versionDiff < 100; + + return ( + + + +
+ {isHealthy ? "Indexer up to date" : "Indexer lagging"} +
+
+
+ +
+

Indexer Version: {data.indexerLastVersion}

+

On-Chain Version: {data.onChainLastVersion}

+

Difference: {versionDiff}

+

+ When the difference is greater than 100, the indexer is considered + lagging. +

+
+
+
+
+ ); +}; diff --git a/templates/indexer-template/next-app/src/components/LabelValueGrid.tsx b/templates/indexer-template/next-app/src/components/LabelValueGrid.tsx new file mode 100644 index 00000000..d7c44036 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/LabelValueGrid.tsx @@ -0,0 +1,50 @@ +import { Fragment, ReactNode } from "react"; + +export interface LabelValueGridProps { + items: Array<{ label: string; subLabel?: string; value: ReactNode }>; +} + +export function LabelValueGrid({ items }: LabelValueGridProps) { + return ( +
+ {items.map(({ label, subLabel, value }) => ( + +
+
{label}
+ {subLabel &&
{subLabel}
} +
+ {value} +
+ ))} +
+ ); +} + +export interface DisplayValueProps { + value: string; + isCorrect: boolean; + expected?: string; +} + +export function DisplayValue({ + value, + isCorrect, + expected, +}: DisplayValueProps) { + return ( +
+

+ {value} +

+ {!isCorrect && expected ? ( +

Expected: {expected}

+ ) : null} +
+ ); +} diff --git a/templates/indexer-template/next-app/src/components/Message.tsx b/templates/indexer-template/next-app/src/components/Message.tsx new file mode 100644 index 00000000..a1473b86 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/Message.tsx @@ -0,0 +1,96 @@ +"use client"; + +import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; +import { LabelValueGrid } from "@/components/LabelValueGrid"; +import { NETWORK } from "@/lib/aptos"; +import { useEffect, useState } from "react"; +import { MessageOnUi } from "@/lib/type/message"; +import { getMessageOnServer } from "@/app/actions"; + +interface MessageProps { + messageObjAddr: `0x${string}`; +} + +export function Message({ messageObjAddr }: MessageProps) { + const [message, setMessage] = useState(); + + useEffect(() => { + getMessageOnServer({ messageObjAddr }).then(({ message }) => { + setMessage(message); + }); + }, [messageObjAddr]); + + if (!message) { + return <>Loading...; + } + + return ( + + + Message + + +
+ + + {message.message_obj_addr} + +

+ ), + }, + { + label: "Creator address", + value: ( +

+ + {message.creator_addr} + +

+ ), + }, + { + label: "Creation timestamp", + value: ( +

+ {new Date( + message.creation_timestamp * 1000 + ).toLocaleString()} +

+ ), + }, + { + label: "Last update timestamp", + value: ( +

+ {new Date( + message.last_update_timestamp * 1000 + ).toLocaleString()} +

+ ), + }, + { + label: "Content", + value:

{message.content}

, + }, + ]} + /> +
+
+
+ ); +} diff --git a/templates/indexer-template/next-app/src/components/MessageBoard.tsx b/templates/indexer-template/next-app/src/components/MessageBoard.tsx new file mode 100644 index 00000000..d6ae45bd --- /dev/null +++ b/templates/indexer-template/next-app/src/components/MessageBoard.tsx @@ -0,0 +1,16 @@ +import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; +import { DataTable } from "@/components/message-board/data-table"; +import { columns } from "@/components/message-board/columns"; + +export const MessageBoard = async () => { + return ( + + + Message Board + + + + + + ); +}; diff --git a/templates/indexer-template/next-app/src/components/PostMessageWithSurf.tsx b/templates/indexer-template/next-app/src/components/PostMessageWithSurf.tsx new file mode 100644 index 00000000..0dc77ccd --- /dev/null +++ b/templates/indexer-template/next-app/src/components/PostMessageWithSurf.tsx @@ -0,0 +1,121 @@ +"use client"; + +import { useWalletClient } from "@thalalabs/surf/hooks"; +import { useWallet } from "@aptos-labs/wallet-adapter-react"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; + +import { getAptosClient } from "@/lib/aptos"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { useToast } from "@/components/ui/use-toast"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { TransactionOnExplorer } from "@/components/ExplorerLink"; +import { ABI } from "@/lib/abi/message_board_abi"; +import { useQueryClient } from "@tanstack/react-query"; + +const FormSchema = z.object({ + stringContent: z.string(), +}); + +export function PostMessageWithSurf() { + const { toast } = useToast(); + const { connected, account } = useWallet(); + const { client: walletClient } = useWalletClient(); + const queryClient = useQueryClient(); + + const form = useForm>({ + resolver: zodResolver(FormSchema), + defaultValues: { + stringContent: "hello world", + }, + }); + + const onSignAndSubmitTransaction = async ( + data: z.infer + ) => { + if (!account || !walletClient) { + console.error("Account or wallet client not available"); + return; + } + + walletClient + .useABI(ABI) + .create_message({ + type_arguments: [], + arguments: [data.stringContent], + }) + .then((committedTransaction) => { + return getAptosClient().waitForTransaction({ + transactionHash: committedTransaction.hash, + }); + }) + .then((executedTransaction) => { + toast({ + title: "Success", + description: ( + + ), + }); + return new Promise((resolve) => setTimeout(resolve, 3000)); + }) + .then(() => { + return queryClient.invalidateQueries({ queryKey: ["messages"] }); + }) + .catch((error) => { + console.error("Error", error); + toast({ + title: "Error", + description: "Failed to create a message", + }); + }); + }; + + return ( + + + Create a new message + + +
+ + ( + + String Content + + + + Store a string content + + + )} + /> + + + +
+
+ ); +} diff --git a/templates/indexer-template/next-app/src/components/RootHeader.tsx b/templates/indexer-template/next-app/src/components/RootHeader.tsx new file mode 100644 index 00000000..68f825f0 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/RootHeader.tsx @@ -0,0 +1,28 @@ +import { ThemeToggle } from "@/components/ThemeToggle"; +import { WalletSelector } from "@/components/wallet/WalletSelector"; +import { IndexerStatus } from "@/components/IndexerStatus"; + +export const RootHeader = () => { + return ( + + ); +}; diff --git a/templates/indexer-template/next-app/src/components/SendTransaction.tsx b/templates/indexer-template/next-app/src/components/SendTransaction.tsx new file mode 100644 index 00000000..d7032b8b --- /dev/null +++ b/templates/indexer-template/next-app/src/components/SendTransaction.tsx @@ -0,0 +1,10 @@ +"use client"; + +import { PostMessageWithSurf } from "@/components/PostMessageWithSurf"; +import { useWallet } from "@aptos-labs/wallet-adapter-react"; + +export const SendTransaction = () => { + const { connected } = useWallet(); + + return connected && ; +}; diff --git a/templates/indexer-template/next-app/src/components/ThemeToggle.tsx b/templates/indexer-template/next-app/src/components/ThemeToggle.tsx new file mode 100644 index 00000000..49448a54 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/ThemeToggle.tsx @@ -0,0 +1,40 @@ +"use client"; + +import * as React from "react"; +import { Moon, Sun } from "lucide-react"; +import { useTheme } from "next-themes"; + +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; + +export function ThemeToggle() { + const { setTheme } = useTheme(); + + return ( + + + + + + setTheme("light")}> + Light + + setTheme("dark")}> + Dark + + setTheme("system")}> + System + + + + ); +} diff --git a/templates/indexer-template/next-app/src/components/WrongNetworkAlert.tsx b/templates/indexer-template/next-app/src/components/WrongNetworkAlert.tsx new file mode 100644 index 00000000..5a47299d --- /dev/null +++ b/templates/indexer-template/next-app/src/components/WrongNetworkAlert.tsx @@ -0,0 +1,33 @@ +"use client"; + +import { useWallet } from "@aptos-labs/wallet-adapter-react"; +import * as Dialog from "@radix-ui/react-dialog"; + +import { NETWORK } from "@/lib/aptos"; + +export function WrongNetworkAlert() { + const { network, connected } = useWallet(); + + return !connected || network?.name === NETWORK ? ( + <> + ) : ( + + + + +
+ + Wrong Network + + + Your wallet is currently on{" "} + {network?.name}. Please switch + to {NETWORK} to continue using + the app. + +
+
+
+
+ ); +} diff --git a/templates/indexer-template/next-app/src/components/message-board/columns.tsx b/templates/indexer-template/next-app/src/components/message-board/columns.tsx new file mode 100644 index 00000000..093ac2b5 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/message-board/columns.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { ColumnDef } from "@tanstack/react-table"; +import { truncateAddress } from "@aptos-labs/wallet-adapter-react"; + +import { DataTableColumnHeader } from "@/components/message-board/data-table-column-header"; +import { DataTableRowActions } from "@/components/message-board/data-table-row-actions"; +import { MessageBoardColumns } from "@/lib/type/message"; + +export const columns: ColumnDef[] = [ + { + accessorKey: "message_obj_addr", + header: ({ column }) => ( + + ), + cell: ({ row }) => ( +
+ {truncateAddress(row.getValue("message_obj_addr"))} +
+ ), + enableSorting: false, + }, + { + accessorKey: "creation_timestamp", + header: ({ column }) => ( + + ), + cell: ({ row }) => ( +
+ {new Date( + (row.getValue("creation_timestamp") as number) * 1000 + ).toLocaleString()} +
+ ), + enableSorting: true, + }, + { + id: "actions", + cell: ({ row }) => , + }, +]; diff --git a/templates/indexer-template/next-app/src/components/message-board/data-table-column-header.tsx b/templates/indexer-template/next-app/src/components/message-board/data-table-column-header.tsx new file mode 100644 index 00000000..e6c56eae --- /dev/null +++ b/templates/indexer-template/next-app/src/components/message-board/data-table-column-header.tsx @@ -0,0 +1,68 @@ +import { + ArrowDownIcon, + ArrowUpIcon, + CaretSortIcon, +} from "@radix-ui/react-icons"; +import { Column } from "@tanstack/react-table"; + +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; + +interface DataTableColumnHeaderProps + extends React.HTMLAttributes { + column: Column; + title: string; +} + +export function DataTableColumnHeader({ + column, + title, + className, +}: DataTableColumnHeaderProps) { + if (!column.getCanSort()) { + return
{title}
; + } + + return ( +
+ + + + + {column.getCanSort() && ( + + column.toggleSorting(false)}> + + Asc + + column.toggleSorting(true)}> + + Desc + + + + )} + +
+ ); +} diff --git a/templates/indexer-template/next-app/src/components/message-board/data-table-pagination.tsx b/templates/indexer-template/next-app/src/components/message-board/data-table-pagination.tsx new file mode 100644 index 00000000..952ce816 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/message-board/data-table-pagination.tsx @@ -0,0 +1,98 @@ +import { + ChevronLeftIcon, + ChevronRightIcon, + DoubleArrowLeftIcon, + DoubleArrowRightIcon, +} from "@radix-ui/react-icons"; +import { Table } from "@tanstack/react-table"; + +import { Button } from "@/components/ui/button"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +interface DataTablePaginationProps { + table: Table; + totalItems: number; +} + +export function DataTablePagination({ + table, + totalItems, +}: DataTablePaginationProps) { + const currentPage = table.getState().pagination.pageIndex + 1; + const pageSize = table.getState().pagination.pageSize; + const totalPages = Math.ceil(totalItems / pageSize); + + return ( +
+
+
+

Rows per page

+ +
+
+ Page {currentPage} of {totalPages} +
+
+ + + + +
+
+
+ ); +} diff --git a/templates/indexer-template/next-app/src/components/message-board/data-table-row-actions.tsx b/templates/indexer-template/next-app/src/components/message-board/data-table-row-actions.tsx new file mode 100644 index 00000000..a8c7a869 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/message-board/data-table-row-actions.tsx @@ -0,0 +1,46 @@ +"use client"; + +import { DotsHorizontalIcon } from "@radix-ui/react-icons"; +import { Row } from "@tanstack/react-table"; + +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { MessageBoardColumns } from "@/lib/type/message"; + +interface DataTableRowActionsProps { + row: Row; +} + +export function DataTableRowActions({ row }: DataTableRowActionsProps) { + const messageDetailPage = `/message/${row.original.message_obj_addr}`; + return ( + + + + + + + + View detail + + + + + ); +} diff --git a/templates/indexer-template/next-app/src/components/message-board/data-table.tsx b/templates/indexer-template/next-app/src/components/message-board/data-table.tsx new file mode 100644 index 00000000..5021b59f --- /dev/null +++ b/templates/indexer-template/next-app/src/components/message-board/data-table.tsx @@ -0,0 +1,155 @@ +"use client"; + +import * as React from "react"; +import { + ColumnDef, + SortingState, + flexRender, + getCoreRowModel, + getFacetedRowModel, + getFacetedUniqueValues, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; + +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; + +import { DataTablePagination } from "@/components/message-board/data-table-pagination"; +import { getMessagesOnServer } from "@/app/actions"; +import { useQuery } from "@tanstack/react-query"; + +interface DataTableProps { + columns: ColumnDef[]; +} + +export function DataTable({ + columns, +}: DataTableProps) { + const [sorting, setSorting] = React.useState([ + { id: "creation_timestamp", desc: true }, + ]); + const [{ pageIndex, pageSize }, setPagination] = React.useState({ + pageIndex: 0, + pageSize: 5, + }); + + const fetchData = async () => { + return await getMessagesOnServer({ + page: pageIndex + 1, + limit: pageSize, + sortedBy: sorting[0]?.id as "creation_timestamp", + order: sorting[0]?.desc ? "DESC" : "ASC", + }); + }; + + const { data, isLoading, isError, error } = useQuery({ + queryKey: ["messages", pageIndex, pageSize, sorting], + queryFn: fetchData, + }); + + const pagination = React.useMemo( + () => ({ + pageIndex, + pageSize, + }), + [pageIndex, pageSize] + ); + + const table = useReactTable({ + data: (data?.messages as TData[]) || [], + columns, + state: { + sorting, + pagination, + }, + pageCount: Math.ceil(data?.totalMessages || 0 / pageSize), + enableRowSelection: true, + onSortingChange: setSorting, + onPaginationChange: setPagination, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFacetedRowModel: getFacetedRowModel(), + getFacetedUniqueValues: getFacetedUniqueValues(), + manualPagination: true, + }); + + if (isLoading) { + return
Loading...
; + } + + if (isError) { + return
Error: {error.message}
; + } + + console.log("data", data); + + return ( +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+ +
+ ); +} diff --git a/templates/indexer-template/next-app/src/components/providers/QueryProvider.tsx b/templates/indexer-template/next-app/src/components/providers/QueryProvider.tsx new file mode 100644 index 00000000..562c6e24 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/providers/QueryProvider.tsx @@ -0,0 +1,12 @@ +"use client"; + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { useState } from "react"; + +export const QueryProvider = ({ children }: { children: React.ReactNode }) => { + const [queryClient] = useState(() => new QueryClient()); + + return ( + {children} + ); +}; diff --git a/templates/indexer-template/next-app/src/components/providers/ThemeProvider.tsx b/templates/indexer-template/next-app/src/components/providers/ThemeProvider.tsx new file mode 100644 index 00000000..b0ff2660 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/providers/ThemeProvider.tsx @@ -0,0 +1,9 @@ +"use client"; + +import * as React from "react"; +import { ThemeProvider as NextThemesProvider } from "next-themes"; +import { type ThemeProviderProps } from "next-themes/dist/types"; + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children}; +} diff --git a/templates/indexer-template/next-app/src/components/providers/WalletProvider.tsx b/templates/indexer-template/next-app/src/components/providers/WalletProvider.tsx new file mode 100644 index 00000000..75a4ee3f --- /dev/null +++ b/templates/indexer-template/next-app/src/components/providers/WalletProvider.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { AptosWalletAdapterProvider } from "@aptos-labs/wallet-adapter-react"; +import { PropsWithChildren } from "react"; +import { Network } from "@aptos-labs/ts-sdk"; +import { useToast } from "../ui/use-toast"; + +export const WalletProvider = ({ children }: PropsWithChildren) => { + const { toast } = useToast(); + + return ( + { + toast({ + variant: "destructive", + title: "Error", + description: error || "Unknown wallet error", + }); + }} + > + {children} + + ); +}; diff --git a/templates/indexer-template/next-app/src/components/ui/alert.tsx b/templates/indexer-template/next-app/src/components/ui/alert.tsx new file mode 100644 index 00000000..c3f73141 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/ui/alert.tsx @@ -0,0 +1,61 @@ +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; +import { HTMLAttributes, forwardRef } from "react"; + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + warning: + "border-amber-600/50 text-amber-600 dark:text-amber-300 dark:border-amber-300 [&>svg]:text-amber-600 dark:[&>svg]:text-amber-300", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +const Alert = forwardRef< + HTMLDivElement, + HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)); +Alert.displayName = "Alert"; + +const AlertTitle = forwardRef< + HTMLParagraphElement, + HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +AlertTitle.displayName = "AlertTitle"; + +const AlertDescription = forwardRef< + HTMLParagraphElement, + HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +AlertDescription.displayName = "AlertDescription"; + +export { Alert, AlertTitle, AlertDescription }; diff --git a/templates/indexer-template/next-app/src/components/ui/button.tsx b/templates/indexer-template/next-app/src/components/ui/button.tsx new file mode 100644 index 00000000..6980d1cc --- /dev/null +++ b/templates/indexer-template/next-app/src/components/ui/button.tsx @@ -0,0 +1,56 @@ +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; +import { ButtonHTMLAttributes, forwardRef } from "react"; + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export interface ButtonProps + extends ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/templates/indexer-template/next-app/src/components/ui/card.tsx b/templates/indexer-template/next-app/src/components/ui/card.tsx new file mode 100644 index 00000000..dc3b01de --- /dev/null +++ b/templates/indexer-template/next-app/src/components/ui/card.tsx @@ -0,0 +1,86 @@ +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Card.displayName = "Card"; + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardHeader.displayName = "CardHeader"; + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardTitle.displayName = "CardTitle"; + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardDescription.displayName = "CardDescription"; + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardContent.displayName = "CardContent"; + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardFooter.displayName = "CardFooter"; + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +}; diff --git a/templates/indexer-template/next-app/src/components/ui/checkbox.tsx b/templates/indexer-template/next-app/src/components/ui/checkbox.tsx new file mode 100644 index 00000000..576f3726 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/ui/checkbox.tsx @@ -0,0 +1,30 @@ +"use client"; + +import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; +import { Check } from "lucide-react"; + +import { cn } from "@/lib/utils"; +import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react"; + +const Checkbox = forwardRef< + ElementRef, + ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)); +Checkbox.displayName = CheckboxPrimitive.Root.displayName; + +export { Checkbox }; diff --git a/templates/indexer-template/next-app/src/components/ui/collapsible.tsx b/templates/indexer-template/next-app/src/components/ui/collapsible.tsx new file mode 100644 index 00000000..cb003d17 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/ui/collapsible.tsx @@ -0,0 +1,11 @@ +"use client"; + +import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; + +const Collapsible = CollapsiblePrimitive.Root; + +const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; + +const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent; + +export { Collapsible, CollapsibleTrigger, CollapsibleContent }; diff --git a/templates/indexer-template/next-app/src/components/ui/dialog.tsx b/templates/indexer-template/next-app/src/components/ui/dialog.tsx new file mode 100644 index 00000000..e72c2b5d --- /dev/null +++ b/templates/indexer-template/next-app/src/components/ui/dialog.tsx @@ -0,0 +1,122 @@ +"use client"; + +import * as React from "react"; +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { X } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +const Dialog = DialogPrimitive.Root; + +const DialogTrigger = DialogPrimitive.Trigger; + +const DialogPortal = DialogPrimitive.Portal; + +const DialogClose = DialogPrimitive.Close; + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DialogHeader.displayName = "DialogHeader"; + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DialogFooter.displayName = "DialogFooter"; + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +}; diff --git a/templates/indexer-template/next-app/src/components/ui/dropdown-menu.tsx b/templates/indexer-template/next-app/src/components/ui/dropdown-menu.tsx new file mode 100644 index 00000000..3a0c7fed --- /dev/null +++ b/templates/indexer-template/next-app/src/components/ui/dropdown-menu.tsx @@ -0,0 +1,200 @@ +"use client"; + +import * as React from "react"; +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import { Check, ChevronRight, Circle } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +const DropdownMenu = DropdownMenuPrimitive.Root; + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; + +const DropdownMenuGroup = DropdownMenuPrimitive.Group; + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal; + +const DropdownMenuSub = DropdownMenuPrimitive.Sub; + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)); +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName; + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName; + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)); +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + +)); +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)); +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName; + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)); +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + +)); +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; + +const DropdownMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ); +}; +DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +}; diff --git a/templates/indexer-template/next-app/src/components/ui/form.tsx b/templates/indexer-template/next-app/src/components/ui/form.tsx new file mode 100644 index 00000000..497718a9 --- /dev/null +++ b/templates/indexer-template/next-app/src/components/ui/form.tsx @@ -0,0 +1,177 @@ +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { Slot } from "@radix-ui/react-slot"; +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from "react-hook-form"; + +import { cn } from "@/lib/utils"; +import { Label } from "@/components/ui/label"; + +const Form = FormProvider; + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue, +); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error("useFormField should be used within "); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +
+ + ); +}); +FormItem.displayName = "FormItem"; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return ( +