├── .cursor └── rules │ ├── rescript-rule.mdc │ └── rescript.mdc ├── .github ├── Dockerfile ├── Dockerfile.typescript ├── ISSUE_TEMPLATE │ └── bug_report.md ├── changelog.json └── workflows │ ├── build_and_test.yml │ ├── cache-clean-guide.md │ ├── codegen_diff.yml │ ├── release.yml │ ├── templates_integration_test.yml │ └── update_codegen_on_main.yaml ├── .gitignore ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── README.md ├── codegenerator ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── cli │ ├── .idea │ │ ├── .gitignore │ │ ├── codegenerator.iml │ │ ├── modules.xml │ │ └── vcs.xml │ ├── Cargo.toml │ ├── CommandLineHelp.md │ ├── Makefile │ ├── README.md │ ├── npm │ │ ├── envio │ │ │ ├── .gitignore │ │ │ ├── bin.js │ │ │ ├── evm.schema.json │ │ │ ├── fuel.schema.json │ │ │ ├── index.d.ts │ │ │ ├── index.js │ │ │ ├── local-bin.mjs │ │ │ ├── package.json │ │ │ ├── package.json.tmpl │ │ │ ├── rescript.json │ │ │ └── src │ │ │ │ ├── Address.gen.ts │ │ │ │ ├── Address.res │ │ │ │ ├── ChainMap.res │ │ │ │ ├── ChainMap.resi │ │ │ │ ├── Envio.gen.ts │ │ │ │ ├── Envio.res │ │ │ │ ├── ErrorHandling.res │ │ │ │ ├── EventUtils.res │ │ │ │ ├── EvmTypes.res │ │ │ │ ├── FetchState.res │ │ │ │ ├── Hasura.res │ │ │ │ ├── Internal.gen.ts │ │ │ │ ├── Internal.res │ │ │ │ ├── LazyLoader.res │ │ │ │ ├── LoadManager.res │ │ │ │ ├── LogSelection.res │ │ │ │ ├── Logging.res │ │ │ │ ├── Persistence.res │ │ │ │ ├── PgStorage.res │ │ │ │ ├── Prometheus.res │ │ │ │ ├── ReorgDetection.res │ │ │ │ ├── Throttler.res │ │ │ │ ├── Throttler.resi │ │ │ │ ├── Time.res │ │ │ │ ├── TopicFilter.res │ │ │ │ ├── Types.ts │ │ │ │ ├── Utils.res │ │ │ │ ├── bindings │ │ │ │ ├── BigDecimal.gen.ts │ │ │ │ ├── BigDecimal.res │ │ │ │ ├── BigInt.res │ │ │ │ ├── Ethers.gen.ts │ │ │ │ ├── Ethers.res │ │ │ │ ├── Express.res │ │ │ │ ├── Hrtime.res │ │ │ │ ├── Hrtime.resi │ │ │ │ ├── NodeJs.res │ │ │ │ ├── Pino.gen.ts │ │ │ │ ├── Pino.res │ │ │ │ ├── Postgres.res │ │ │ │ ├── PromClient.res │ │ │ │ ├── Promise.res │ │ │ │ ├── SDSL.res │ │ │ │ └── Viem.res │ │ │ │ ├── db │ │ │ │ ├── EntityHistory.res │ │ │ │ ├── Schema.res │ │ │ │ └── Table.res │ │ │ │ ├── sources │ │ │ │ ├── Fuel.res │ │ │ │ ├── HyperFuel.res │ │ │ │ ├── HyperFuel.resi │ │ │ │ ├── HyperFuelClient.res │ │ │ │ ├── HyperSync.res │ │ │ │ ├── HyperSync.resi │ │ │ │ ├── HyperSyncClient.gen.ts │ │ │ │ ├── HyperSyncClient.res │ │ │ │ ├── HyperSyncJsonApi.res │ │ │ │ ├── Rpc.res │ │ │ │ ├── Source.res │ │ │ │ ├── SourceManager.res │ │ │ │ ├── SourceManager.resi │ │ │ │ └── vendored-fuel-abi-coder.js │ │ │ │ └── vendored │ │ │ │ ├── Rest.res │ │ │ │ └── Rest.resi │ │ └── package.json.tmpl │ ├── src │ │ ├── cli_args │ │ │ ├── clap_definitions.rs │ │ │ ├── init_config.rs │ │ │ ├── interactive_init │ │ │ │ ├── evm_prompts.rs │ │ │ │ ├── fuel_prompts.rs │ │ │ │ ├── inquire_helpers.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── shared_prompts.rs │ │ │ │ └── validation.rs │ │ │ └── mod.rs │ │ ├── commands.rs │ │ ├── config_parsing │ │ │ ├── chain_helpers.rs │ │ │ ├── contract_import │ │ │ │ ├── converters.rs │ │ │ │ └── mod.rs │ │ │ ├── entity_parsing.rs │ │ │ ├── event_parsing.rs │ │ │ ├── graph_migration │ │ │ │ └── mod.rs │ │ │ ├── human_config.rs │ │ │ ├── hypersync_endpoints.rs │ │ │ ├── mod.rs │ │ │ ├── postgres_types.rs │ │ │ ├── system_config.rs │ │ │ └── validation.rs │ │ ├── constants.rs │ │ ├── evm │ │ │ ├── abi.rs │ │ │ ├── address.rs │ │ │ └── mod.rs │ │ ├── executor │ │ │ ├── codegen.rs │ │ │ ├── dev.rs │ │ │ ├── init.rs │ │ │ ├── local.rs │ │ │ └── mod.rs │ │ ├── fuel │ │ │ ├── abi.rs │ │ │ ├── address.rs │ │ │ └── mod.rs │ │ ├── hbs_templating │ │ │ ├── codegen_templates.rs │ │ │ ├── contract_import_templates.rs │ │ │ ├── hbs_dir_generator.rs │ │ │ ├── init_templates.rs │ │ │ └── mod.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── persisted_state │ │ │ ├── db.rs │ │ │ ├── hash_string.rs │ │ │ └── mod.rs │ │ ├── project_paths │ │ │ ├── handler_paths.rs │ │ │ ├── mod.rs │ │ │ └── path_utils.rs │ │ ├── rescript_types.rs │ │ ├── scripts │ │ │ ├── mod.rs │ │ │ └── print_missing_networks.rs │ │ ├── service_health.rs │ │ ├── template_dirs.rs │ │ └── utils │ │ │ ├── file_system.rs │ │ │ ├── mod.rs │ │ │ ├── normalized_list.rs │ │ │ ├── text.rs │ │ │ └── unique_hashmap.rs │ ├── tarpaulin.toml │ ├── templates │ │ ├── dynamic │ │ │ ├── codegen │ │ │ │ ├── index.d.ts.hbs │ │ │ │ ├── package.json.hbs │ │ │ │ ├── persisted_state.envio.json.hbs │ │ │ │ └── src │ │ │ │ │ ├── ConfigYAML.res.hbs │ │ │ │ │ ├── ContextEnv.res.hbs │ │ │ │ │ ├── Handlers.res.hbs │ │ │ │ │ ├── Path.res.hbs │ │ │ │ │ ├── RegisterHandlers.res.hbs │ │ │ │ │ ├── TestHelpers.res.hbs │ │ │ │ │ ├── TestHelpers_MockDb.res.hbs │ │ │ │ │ ├── Types.res.hbs │ │ │ │ │ ├── Types.ts.hbs │ │ │ │ │ └── db │ │ │ │ │ ├── Entities.res.hbs │ │ │ │ │ └── Enums.res.hbs │ │ │ ├── contract_import_templates │ │ │ │ ├── javascript │ │ │ │ │ ├── src │ │ │ │ │ │ └── EventHandlers.js.hbs │ │ │ │ │ └── test │ │ │ │ │ │ └── Test.js.hbs │ │ │ │ ├── rescript │ │ │ │ │ ├── src │ │ │ │ │ │ └── EventHandlers.res.hbs │ │ │ │ │ └── test │ │ │ │ │ │ └── Test.res.hbs │ │ │ │ ├── shared │ │ │ │ │ ├── .env.hbs │ │ │ │ │ └── schema.graphql.hbs │ │ │ │ └── typescript │ │ │ │ │ ├── src │ │ │ │ │ └── EventHandlers.ts.hbs │ │ │ │ │ └── test │ │ │ │ │ └── Test.ts.hbs │ │ │ ├── init_templates │ │ │ │ └── shared │ │ │ │ │ ├── .env.hbs │ │ │ │ │ └── package.json.hbs │ │ │ └── subgraph_migration_templates │ │ │ │ ├── javascript │ │ │ │ └── src │ │ │ │ │ └── EventHandlers.js.hbs │ │ │ │ ├── rescript │ │ │ │ └── src │ │ │ │ │ └── EventHandlers.res.hbs │ │ │ │ └── typescript │ │ │ │ └── src │ │ │ │ └── EventHandlers.ts.hbs │ │ └── static │ │ │ ├── blank_template │ │ │ ├── javascript │ │ │ │ └── .gitkeep │ │ │ ├── rescript │ │ │ │ ├── rescript.json │ │ │ │ └── test │ │ │ │ │ └── Test.res │ │ │ ├── shared │ │ │ │ ├── .env.example │ │ │ │ ├── .gitignore │ │ │ │ └── README.md │ │ │ └── typescript │ │ │ │ └── tsconfig.json │ │ │ ├── codegen │ │ │ ├── .gitignore │ │ │ ├── .npmrc │ │ │ ├── LICENSE.md │ │ │ ├── docker-compose.yaml │ │ │ ├── index.js │ │ │ ├── rescript.json │ │ │ └── src │ │ │ │ ├── Benchmark.res │ │ │ │ ├── Config.res │ │ │ │ ├── Env.res │ │ │ │ ├── EventProcessing.res │ │ │ │ ├── EventRouter.res │ │ │ │ ├── GqlDbCustomTypes.res │ │ │ │ ├── IO.res │ │ │ │ ├── InMemoryStore.res │ │ │ │ ├── InMemoryTable.res │ │ │ │ ├── Index.res │ │ │ │ ├── Js.shim.ts │ │ │ │ ├── LoadLayer.res │ │ │ │ ├── LoadLayer.resi │ │ │ │ ├── PersistedState.res │ │ │ │ ├── TableIndices.res │ │ │ │ ├── TestHelpers_MockAddresses.res │ │ │ │ ├── UserContext.res │ │ │ │ ├── bindings │ │ │ │ ├── Dotenv.res │ │ │ │ ├── Ethers.gen.ts │ │ │ │ ├── Lodash.res │ │ │ │ ├── OpaqueTypes.ts │ │ │ │ ├── RescriptMocha.res │ │ │ │ ├── Yargs.res │ │ │ │ └── vendored-lodash-fns.js │ │ │ │ ├── db │ │ │ │ ├── Db.res │ │ │ │ ├── DbFunctions.res │ │ │ │ ├── DbFunctionsEntities.res │ │ │ │ ├── DbFunctionsImplementation.js │ │ │ │ ├── Migrations.res │ │ │ │ └── TablesStatic.res │ │ │ │ ├── eventFetching │ │ │ │ ├── ChainFetcher.res │ │ │ │ ├── ChainManager.res │ │ │ │ ├── NetworkSources.res │ │ │ │ ├── hyperfuel │ │ │ │ │ └── HyperFuelSource.res │ │ │ │ ├── hypersync │ │ │ │ │ └── HyperSyncSource.res │ │ │ │ └── rpc │ │ │ │ │ └── RpcSource.res │ │ │ │ ├── globalState │ │ │ │ ├── GlobalState.res │ │ │ │ ├── GlobalStateManager.res │ │ │ │ └── GlobalStateManager.resi │ │ │ │ └── ink │ │ │ │ ├── EnvioInkApp.res │ │ │ │ ├── bindings │ │ │ │ ├── DateFns.res │ │ │ │ ├── Ink.res │ │ │ │ └── Style.res │ │ │ │ └── components │ │ │ │ ├── BufferedProgressBar.res │ │ │ │ ├── ChainData.res │ │ │ │ ├── CustomHooks.res │ │ │ │ ├── Messages.res │ │ │ │ └── SyncETA.res │ │ │ ├── erc20_template │ │ │ ├── javascript │ │ │ │ ├── config.yaml │ │ │ │ ├── src │ │ │ │ │ └── EventHandlers.js │ │ │ │ └── test │ │ │ │ │ └── test.js │ │ │ ├── rescript │ │ │ │ ├── config.yaml │ │ │ │ ├── rescript.json │ │ │ │ ├── src │ │ │ │ │ ├── EventHandlers.bs.js │ │ │ │ │ └── EventHandlers.res │ │ │ │ └── test │ │ │ │ │ └── Test.res │ │ │ ├── shared │ │ │ │ ├── .env.example │ │ │ │ ├── .gitignore │ │ │ │ ├── README.md │ │ │ │ └── schema.graphql │ │ │ └── typescript │ │ │ │ ├── build │ │ │ │ └── src │ │ │ │ │ └── EventHandlers.js │ │ │ │ ├── config.yaml │ │ │ │ ├── src │ │ │ │ └── EventHandlers.ts │ │ │ │ ├── test │ │ │ │ └── test.ts │ │ │ │ └── tsconfig.json │ │ │ ├── greeter_template │ │ │ ├── javascript │ │ │ │ ├── README.md │ │ │ │ ├── config.yaml │ │ │ │ ├── src │ │ │ │ │ └── EventHandlers.js │ │ │ │ └── test │ │ │ │ │ └── test.js │ │ │ ├── rescript │ │ │ │ ├── README.md │ │ │ │ ├── config.yaml │ │ │ │ ├── rescript.json │ │ │ │ ├── src │ │ │ │ │ └── EventHandlers.res │ │ │ │ └── test │ │ │ │ │ └── Test.res │ │ │ ├── shared │ │ │ │ ├── .env.example │ │ │ │ ├── .gitignore │ │ │ │ ├── abis │ │ │ │ │ └── greeter-abi.json │ │ │ │ └── schema.graphql │ │ │ └── typescript │ │ │ │ ├── README.md │ │ │ │ ├── config.yaml │ │ │ │ ├── src │ │ │ │ └── EventHandlers.ts │ │ │ │ ├── test │ │ │ │ └── test.ts │ │ │ │ └── tsconfig.json │ │ │ └── greeteronfuel_template │ │ │ ├── javascript │ │ │ ├── README.md │ │ │ ├── config.yaml │ │ │ ├── src │ │ │ │ └── EventHandlers.js │ │ │ └── test │ │ │ │ └── test.js │ │ │ ├── rescript │ │ │ ├── README.md │ │ │ ├── config.yaml │ │ │ ├── rescript.json │ │ │ ├── src │ │ │ │ └── EventHandlers.res │ │ │ └── test │ │ │ │ └── Test.res │ │ │ ├── shared │ │ │ ├── .env.example │ │ │ ├── .gitignore │ │ │ ├── abis │ │ │ │ └── greeter-abi.json │ │ │ └── schema.graphql │ │ │ └── typescript │ │ │ ├── README.md │ │ │ ├── config.yaml │ │ │ ├── src │ │ │ └── EventHandlers.ts │ │ │ ├── test │ │ │ └── test.ts │ │ │ └── tsconfig.json │ └── test │ │ ├── abis │ │ ├── Contract1.json │ │ ├── Contract2.json │ │ ├── greeter-abi.json │ │ └── nested-abi.json │ │ ├── api_response │ │ └── GetSourceCodeResponse.json │ │ ├── configs │ │ ├── config1.yaml │ │ ├── config2.yaml │ │ ├── config3.yaml │ │ ├── config4.yaml │ │ ├── config5.yaml │ │ ├── dynamic-address-config.yaml │ │ ├── factory-contract-config.yaml │ │ ├── fuel-config.yaml │ │ ├── graph-manifest.yaml │ │ ├── gravatar-with-required-entities.yaml │ │ ├── invalid-multiple-sync-config.yaml │ │ ├── nested-abi.yaml │ │ └── schema.graphql │ │ └── schemas │ │ ├── gravatar-schema.graphql │ │ ├── schema-with-directive.graphql │ │ └── schema.graphql └── integration_tests │ ├── Cargo.toml │ ├── src │ ├── hypersync_health.rs │ └── main.rs │ └── tests │ ├── DynamicContracts.js │ ├── EndblockSuccess.js │ ├── WildcardUniFactory.js │ ├── databaseTestHelpers.js │ ├── evm_Erc20.js │ ├── evm_Greeter.js │ ├── fuel_Greeter.js │ ├── graphqlFetchWithTestCallback.js │ ├── runAll.sh │ ├── runSingle.sh │ ├── testIndexerExits.sh │ └── test_indexers │ ├── dynamic_contracts │ ├── .gitignore │ ├── config-dynamic-contracts.yaml │ ├── package.json │ ├── pnpm-lock.yaml │ ├── schema.graphql │ ├── src │ │ └── DynamicContractHandlers.ts │ └── tsconfig.json │ ├── test_exits │ ├── .gitignore │ ├── config-broken.yaml │ ├── config.yaml │ ├── package.json │ ├── pnpm-lock.yaml │ ├── schema.graphql │ ├── src │ │ ├── BrokenHandler.ts │ │ ├── DynamicContractHandlers.ts │ │ └── EventHandler.ts │ └── tsconfig.json │ └── wildcard-uni-factory │ ├── .gitignore │ ├── README.md │ ├── config.yaml │ ├── package.json │ ├── pnpm-lock.yaml │ ├── schema.graphql │ ├── src │ └── EventHandlers.ts │ └── tsconfig.json ├── internal_docs ├── EventFetchers.md ├── EventProcessor.md ├── IndexerStructure.md └── README.md ├── licenses ├── CLA.md ├── EULA.md ├── LICENSE.md └── README.md ├── scenarios ├── erc20_multichain_factory │ ├── .gitignore │ ├── .mocharc.json │ ├── README.md │ ├── config.yaml │ ├── package.json │ ├── pnpm-lock.yaml │ ├── pnpm-workspace.yaml │ ├── rescript.json │ ├── schema.graphql │ ├── src │ │ └── EventHandlers.res │ └── test │ │ ├── ChainDataHelpers.res │ │ ├── DbHelpers.res │ │ ├── DynamicContractRecovery_test.res │ │ ├── Handler_Test.res │ │ ├── MockChainData.res │ │ ├── RollbackDynamicContract_test.res │ │ ├── RollbackMultichain_test.res │ │ ├── TestDeleteEntity.res │ │ └── TestWhereQuery.res ├── fuel_test │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── abis │ │ ├── all-events-abi.json │ │ └── greeter-abi.json │ ├── config.yaml │ ├── contracts │ │ ├── all-events │ │ │ ├── .gitignore │ │ │ ├── Forc.lock │ │ │ ├── Forc.toml │ │ │ └── src │ │ │ │ └── main.sw │ │ ├── interaction-tools │ │ │ ├── .env.example │ │ │ ├── .gitignore │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ └── main.rs │ │ └── ts-interaction-tools │ │ │ ├── .env.example │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src │ │ │ ├── @types │ │ │ │ └── env.d.ts │ │ │ └── deploy.ts │ │ │ └── tsconfig.json │ ├── package.json │ ├── pnpm-lock.yaml │ ├── rescript.json │ ├── schema.graphql │ ├── src │ │ ├── AllEventsHandlers.ts │ │ └── GreeterHandlers.ts │ ├── test │ │ ├── HyperFuelSource_test.res │ │ └── test.ts │ └── tsconfig.json ├── helpers │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── pnpm-lock.yaml │ ├── rescript.json │ └── src │ │ ├── ChainMocking.res │ │ └── Indexer.res └── test_codegen │ ├── .gitignore │ ├── .mocharc.json │ ├── .npmrc │ ├── README.md │ ├── abis │ ├── NftFactory.json │ ├── SimpleNft.json │ └── gravatar-abi.json │ ├── config.yaml │ ├── contracts │ ├── .gitignore │ ├── README.md │ ├── artifacts │ │ └── contracts │ │ │ ├── GravatarRegistry.sol │ │ │ └── GravatarRegistry.json │ │ │ ├── NftFactory.sol │ │ │ └── NftFactory.json │ │ │ ├── SimpleNft.sol │ │ │ └── SimpleNft.json │ │ │ └── TestEvents.sol │ │ │ └── TestEvents.json │ ├── contracts │ │ ├── GravatarRegistry.sol │ │ ├── NftFactory.sol │ │ ├── SimpleNft.sol │ │ └── TestEvents.sol │ ├── deploy │ │ └── deploy.js │ ├── package.json │ ├── secretsManager.example.ts │ └── tasks │ │ ├── index.js │ │ ├── newGravatarTask.js │ │ ├── updateGravatarImageTask.js │ │ └── updateGravatarNameTask.js │ ├── hardhat.config.ts │ ├── package.json │ ├── pnpm-lock.yaml │ ├── pnpm-workspace.yaml │ ├── rescript.json │ ├── schema.graphql │ ├── src │ ├── EventHandlers.res │ └── EventHandlers.ts │ ├── test │ ├── ChainFetcher_test.res │ ├── ChainManager_test.res │ ├── Config_test.res │ ├── CustomSelection_test.res │ ├── CustomSelection_test.ts │ ├── E2EEthNode_test.res │ ├── Encoders_schema_test.res │ ├── EventFilters_test.res │ ├── EventHandler_test.ts │ ├── HyperSyncSource_test.res │ ├── Integration_ts_helpers.gen.ts │ ├── Integration_ts_helpers.res │ ├── LoadLayer_test.res │ ├── LoadLinkedEntities.res │ ├── Mock_test.res │ ├── ReorgDetection_test.res │ ├── RpcSource_test.res │ ├── SerDe_Test.res │ ├── Utils_test.res │ ├── Viem_test.res │ ├── __mocks__ │ │ ├── DbStub.res │ │ ├── MockConfig.res │ │ ├── MockEntities.res │ │ └── MockEvents.res │ ├── entity-column-types-test.ts │ ├── helpers │ │ ├── DbHelpers.res │ │ ├── LiveGravatarTask.res │ │ ├── Mock.res │ │ ├── SetupRpcNode.res │ │ ├── contracts │ │ │ └── GravatarRegistry.sol │ │ ├── node-and-contracts.ts │ │ ├── setupNodeAndContracts.js │ │ ├── taskLiveGravatarTxs.js │ │ └── utils.ts │ ├── integration-raw-events-test.ts │ ├── lib_tests │ │ ├── EntityHistory_test.res │ │ ├── EventRouter_test.res │ │ ├── FetchState_test.res │ │ ├── Persistence_test.res │ │ ├── PgStorage_test.res │ │ ├── Rpc_Test.res │ │ ├── SingleOrMultiple_test.res │ │ ├── SourceManager_test.res │ │ ├── Table_test.res │ │ └── Throttler_test.res │ ├── manual │ │ ├── LogTesting.res │ │ └── README.md │ ├── raw-events-table-migration-test.ts │ ├── rollback │ │ ├── ChainDataHelpers.res │ │ ├── MockChainData.res │ │ ├── MockChainData_test.res │ │ └── Rollback_test.res │ ├── schema_types │ │ ├── BigDecimal_test.res │ │ ├── BigDecimal_test_typescript.ts │ │ └── Timestamp_test.res │ ├── sql-transaction-test.ts │ └── topic-hashing-test.ts │ └── tsconfig.json └── sync.gif /.cursor/rules/rescript-rule.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: *.res 4 | alwaysApply: false 5 | --- 6 | Always use ReScript 11 documentation. 7 | Never suggest ReasonML syntax. -------------------------------------------------------------------------------- /.cursor/rules/rescript.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Rules to let Cursor help you write relevant ReScript code. 3 | globs: *.res,*.resi 4 | alwaysApply: false 5 | --- 6 | - Always use ReScript 11 documentation. 7 | - Never suggest ReasonML syntax. 8 | - Never use `[| item |]` to create an array. Use `[ item ]` instead. 9 | - Must always use `=` for setting value to a field. Use `:=` only for ref values created using `ref` function. 10 | -------------------------------------------------------------------------------- /.github/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | #rescript base 3 | 4 | FROM --platform=linux/amd64 node:18.16.1-bookworm-slim 5 | # FROM --platform=linux/amd64 node:20.4.0-bookworm-slim # Testing differen versions for 6 | WORKDIR /app/base-template 7 | 8 | RUN apt-get update 9 | RUN apt-get install -y wget 10 | #install the libssl required by the rust package 11 | RUN wget http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb 12 | RUN dpkg -i libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb 13 | 14 | ENV PNPM_HOME /usr/local/binp 15 | RUN npm install --global pnpm 16 | 17 | ARG ENVIO_VERSION 18 | RUN npm install --global envio@${ENVIO_VERSION} 19 | 20 | ARG COMMIT_HASH_ARG 21 | ENV COMMIT_HASH=${COMMIT_HASH_ARG} 22 | 23 | RUN envio init -d . --name erc20indexer -t erc20 -l rescript 24 | -------------------------------------------------------------------------------- /.github/Dockerfile.typescript: -------------------------------------------------------------------------------- 1 | 2 | #rescript base 3 | 4 | FROM --platform=linux/amd64 node:18.16.1-bookworm-slim 5 | # FROM --platform=linux/amd64 node:20.4.0-bookworm-slim # Testing differen versions for 6 | WORKDIR /app/base-template 7 | 8 | RUN apt-get update 9 | RUN apt-get install -y wget 10 | 11 | RUN wget http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb 12 | RUN dpkg -i libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb 13 | 14 | ENV PNPM_HOME /usr/local/binp 15 | RUN npm install --global pnpm 16 | 17 | ARG ENVIO_VERSION 18 | RUN npm install --global envio@${ENVIO_VERSION} 19 | 20 | ARG COMMIT_HASH_ARG 21 | ENV COMMIT_HASH=${COMMIT_HASH_ARG} 22 | 23 | RUN envio init -d . --name erc20indexer -t erc20 -l typescript 24 | 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | E.g. 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | **Screenshots** 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | **Local (please complete the following information):** 29 | - Envio version 30 | - Node version 31 | - pnpm version 32 | - Docker version (if running locally) 33 | 34 | **Hosted Service (please complete the following information):** 35 | - Envio version 36 | - Link to the deployed indexer 37 | 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/changelog.json: -------------------------------------------------------------------------------- 1 | { 2 | "categories": [ 3 | { 4 | "title": "## Features", 5 | "labels": ["T-feature"] 6 | }, 7 | { 8 | "title": "## Fixes", 9 | "labels": ["T-bug", "T-fix"] 10 | } 11 | ], 12 | "ignore_labels": ["L-ignore"], 13 | "template": "${{CHANGELOG}}\n## Other\n\n${{UNCATEGORIZED}}", 14 | "pr_template": "- ${{TITLE}} (#${{NUMBER}})", 15 | "empty_template": "- No changes" 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/cache-clean-guide.md: -------------------------------------------------------------------------------- 1 | # GitHub Cache Cleanup Guide 2 | 3 | This guide outlines the steps to update and clean the GitHub cache for the `hyperindex` repository. Follow these instructions to authenticate with GitHub, install the GitHub CLI if needed, and clear specific cache entries. 4 | 5 | ### Prerequisites 6 | 7 | - Ensure you have the [GitHub CLI (`gh`)](https://cli.github.com/) installed. 8 | 9 | ## Steps 10 | 11 | ### 1. Authenticate with GitHub 12 | 13 | ```bash 14 | gh auth login 15 | gh --version # sanity check version 16 | ``` 17 | 18 | ### 2. List GitHub Action Caches 19 | 20 | To view a list of caches in the `hyperindex` repository: 21 | 22 | ```bash 23 | gh api \ 24 | -H "Accept: application/vnd.github+json" \ 25 | /repos/enviodev/hyperindex/actions/caches 26 | ``` 27 | 28 | ### 3. Delete GitHub Action Caches 29 | 30 | To delete specific caches, first identify the cache IDs from the previous step (manually for now, but if needed a jq script could be made). Then, use the following script to delete caches by ID: 31 | 32 | ```bash 33 | # List of cache IDs you want to delete 34 | cache_ids=(111 222 333 444 555) 35 | 36 | # Loop through the list and delete each cache 37 | for id in "${cache_ids[@]}" 38 | do 39 | gh api \ 40 | --method DELETE \ 41 | -H "Accept: application/vnd.github+json" \ 42 | -H "X-GitHub-Api-Version: 2022-11-28" \ 43 | /repos/enviodev/hyperindex/actions/caches/$id 44 | echo "Deleted cache with ID $id" 45 | done 46 | ``` 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */target 2 | .DS_Store 3 | # vim files 4 | *~ 5 | *.swo 6 | *.swp 7 | 8 | #All generated code for scenarios 9 | scenarios/**/*.bs.js 10 | scenarios/**/*.res.js 11 | scenarios/**/*.gen.ts 12 | scenarios/**/lib/* 13 | scenarios/**/node_modules/* 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.showUnlinkedFileNotification": false 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | codegenerator/cli/README.md -------------------------------------------------------------------------------- /codegenerator/.gitignore: -------------------------------------------------------------------------------- 1 | */target 2 | */tarpaulin_target 3 | */tarpaulin-report.html 4 | integration_test_output -------------------------------------------------------------------------------- /codegenerator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["cli", "integration_tests"] 3 | resolver = "2" 4 | -------------------------------------------------------------------------------- /codegenerator/cli/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /codegenerator/cli/.idea/codegenerator.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /codegenerator/cli/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /codegenerator/cli/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /codegenerator/cli/Makefile: -------------------------------------------------------------------------------- 1 | # Target to update schemas 2 | update-schemas: 3 | @echo "Updating schemas json schemas for evm human config..." 4 | cargo run -- script print-config-json-schema evm > ./npm/envio/evm.schema.json 5 | @echo "Updating schemas json schemas for fuel human config..." 6 | cargo run -- script print-config-json-schema fuel > ./npm/envio/fuel.schema.json 7 | 8 | # Target to update help 9 | update-help: 10 | @echo "Updating help doc..." 11 | cargo run -- script print-cli-help-md > ./CommandLineHelp.md 12 | 13 | # Target to help find missing networks 14 | print-missing-networks: 15 | @echo "Printing missing networks..." 16 | cargo run -- script print-missing-networks 17 | 18 | # Target to update generated docs, depends on update-schemas and update-help 19 | update-generated-docs: update-schemas update-help 20 | 21 | # Runs rustfmt with format_strings option. 22 | format: 23 | @echo "Formatting code" 24 | cargo fmt -- --config format_strings=true 25 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | //@ts-check 3 | "use strict"; 4 | 5 | const { spawnSync } = require("child_process"); 6 | 7 | /** 8 | * Returns the executable path for envio located inside node_modules 9 | * The naming convention is envio-${os}-${arch} 10 | * If the platform is `win32` or `cygwin`, executable will include a `.exe` extension 11 | * @see https://nodejs.org/api/os.html#osarch 12 | * @see https://nodejs.org/api/os.html#osplatform 13 | * @example "x/xx/node_modules/envio-darwin-arm64" 14 | */ 15 | function getExePath() { 16 | const arch = process.arch; 17 | /** 18 | * @type string 19 | */ 20 | let os = process.platform; 21 | let extension = ""; 22 | if (["win32", "cygwin"].includes(process.platform)) { 23 | os = "windows"; 24 | extension = ".exe"; 25 | } 26 | 27 | try { 28 | // Since the bin will be located inside `node_modules`, we can simply call require.resolve 29 | return require.resolve(`envio-${os}-${arch}/bin/envio${extension}`); 30 | } catch (e) { 31 | throw new Error( 32 | `Couldn't find envio binary inside node_modules for ${os}-${arch}` 33 | ); 34 | } 35 | } 36 | 37 | /** 38 | * Runs `envio` with args using nodejs spawn 39 | */ 40 | function runEnvio() { 41 | const args = process.argv.slice(2); 42 | const processResult = spawnSync(getExePath(), args, { stdio: "inherit" }); 43 | process.exit(processResult.status ?? 0); 44 | } 45 | 46 | runEnvio(); 47 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/index.js: -------------------------------------------------------------------------------- 1 | // This file is needed to have control over TS version exports 2 | // Some parts like Sury reexport are impossible to implement 3 | // on the JS side, so we need to do it here 4 | 5 | const envioGen = require("./src/Envio.bs.js"); 6 | Object.assign(exports, envioGen); 7 | 8 | const Sury = require("rescript-schema"); 9 | // Important! Should match the index.d.ts file 10 | exports.S = { 11 | string: Sury.string, 12 | jsonString: Sury.jsonString, 13 | boolean: Sury.boolean, 14 | int32: Sury.int32, 15 | number: Sury.number, 16 | bigint: Sury.bigint, 17 | never: Sury.never, 18 | union: Sury.union, 19 | object: Sury.object, 20 | // Might change in a near future 21 | // custom: Sury.custom, 22 | // Don't expose recursive for now, since it's too advanced 23 | // recursive: Sury.recursive, 24 | transform: Sury.transform, 25 | refine: Sury.refine, 26 | schema: Sury.schema, 27 | record: Sury.record, 28 | array: Sury.array, 29 | tuple: Sury.tuple, 30 | merge: Sury.merge, 31 | optional: Sury.optional, 32 | nullable: Sury.nullable, 33 | bigDecimal: require("./src/bindings/BigDecimal.bs.js").schema, 34 | // Nullish type will change in "sury@10" 35 | // nullish: Sury.nullish, 36 | assertOrThrow: Sury.assertOrThrow, 37 | }; 38 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "envio", 3 | "version": "0.0.1-dev", 4 | "private": true, 5 | "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.", 6 | "bin": "./local-bin.mjs", 7 | "main": "./index.js", 8 | "types": "./index.d.ts", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/enviodev/hyperindex.git" 12 | }, 13 | "scripts": { 14 | "watch": "rescript -w" 15 | }, 16 | "author": "envio contributors ", 17 | "license": "GPL-3.0", 18 | "bugs": { 19 | "url": "https://github.com/enviodev/hyperindex/issues" 20 | }, 21 | "homepage": "https://envio.dev", 22 | "dependencies": { 23 | "@envio-dev/hypersync-client": "0.6.5", 24 | "rescript": "11.1.3", 25 | "rescript-schema": "9.3.0", 26 | "viem": "2.21.0", 27 | "bignumber.js": "9.1.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/package.json.tmpl: -------------------------------------------------------------------------------- 1 | { 2 | "name": "envio", 3 | "version": "${version}", 4 | "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.", 5 | "bin": "./bin.js", 6 | "main": "./index.js", 7 | "types": "./index.d.ts", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/enviodev/hyperindex.git" 11 | }, 12 | "keywords": [ 13 | "blockchain", 14 | "indexer", 15 | "ethereum", 16 | "evm", 17 | "fuel", 18 | "data", 19 | "dapp" 20 | ], 21 | "author": "envio contributors ", 22 | "license": "GPL-3.0", 23 | "bugs": { 24 | "url": "https://github.com/enviodev/hyperindex/issues" 25 | }, 26 | "homepage": "https://envio.dev", 27 | "optionalDependencies": { 28 | "envio-linux-x64": "${version}", 29 | "envio-linux-arm64": "${version}", 30 | "envio-darwin-x64": "${version}", 31 | "envio-darwin-arm64": "${version}" 32 | }, 33 | "dependencies": { 34 | "@envio-dev/hypersync-client": "0.6.5", 35 | "rescript": "11.1.3", 36 | "rescript-schema": "9.3.0", 37 | "viem": "2.21.0", 38 | "bignumber.js": "9.1.2" 39 | }, 40 | "files": [ 41 | "bin.js", 42 | "evm.schema.json", 43 | "fuel.schema.json", 44 | "rescript.json", 45 | "index.d.ts", 46 | "index.js", 47 | "src", 48 | "!src/**/*.bs.js" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "envio", 3 | "namespace": false, 4 | "sources": [ 5 | { 6 | "dir": "src", 7 | "subdirs": true 8 | } 9 | ], 10 | "suffix": ".bs.js", 11 | "package-specs": { 12 | "module": "commonjs", 13 | "in-source": true 14 | }, 15 | "gentypeconfig": { 16 | "generatedFileExtension": ".gen.ts" 17 | }, 18 | "bs-dependencies": ["rescript-schema"], 19 | "bsc-flags": ["-open RescriptSchema"] 20 | } 21 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/Address.gen.ts: -------------------------------------------------------------------------------- 1 | /* TypeScript file generated from Address.res by genType. */ 2 | 3 | /* eslint-disable */ 4 | /* tslint:disable */ 5 | 6 | import type {Address as $$t} from './Types.ts'; 7 | 8 | export type t = $$t; 9 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/Address.res: -------------------------------------------------------------------------------- 1 | @genType.import(("./Types.ts", "Address")) 2 | type t 3 | 4 | let schema = S.string->S.setName("Address")->(Utils.magic: S.t => S.t) 5 | 6 | external toString: t => string = "%identity" 7 | 8 | external unsafeFromString: string => t = "%identity" 9 | 10 | module Evm = { 11 | @module("viem") 12 | external fromStringOrThrow: string => t = "getAddress" 13 | // Reassign since the function might be used in the handler code 14 | // and we don't want to have a "viem" import there. It's needed to keep "viem" a dependency 15 | // of generated code instead of adding it to the indexer project dependencies. 16 | // Also, we want a custom error message, which is searchable in our codebase. 17 | let fromStringOrThrow = string => { 18 | try { 19 | fromStringOrThrow(string) 20 | } catch { 21 | | _ => 22 | Js.Exn.raiseError( 23 | `Address "${string}" is invalid. Expected a 20-byte hex string starting with 0x.`, 24 | ) 25 | } 26 | } 27 | 28 | let fromAddressOrThrow = address => address->toString->fromStringOrThrow 29 | } 30 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/ChainMap.res: -------------------------------------------------------------------------------- 1 | open Belt 2 | 3 | module Chain = { 4 | type t = int 5 | 6 | external toChainId: t => int = "%identity" 7 | 8 | let toString = chainId => chainId->Int.toString 9 | 10 | let makeUnsafe = (~chainId) => chainId 11 | } 12 | 13 | module ChainIdCmp = Belt.Id.MakeComparableU({ 14 | type t = Chain.t 15 | let cmp = (a, b) => Pervasives.compare(a->Chain.toChainId, b->Chain.toChainId) 16 | }) 17 | 18 | type t<'a> = Belt.Map.t 19 | 20 | let fromArrayUnsafe: array<(Chain.t, 'a)> => t<'a> = arr => { 21 | arr->Map.fromArray(~id=module(ChainIdCmp)) 22 | } 23 | 24 | let get: (t<'a>, Chain.t) => 'a = (self, chain) => 25 | switch Map.get(self, chain) { 26 | | Some(v) => v 27 | | None => 28 | // Should be unreachable, since we validate on Chain.t creation 29 | // Still throw just in case something went wrong 30 | Js.Exn.raiseError("No chain with id " ++ chain->Chain.toString ++ " found in chain map") 31 | } 32 | 33 | let set: (t<'a>, Chain.t, 'a) => t<'a> = (map, chain, v) => Map.set(map, chain, v) 34 | let values: t<'a> => array<'a> = map => Map.valuesToArray(map) 35 | let keys: t<'a> => array = map => Map.keysToArray(map) 36 | let entries: t<'a> => array<(Chain.t, 'a)> = map => Map.toArray(map) 37 | let has: (t<'a>, Chain.t) => bool = (map, chain) => Map.has(map, chain) 38 | let map: (t<'a>, 'a => 'b) => t<'b> = (map, fn) => Map.map(map, fn) 39 | let mapWithKey: (t<'a>, (Chain.t, 'a) => 'b) => t<'b> = (map, fn) => Map.mapWithKey(map, fn) 40 | let size: t<'a> => int = map => Map.size(map) 41 | let update: (t<'a>, Chain.t, 'a => 'a) => t<'a> = (map, chain, updateFn) => 42 | Map.update(map, chain, opt => opt->Option.map(updateFn)) 43 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/ChainMap.resi: -------------------------------------------------------------------------------- 1 | module Chain: { 2 | type t 3 | 4 | external toChainId: t => int = "%identity" 5 | 6 | let toString: t => string 7 | 8 | let makeUnsafe: (~chainId: int) => t 9 | } 10 | 11 | type t<'a> 12 | let fromArrayUnsafe: array<(Chain.t, 'a)> => t<'a> 13 | let get: (t<'a>, Chain.t) => 'a 14 | let set: (t<'a>, Chain.t, 'a) => t<'a> 15 | let values: t<'a> => array<'a> 16 | let keys: t<'a> => array 17 | let entries: t<'a> => array<(Chain.t, 'a)> 18 | let has: (t<'a>, Chain.t) => bool 19 | let map: (t<'a>, 'a => 'b) => t<'b> 20 | let mapWithKey: (t<'a>, (Chain.t, 'a) => 'b) => t<'b> 21 | let size: t<'a> => int 22 | let update: (t<'a>, Chain.t, 'a => 'a) => t<'a> 23 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/Envio.gen.ts: -------------------------------------------------------------------------------- 1 | /* TypeScript file generated from Envio.res by genType. */ 2 | 3 | /* eslint-disable */ 4 | /* tslint:disable */ 5 | 6 | import type {EffectContext as $$effectContext} from './Types.ts'; 7 | 8 | import type {Effect as $$effect} from './Types.ts'; 9 | 10 | import type {Logger as $$logger} from './Types.ts'; 11 | 12 | import type {S_t as RescriptSchema_S_t} from 'rescript-schema/RescriptSchema.gen'; 13 | 14 | export type logger = $$logger; 15 | 16 | export type effect = $$effect; 17 | 18 | export type effectOptions = { 19 | /** The name of the effect. Used for logging and debugging. */ 20 | readonly name: string; 21 | /** The input schema of the effect. */ 22 | readonly input: RescriptSchema_S_t; 23 | /** The output schema of the effect. */ 24 | readonly output: RescriptSchema_S_t 25 | }; 26 | 27 | export type effectContext = $$effectContext; 28 | 29 | export type effectArgs = { readonly input: input; readonly context: effectContext }; 30 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/Envio.res: -------------------------------------------------------------------------------- 1 | // The file with public API. 2 | // Should be an entry point after we get rid of the generated project. 3 | // Don't forget to keep index.d.ts in sync with this file. 4 | 5 | @genType.import(("./Types.ts", "Logger")) 6 | type logger = { 7 | debug: 'params. (string, ~params: {..} as 'params=?) => unit, 8 | info: 'params. (string, ~params: {..} as 'params=?) => unit, 9 | warn: 'params. (string, ~params: {..} as 'params=?) => unit, 10 | error: 'params. (string, ~params: {..} as 'params=?) => unit, 11 | errorWithExn: (string, exn) => unit, 12 | } 13 | 14 | @@warning("-30") // Duplicated type names (input) 15 | @genType.import(("./Types.ts", "Effect")) 16 | type rec effect<'input, 'output> 17 | @genType 18 | and effectOptions<'input, 'output> = { 19 | /** The name of the effect. Used for logging and debugging. */ 20 | name: string, 21 | /** The input schema of the effect. */ 22 | input: S.t<'input>, 23 | /** The output schema of the effect. */ 24 | output: S.t<'output>, 25 | } 26 | @genType.import(("./Types.ts", "EffectContext")) 27 | and effectContext = { 28 | log: logger, 29 | effect: 'input 'output. (effect<'input, 'output>, 'input) => promise<'output>, 30 | } 31 | @genType 32 | and effectArgs<'input> = { 33 | input: 'input, 34 | context: effectContext, 35 | } 36 | @@warning("+30") 37 | 38 | let experimental_createEffect = ( 39 | options: effectOptions<'input, 'output>, 40 | handler: effectArgs<'input> => promise<'output>, 41 | ) => { 42 | { 43 | name: options.name, 44 | handler: handler->( 45 | Utils.magic: (effectArgs<'input> => promise<'output>) => Internal.effectArgs => promise< 46 | Internal.effectOutput, 47 | > 48 | ), 49 | }->(Utils.magic: Internal.effect => effect<'input, 'output>) 50 | } 51 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/ErrorHandling.res: -------------------------------------------------------------------------------- 1 | type t = {logger: Pino.t, exn: exn, msg: option} 2 | 3 | let make = (exn, ~logger=Logging.getLogger(), ~msg=?) => { 4 | {logger, msg, exn} 5 | } 6 | 7 | let log = (self: t) => { 8 | switch self { 9 | | {exn, msg: Some(msg), logger} => 10 | logger->Logging.childErrorWithExn(exn->Internal.prettifyExn, msg) 11 | | {exn, msg: None, logger} => logger->Logging.childError(exn->Internal.prettifyExn) 12 | } 13 | } 14 | 15 | let raiseExn = (self: t) => { 16 | self.exn->Internal.prettifyExn->raise 17 | } 18 | 19 | let mkLogAndRaise = (~logger=?, ~msg=?, exn) => { 20 | let exn = exn->Internal.prettifyExn 21 | exn->make(~logger?, ~msg?)->log 22 | exn->raise 23 | } 24 | 25 | let unwrapLogAndRaise = (~logger=?, ~msg=?, result) => { 26 | switch result { 27 | | Ok(v) => v 28 | | Error(exn) => exn->mkLogAndRaise(~logger?, ~msg?) 29 | } 30 | } 31 | 32 | let logAndRaise = self => { 33 | self->log 34 | self->raiseExn 35 | } 36 | 37 | /** 38 | An environment to manage control flow propogating results 39 | with Error that contain ErrorHandling.t in async 40 | contexts and avoid nested switch statements on awaited promises 41 | Similar to rust result propogation 42 | */ 43 | module ResultPropogateEnv = { 44 | exception ErrorHandlingEarlyReturn(t) 45 | 46 | type resultWithErrorHandle<'a> = result<'a, t> 47 | type asyncBody<'a> = unit => promise> 48 | 49 | let runAsyncEnv = async (body: asyncBody<'a>) => { 50 | switch await body() { 51 | | exception ErrorHandlingEarlyReturn(e) => Error(e) 52 | | endReturn => endReturn 53 | } 54 | } 55 | 56 | let propogate = (res: resultWithErrorHandle<'a>) => 57 | switch res { 58 | | Ok(v) => v 59 | | Error(e) => raise(ErrorHandlingEarlyReturn(e)) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/EvmTypes.res: -------------------------------------------------------------------------------- 1 | module Hex = { 2 | type t 3 | /**No string validation in schema*/ 4 | let schema = 5 | S.string->S.setName("EVM.Hex")->(Utils.magic: S.t => S.t) 6 | external fromStringUnsafe: string => t = "%identity" 7 | external fromStringsUnsafe: array => array = "%identity" 8 | external toString: t => string = "%identity" 9 | external toStrings: array => array = "%identity" 10 | } 11 | 12 | module Abi = { 13 | type t 14 | } 15 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/Throttler.res: -------------------------------------------------------------------------------- 1 | type t = { 2 | mutable lastRunTimeMillis: float, 3 | mutable isRunning: bool, 4 | mutable isAwaitingInterval: bool, 5 | mutable scheduled: option promise>, 6 | intervalMillis: float, 7 | logger: Pino.t, 8 | } 9 | 10 | let make = (~intervalMillis: int, ~logger) => { 11 | lastRunTimeMillis: 0., 12 | isRunning: false, 13 | isAwaitingInterval: false, 14 | scheduled: None, 15 | intervalMillis: intervalMillis->Belt.Int.toFloat, 16 | logger, 17 | } 18 | 19 | let rec startInternal = async (throttler: t) => { 20 | switch throttler { 21 | | {scheduled: Some(fn), isRunning: false, isAwaitingInterval: false} => 22 | let timeSinceLastRun = Js.Date.now() -. throttler.lastRunTimeMillis 23 | 24 | //Only execute if we are passed the interval 25 | if timeSinceLastRun >= throttler.intervalMillis { 26 | throttler.isRunning = true 27 | throttler.scheduled = None 28 | throttler.lastRunTimeMillis = Js.Date.now() 29 | 30 | switch await fn() { 31 | | exception exn => 32 | throttler.logger->Pino.errorExn( 33 | Pino.createPinoMessageWithError("Scheduled action failed in throttler", exn), 34 | ) 35 | | _ => () 36 | } 37 | throttler.isRunning = false 38 | 39 | await throttler->startInternal 40 | } else { 41 | //Store isAwaitingInterval in state so that timers don't continuously get created 42 | throttler.isAwaitingInterval = true 43 | let _ = Js.Global.setTimeout(() => { 44 | throttler.isAwaitingInterval = false 45 | throttler->startInternal->ignore 46 | }, Belt.Int.fromFloat(throttler.intervalMillis -. timeSinceLastRun)) 47 | } 48 | | _ => () 49 | } 50 | } 51 | 52 | let schedule = (throttler: t, fn) => { 53 | throttler.scheduled = Some(fn) 54 | throttler->startInternal->ignore 55 | } 56 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/Throttler.resi: -------------------------------------------------------------------------------- 1 | /** 2 | Throttles a scheduled function to run at a minimum given interval 3 | 4 | Does NOT queue scheduled functions but rather overwrites them 5 | on each schedule call. 6 | */ 7 | type t 8 | 9 | /** 10 | Creates a throttler that throttles scheduled functions to run at a minimum 11 | given interval in milliseconds. 12 | 13 | Does NOT queue scheduled functions but rather overwrites them 14 | 15 | The logger will be used to log any errors that occur in the scheduled 16 | functions. 17 | */ 18 | let make: (~intervalMillis: int, ~logger: Pino.t) => t 19 | 20 | /** 21 | Schedules a function to be run on a throttler, overwriting any 22 | previously scheduled functions. Should only be used for functions 23 | that do not need to be executed if there is a more up to date scheduled 24 | function available. 25 | */ 26 | let schedule: (t, unit => promise) => unit 27 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/Time.res: -------------------------------------------------------------------------------- 1 | let resolvePromiseAfterDelay = (~delayMilliseconds) => Utils.delay(delayMilliseconds) 2 | 3 | let rec retryAsyncWithExponentialBackOff = async ( 4 | ~backOffMillis=100, 5 | ~multiplicative=4, 6 | ~retryCount=0, 7 | ~maxRetries=5, 8 | ~logger: Pino.t, 9 | f: unit => promise<'a>, 10 | ) => { 11 | try { 12 | await f() 13 | } catch { 14 | | exn => 15 | if retryCount < maxRetries { 16 | let nextRetryCount = retryCount + 1 17 | let log = retryCount === 0 ? Logging.childTrace : Logging.childWarn 18 | logger->log({ 19 | "msg": `Retrying query ${nextRetryCount->Belt.Int.toString}/${maxRetries->Belt.Int.toString} in ${backOffMillis->Belt.Int.toString}ms - waiting for correct result.`, 20 | "err": exn->Internal.prettifyExn, 21 | }) 22 | await resolvePromiseAfterDelay(~delayMilliseconds=backOffMillis) 23 | 24 | await f->retryAsyncWithExponentialBackOff( 25 | ~backOffMillis=backOffMillis * multiplicative, 26 | ~multiplicative, 27 | ~retryCount=nextRetryCount, 28 | ~maxRetries, 29 | ~logger, 30 | ) 31 | } else { 32 | exn 33 | ->ErrorHandling.make( 34 | ~logger, 35 | ~msg=`Failure. Max retries ${retryCount->Belt.Int.toString}/${maxRetries->Belt.Int.toString} exceeded`, 36 | ) 37 | ->ErrorHandling.log 38 | await Promise.reject(exn->Js.Exn.anyToExnInternal) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/TopicFilter.res: -------------------------------------------------------------------------------- 1 | let toTwosComplement = (num: bigint, ~bytesLen: int) => { 2 | let maxValue = 1n->BigInt.Bitwise.shift_left(BigInt.fromInt(bytesLen * 8)) 3 | let mask = maxValue->BigInt.sub(1n) 4 | num->BigInt.add(maxValue)->BigInt.Bitwise.logand(mask) 5 | } 6 | 7 | let fromSignedBigInt = val => { 8 | let bytesLen = 32 9 | let val = val >= 0n ? val : val->toTwosComplement(~bytesLen) 10 | val->Viem.bigintToHex(~options={size: bytesLen}) 11 | } 12 | 13 | type hex = EvmTypes.Hex.t 14 | //bytes currently does not work with genType and we also currently generate bytes as a string type 15 | type bytesHex = string 16 | let keccak256 = Viem.keccak256 17 | let bytesToHex = Viem.bytesToHex 18 | let concat = Viem.concat 19 | let castToHexUnsafe: 'a => hex = val => val->Utils.magic 20 | let fromBigInt: bigint => hex = val => val->Viem.bigintToHex(~options={size: 32}) 21 | let fromDynamicString: string => hex = val => val->(Utils.magic: string => hex)->keccak256 22 | let fromString: string => hex = val => val->Viem.stringToHex(~options={size: 32}) 23 | let fromAddress: Address.t => hex = addr => addr->(Utils.magic: Address.t => hex)->Viem.pad 24 | let fromDynamicBytes: bytesHex => hex = bytes => bytes->(Utils.magic: bytesHex => hex)->keccak256 25 | let fromBytes: bytesHex => hex = bytes => 26 | bytes->(Utils.magic: bytesHex => bytes)->Viem.bytesToHex(~options={size: 32}) 27 | let fromBool: bool => hex = b => b->Viem.boolToHex(~options={size: 32}) 28 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/Types.ts: -------------------------------------------------------------------------------- 1 | export type Invalid = never; 2 | 3 | export type Address = string; 4 | 5 | export type Logger = { 6 | readonly debug: ( 7 | message: string, 8 | params?: Record | Error 9 | ) => void; 10 | readonly info: ( 11 | message: string, 12 | params?: Record | Error 13 | ) => void; 14 | readonly warn: ( 15 | message: string, 16 | params?: Record | Error 17 | ) => void; 18 | readonly error: ( 19 | message: string, 20 | params?: Record | Error 21 | ) => void; 22 | }; 23 | 24 | export abstract class Effect { 25 | protected opaque!: I | O; 26 | } 27 | 28 | export type EffectCaller = ( 29 | effect: Effect, 30 | // This is a hack to make the call complain on undefined 31 | // when it's not needed, instead of extending the input type. 32 | // Might be not needed if I misunderstand something in TS. 33 | input: I extends undefined ? undefined : I 34 | ) => Promise; 35 | 36 | export type EffectContext = { 37 | /** 38 | * Access the logger instance with event as a context. The logs will be displayed in the console and Envio Hosted Service. 39 | */ 40 | readonly log: Logger; 41 | /** 42 | * Call the provided Effect with the given input. 43 | * Effects are the best for external calls with automatic deduplication, error handling and caching. 44 | * Define a new Effect using createEffect outside of the handler. 45 | */ 46 | readonly effect: EffectCaller; 47 | }; 48 | 49 | export type GenericContractRegister = ( 50 | args: Args 51 | ) => void | Promise; 52 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/bindings/BigDecimal.gen.ts: -------------------------------------------------------------------------------- 1 | /* TypeScript file generated from BigDecimal.res by genType. */ 2 | 3 | /* eslint-disable */ 4 | /* tslint:disable */ 5 | 6 | const BigDecimalJS = require('./BigDecimal.bs.js'); 7 | 8 | import type {S_t as RescriptSchema_S_t} from 'rescript-schema/RescriptSchema.gen'; 9 | 10 | import type {default as $$t} from 'bignumber.js'; 11 | 12 | export type t = $$t; 13 | 14 | export const schema: RescriptSchema_S_t = BigDecimalJS.schema as any; 15 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/bindings/Ethers.gen.ts: -------------------------------------------------------------------------------- 1 | /* TypeScript file generated from Ethers.res by genType. */ 2 | 3 | /* eslint-disable */ 4 | /* tslint:disable */ 5 | 6 | const EthersJS = require('./Ethers.bs.js'); 7 | 8 | import type {t as Address_t} from '../../src/Address.gen'; 9 | 10 | export const Addresses_mockAddresses: Address_t[] = EthersJS.Addresses.mockAddresses as any; 11 | 12 | export const Addresses_defaultAddress: Address_t = EthersJS.Addresses.defaultAddress as any; 13 | 14 | export const Addresses: { mockAddresses: Address_t[]; defaultAddress: Address_t } = EthersJS.Addresses as any; 15 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/bindings/Express.res: -------------------------------------------------------------------------------- 1 | // Inspired by https://github.com/bloodyowl/rescript-express 2 | 3 | type app 4 | 5 | // "default" & seem to conflict a bit right now 6 | // https://github.com/rescript-lang/rescript-compiler/issues/5004 7 | @module external makeCjs: unit => app = "express" 8 | @module("express") external make: unit => app = "default" 9 | 10 | type req = private { 11 | headers: dict, 12 | method: Rest.method, 13 | query: dict, 14 | } 15 | type res 16 | 17 | type handler = (req, res) => unit 18 | type middleware = (req, res, unit => unit) => unit 19 | 20 | @module("express") external jsonMiddleware: unit => middleware = "json" 21 | 22 | @send external use: (app, middleware) => unit = "use" 23 | @send external useFor: (app, string, middleware) => unit = "use" 24 | 25 | @send external get: (app, string, handler) => unit = "get" 26 | @send external post: (app, string, handler) => unit = "post" 27 | 28 | type server 29 | 30 | @send external listen: (app, int) => server = "listen" 31 | 32 | // res methods 33 | @send external sendStatus: (res, int) => unit = "sendStatus" 34 | @send external set: (res, string, string) => unit = "set" 35 | @send external json: (res, Js.Json.t) => unit = "json" 36 | @send external endWithData: (res, 'a) => res = "end" 37 | @send external setHeader: (res, string, string) => unit = "setHeader" 38 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/bindings/Hrtime.res: -------------------------------------------------------------------------------- 1 | type seconds = float 2 | type milliseconds = float 3 | type nanoseconds = float 4 | 5 | type timeTuple = (seconds, nanoseconds) 6 | 7 | type timeRef = timeTuple 8 | 9 | type timeElapsed = timeTuple 10 | 11 | @val @scope("process") external makeTimer: unit => timeRef = "hrtime" 12 | 13 | @val @scope("process") external timeSince: timeRef => timeElapsed = "hrtime" 14 | 15 | let nanoToMilli = (nano: nanoseconds): milliseconds => nano /. 1_000_000. 16 | let secToMilli = (sec: seconds): milliseconds => sec *. 1_000. 17 | 18 | let nanoToTimeTuple = (nano: nanoseconds): timeTuple => { 19 | let factor = 1_000_000_000. 20 | let seconds = Js.Math.floor_float(nano /. factor) 21 | let nanos = mod_float(nano, factor) 22 | (seconds, nanos) 23 | } 24 | 25 | let timeElapsedToNewRef = (elapsed: timeElapsed, ref: timeRef): timeRef => { 26 | let (elapsedSeconds, elapsedNano) = elapsed 27 | let (refSeconds, refNano) = ref 28 | 29 | let (nanoExtraSeconds, remainderNanos) = nanoToTimeTuple(elapsedNano +. refNano) 30 | (elapsedSeconds +. refSeconds +. nanoExtraSeconds, remainderNanos) 31 | } 32 | 33 | let toMillis = ((sec, nano): timeElapsed): milliseconds => { 34 | sec->secToMilli +. nano->nanoToMilli 35 | } 36 | 37 | let toInt = float => float->Belt.Int.fromFloat 38 | let intFromMillis = toInt 39 | let intFromNanos = toInt 40 | let intFromSeconds = toInt 41 | let floatFromMillis = Utils.magic 42 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/bindings/Hrtime.resi: -------------------------------------------------------------------------------- 1 | type seconds 2 | type milliseconds 3 | type nanoseconds 4 | 5 | type timeTuple = (seconds, nanoseconds) 6 | 7 | type timeRef 8 | 9 | type timeElapsed = timeTuple 10 | 11 | let makeTimer: unit => timeRef 12 | 13 | let timeSince: timeRef => timeElapsed 14 | 15 | let nanoToMilli: nanoseconds => milliseconds 16 | let secToMilli: seconds => milliseconds 17 | 18 | let timeElapsedToNewRef: (timeElapsed, timeRef) => timeRef 19 | let toMillis: timeElapsed => milliseconds 20 | 21 | let intFromMillis: milliseconds => int 22 | let intFromNanos: nanoseconds => int 23 | let intFromSeconds: seconds => int 24 | let floatFromMillis: milliseconds => float 25 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/bindings/Pino.gen.ts: -------------------------------------------------------------------------------- 1 | /* TypeScript file generated from Pino.res by genType. */ 2 | 3 | /* eslint-disable */ 4 | /* tslint:disable */ 5 | 6 | export type logLevelUser = "udebug" | "uinfo" | "uwarn" | "uerror"; 7 | 8 | export abstract class pinoMessageBlob { protected opaque!: any }; /* simulate opaque types */ 9 | 10 | export type t = { 11 | readonly trace: (_1:pinoMessageBlob) => void; 12 | readonly debug: (_1:pinoMessageBlob) => void; 13 | readonly info: (_1:pinoMessageBlob) => void; 14 | readonly warn: (_1:pinoMessageBlob) => void; 15 | readonly error: (_1:pinoMessageBlob) => void; 16 | readonly fatal: (_1:pinoMessageBlob) => void 17 | }; 18 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/bindings/Promise.res: -------------------------------------------------------------------------------- 1 | type t<+'a> = promise<'a> 2 | 3 | @new 4 | external make: ((@uncurry 'a => unit, 'e => unit) => unit) => t<'a> = "Promise" 5 | 6 | @val @scope("Promise") 7 | external resolve: 'a => t<'a> = "resolve" 8 | 9 | @send external then: (t<'a>, @uncurry 'a => t<'b>) => t<'b> = "then" 10 | 11 | @send 12 | external thenResolve: (t<'a>, @uncurry 'a => 'b) => t<'b> = "then" 13 | 14 | @send external finally: (t<'a>, unit => unit) => t<'a> = "finally" 15 | 16 | @scope("Promise") @val 17 | external reject: exn => t<_> = "reject" 18 | 19 | @scope("Promise") @val 20 | external all: array> => t> = "all" 21 | 22 | @scope("Promise") @val 23 | external all2: ((t<'a>, t<'b>)) => t<('a, 'b)> = "all" 24 | 25 | @scope("Promise") @val 26 | external all3: ((t<'a>, t<'b>, t<'c>)) => t<('a, 'b, 'c)> = "all" 27 | 28 | @scope("Promise") @val 29 | external all4: ((t<'a>, t<'b>, t<'c>, t<'d>)) => t<('a, 'b, 'c, 'd)> = "all" 30 | 31 | @scope("Promise") @val 32 | external all5: ((t<'a>, t<'b>, t<'c>, t<'d>, t<'e>)) => t<('a, 'b, 'c, 'd, 'e)> = "all" 33 | 34 | @scope("Promise") @val 35 | external all6: ((t<'a>, t<'b>, t<'c>, t<'d>, t<'e>, t<'f>)) => t<('a, 'b, 'c, 'd, 'e, 'f)> = "all" 36 | 37 | @send 38 | external catch: (t<'a>, @uncurry exn => t<'a>) => t<'a> = "catch" 39 | 40 | let catch = (promise: promise<'a>, callback: exn => promise<'a>): promise<'a> => { 41 | catch(promise, err => { 42 | callback(Js.Exn.anyToExnInternal(err)) 43 | }) 44 | } 45 | 46 | @scope("Promise") @val 47 | external race: array> => t<'a> = "race" 48 | 49 | external done: promise<'a> => unit = "%ignore" 50 | 51 | external unsafe_async: 'a => promise<'a> = "%identity" 52 | external unsafe_await: promise<'a> => 'a = "?await" 53 | 54 | let isCatchable: 'any => bool = %raw(`value => value && typeof value.catch === 'function'`) 55 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/bindings/SDSL.res: -------------------------------------------------------------------------------- 1 | module Queue = { 2 | type t<'a> 3 | 4 | @module("js-sdsl") @new external make: unit => t<'a> = "Queue" 5 | 6 | type containerSize = int 7 | @send external size: t<'a> => containerSize = "size" 8 | @send external push: (t<'a>, 'a) => containerSize = "push" 9 | @send external pop: t<'a> => option<'a> = "pop" 10 | //Returns the front item without popping it 11 | @send external front: t<'a> => option<'a> = "front" 12 | } 13 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/bindings/Viem.res: -------------------------------------------------------------------------------- 1 | type eventLog = { 2 | abi: EvmTypes.Abi.t, 3 | data: string, 4 | topics: array, 5 | } 6 | 7 | type decodedEvent<'a> = { 8 | eventName: string, 9 | args: 'a, 10 | } 11 | 12 | @module("viem") external decodeEventLogOrThrow: eventLog => decodedEvent<'a> = "decodeEventLog" 13 | 14 | type hex = EvmTypes.Hex.t 15 | @module("viem") external toHex: 'a => hex = "toHex" 16 | @module("viem") external keccak256: hex => hex = "keccak256" 17 | @module("viem") external keccak256Bytes: bytes => hex = "keccak256" 18 | @module("viem") external pad: hex => hex = "pad" 19 | @module("viem") 20 | external encodePacked: (~types: array, ~values: array<'a>) => hex = "encodePacked" 21 | 22 | type sizeOptions = {size: int} 23 | @module("viem") external intToHex: (int, ~options: sizeOptions=?) => hex = "numberToHex" 24 | @module("viem") external bigintToHex: (bigint, ~options: sizeOptions=?) => hex = "numberToHex" 25 | @module("viem") external stringToHex: (string, ~options: sizeOptions=?) => hex = "stringToHex" 26 | @module("viem") external boolToHex: (bool, ~options: sizeOptions=?) => hex = "boolToHex" 27 | @module("viem") external bytesToHex: (bytes, ~options: sizeOptions=?) => hex = "bytesToHex" 28 | @module("viem") external concat: array => hex = "concat" 29 | 30 | exception ParseError(exn) 31 | exception UnknownContractName({contractName: string}) 32 | 33 | let parseLogOrThrow = ( 34 | contractNameAbiMapping: dict, 35 | ~contractName, 36 | ~topics, 37 | ~data, 38 | ) => { 39 | switch contractNameAbiMapping->Utils.Dict.dangerouslyGetNonOption(contractName) { 40 | | None => raise(UnknownContractName({contractName: contractName})) 41 | | Some(abi) => 42 | let viemLog: eventLog = { 43 | abi, 44 | data, 45 | topics, 46 | } 47 | 48 | try viemLog->decodeEventLogOrThrow catch { 49 | | exn => raise(ParseError(exn)) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/db/Schema.res: -------------------------------------------------------------------------------- 1 | open Belt 2 | type t = dict 3 | 4 | let make = (tables: array) => { 5 | tables->Array.map(table => (table.tableName, table))->Js.Dict.fromArray 6 | } 7 | 8 | exception UndefinedEntity(Table.derivedFromField) 9 | exception UndefinedFieldInEntity(Table.derivedFromField) 10 | let getDerivedFromFieldName = (schema: t, derivedFromField: Table.derivedFromField) => 11 | switch schema->Utils.Dict.dangerouslyGetNonOption(derivedFromField.derivedFromEntity) { 12 | | Some(entity) => 13 | switch entity->Table.getFieldByName(derivedFromField.derivedFromField) { 14 | | Some(field) => field->Table.getFieldName->Ok 15 | | None => Error(UndefinedFieldInEntity(derivedFromField)) //Unexpected, schema should be parsed on codegen 16 | } 17 | | None => Error(UndefinedEntity(derivedFromField)) //Unexpected, schema should be parsed on codegen 18 | } 19 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/sources/Fuel.res: -------------------------------------------------------------------------------- 1 | type receiptType = 2 | | @as(0) Call 3 | | @as(1) Return 4 | | @as(2) ReturnData 5 | | @as(3) Panic 6 | | @as(4) Revert 7 | | @as(5) Log 8 | | @as(6) LogData 9 | // Transfer is to another contract, TransferOut is to wallet address 10 | | @as(7) Transfer 11 | | @as(8) TransferOut 12 | | @as(9) ScriptResult 13 | | @as(10) MessageOut 14 | | @as(11) Mint 15 | | @as(12) Burn 16 | 17 | @module("./vendored-fuel-abi-coder.js") 18 | external transpileAbi: Js.Json.t => Ethers.abi = "transpileAbi" 19 | 20 | @module("./vendored-fuel-abi-coder.js") @scope("AbiCoder") 21 | external getLogDecoder: (~abi: Ethers.abi, ~logId: string) => string => unknown = "getLogDecoder" 22 | 23 | module Receipt = { 24 | @tag("receiptType") 25 | type t = 26 | | @as(0) Call({assetId: string, amount: bigint, to: string}) 27 | | @as(6) LogData({data: string, rb: bigint}) 28 | | @as(7) Transfer({amount: bigint, assetId: string, to: string}) 29 | | @as(8) TransferOut({amount: bigint, assetId: string, toAddress: string}) 30 | | @as(11) Mint({val: bigint, subId: string}) 31 | | @as(12) Burn({val: bigint, subId: string}) 32 | 33 | let getLogDataDecoder = (~abi: Ethers.abi, ~logId: string) => { 34 | let decode = getLogDecoder(~abi, ~logId) 35 | data => data->decode->Utils.magic 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/sources/HyperFuel.resi: -------------------------------------------------------------------------------- 1 | type hyperSyncPage<'item> = { 2 | items: array<'item>, 3 | nextBlock: int, 4 | archiveHeight: int, 5 | } 6 | 7 | type block = { 8 | id: string, 9 | time: int, 10 | height: int, 11 | } 12 | 13 | type item = { 14 | transactionId: string, 15 | contractId: Address.t, 16 | receipt: Fuel.Receipt.t, 17 | receiptIndex: int, 18 | block: block, 19 | } 20 | 21 | type blockNumberAndHash = { 22 | blockNumber: int, 23 | hash: string, 24 | } 25 | 26 | type logsQueryPage = hyperSyncPage 27 | 28 | type missingParams = { 29 | queryName: string, 30 | missingParams: array, 31 | } 32 | type queryError = UnexpectedMissingParams(missingParams) 33 | 34 | let queryErrorToMsq: queryError => string 35 | 36 | type queryResponse<'a> = result<'a, queryError> 37 | 38 | module GetLogs: { 39 | type error = 40 | | UnexpectedMissingParams({missingParams: array}) 41 | | WrongInstance 42 | 43 | exception Error(error) 44 | 45 | let query: ( 46 | ~serverUrl: string, 47 | ~fromBlock: int, 48 | ~toBlock: option, 49 | ~recieptsSelection: array, 50 | ) => promise 51 | } 52 | 53 | let queryBlockData: ( 54 | ~serverUrl: string, 55 | ~blockNumber: int, 56 | ~logger: Pino.t, 57 | ) => promise> 58 | 59 | let heightRoute: Rest.route 60 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/sources/HyperSync.resi: -------------------------------------------------------------------------------- 1 | type hyperSyncPage<'item> = { 2 | items: array<'item>, 3 | nextBlock: int, 4 | archiveHeight: int, 5 | rollbackGuard: option, 6 | events: array, 7 | } 8 | 9 | module Log: { 10 | type t = { 11 | address: Address.t, 12 | data: string, 13 | topics: array, 14 | logIndex: int, 15 | } 16 | } 17 | 18 | type logsQueryPageItem = { 19 | log: Log.t, 20 | block: Internal.eventBlock, 21 | transaction: Internal.eventTransaction, 22 | } 23 | 24 | type logsQueryPage = hyperSyncPage 25 | 26 | type missingParams = { 27 | queryName: string, 28 | missingParams: array, 29 | } 30 | 31 | type queryError = UnexpectedMissingParams(missingParams) 32 | 33 | let queryErrorToMsq: queryError => string 34 | 35 | type queryResponse<'a> = result<'a, queryError> 36 | 37 | module GetLogs: { 38 | type error = 39 | | UnexpectedMissingParams({missingParams: array}) 40 | | WrongInstance 41 | 42 | exception Error(error) 43 | 44 | let query: ( 45 | ~client: HyperSyncClient.t, 46 | ~fromBlock: int, 47 | ~toBlock: option, 48 | ~logSelections: array, 49 | ~fieldSelection: HyperSyncClient.QueryTypes.fieldSelection, 50 | ~nonOptionalBlockFieldNames: array, 51 | ~nonOptionalTransactionFieldNames: array, 52 | ) => promise 53 | } 54 | 55 | let queryBlockData: ( 56 | ~serverUrl: string, 57 | ~apiToken: string, 58 | ~blockNumber: int, 59 | ~logger: Pino.t, 60 | ) => promise>> 61 | 62 | let queryBlockDataMulti: ( 63 | ~serverUrl: string, 64 | ~apiToken: string, 65 | ~blockNumbers: array, 66 | ~logger: Pino.t, 67 | ) => promise>> 68 | 69 | let mapExn: queryResponse<'a> => result<'a, exn> 70 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/sources/HyperSyncClient.gen.ts: -------------------------------------------------------------------------------- 1 | /* TypeScript file generated from HyperSyncClient.res by genType. */ 2 | 3 | /* eslint-disable */ 4 | /* tslint:disable */ 5 | 6 | import type {t as Address_t} from '../../src/Address.gen'; 7 | 8 | export type ResponseTypes_accessList = { readonly address?: Address_t; readonly storageKeys?: string[] }; 9 | 10 | export type ResponseTypes_authorizationList = { 11 | readonly chainId: bigint; 12 | readonly address: Address_t; 13 | readonly nonce: number; 14 | readonly yParity: 15 | 1 16 | | 0; 17 | readonly r: string; 18 | readonly s: string 19 | }; 20 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/envio/src/sources/SourceManager.resi: -------------------------------------------------------------------------------- 1 | type t 2 | 3 | let make: ( 4 | ~sources: array, 5 | ~maxPartitionConcurrency: int, 6 | ~newBlockFallbackStallTimeout: int=?, 7 | ~stalledPollingInterval: int=?, 8 | ~getHeightRetryInterval: (~retry: int) => int=?, 9 | ) => t 10 | 11 | let getActiveSource: t => Source.t 12 | 13 | let fetchNext: ( 14 | t, 15 | ~fetchState: FetchState.t, 16 | ~currentBlockHeight: int, 17 | ~executeQuery: FetchState.query => promise, 18 | ~waitForNewBlock: (~currentBlockHeight: int) => promise, 19 | ~onNewBlock: (~currentBlockHeight: int) => unit, 20 | ~targetBufferSize: int, 21 | ~stateId: int, 22 | ) => promise 23 | 24 | let waitForNewBlock: (t, ~currentBlockHeight: int) => promise 25 | 26 | let executeQuery: ( 27 | t, 28 | ~query: FetchState.query, 29 | ~currentBlockHeight: int, 30 | ) => promise 31 | 32 | let makeGetHeightRetryInterval: ( 33 | ~initialRetryInterval: int, 34 | ~backoffMultiplicative: int, 35 | ~maxRetryInterval: int, 36 | ) => (~retry: int) => int 37 | -------------------------------------------------------------------------------- /codegenerator/cli/npm/package.json.tmpl: -------------------------------------------------------------------------------- 1 | { 2 | "name": "${node_pkg}", 3 | "version": "${version}", 4 | "description": "A latency and sync speed optimized, developer friendly blockchain data indexer.", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/enviodev/hyperindex.git" 8 | }, 9 | "keywords": [ 10 | "blockchain", 11 | "indexer", 12 | "ethereum", 13 | "data", 14 | "dapp" 15 | ], 16 | "author": "envio contributors ", 17 | "license": "GPL-3.0", 18 | "bugs": { 19 | "url": "https://github.com/enviodev/hyperindex/issues" 20 | }, 21 | "homepage": "https://envio.dev", 22 | "os": [ 23 | "${node_os}" 24 | ], 25 | "cpu": [ 26 | "${node_arch}" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /codegenerator/cli/src/cli_args/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod clap_definitions; 2 | pub mod init_config; 3 | pub mod interactive_init; 4 | -------------------------------------------------------------------------------- /codegenerator/cli/src/config_parsing/hypersync_endpoints.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | 3 | use super::chain_helpers::{HypersyncNetwork, Network}; 4 | 5 | pub fn network_to_hypersync_url(network: &HypersyncNetwork) -> String { 6 | format!("https://{}.hypersync.xyz", *network as u64) 7 | } 8 | 9 | pub fn get_default_hypersync_endpoint(chain_id: u64) -> anyhow::Result { 10 | let network_name = Network::from_network_id(chain_id) 11 | .context(format!("Getting network name from id ({})", chain_id))?; 12 | 13 | let network = HypersyncNetwork::try_from(network_name).context(format!( 14 | "Unsupported network (name: {}, id: {}) provided for hypersync", 15 | network_name, chain_id 16 | ))?; 17 | 18 | Ok(network_to_hypersync_url(&network)) 19 | } 20 | 21 | #[cfg(test)] 22 | mod test { 23 | 24 | use crate::config_parsing::hypersync_endpoints::get_default_hypersync_endpoint; 25 | 26 | use super::HypersyncNetwork; 27 | use strum::IntoEnumIterator; 28 | 29 | #[test] 30 | fn all_supported_chain_ids_return_a_hypersync_endpoint() { 31 | for network in HypersyncNetwork::iter() { 32 | let _ = get_default_hypersync_endpoint(network as u64).unwrap(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /codegenerator/cli/src/config_parsing/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod chain_helpers; 2 | pub mod contract_import; 3 | pub mod entity_parsing; 4 | pub mod event_parsing; 5 | pub mod graph_migration; 6 | pub mod human_config; 7 | pub mod hypersync_endpoints; 8 | pub mod postgres_types; 9 | pub mod system_config; 10 | pub mod validation; 11 | -------------------------------------------------------------------------------- /codegenerator/cli/src/evm/abi.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | 3 | #[derive(Deserialize)] 4 | #[serde(untagged)] 5 | pub enum AbiOrNestedAbi { 6 | Abi(ethers::abi::Abi), 7 | // This is a case for Hardhat or Foundry generated ABI files 8 | NestedAbi { abi: ethers::abi::Abi }, 9 | } 10 | -------------------------------------------------------------------------------- /codegenerator/cli/src/evm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod abi; 2 | pub mod address; 3 | -------------------------------------------------------------------------------- /codegenerator/cli/src/executor/codegen.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | commands, 3 | config_parsing::system_config::SystemConfig, 4 | persisted_state::{PersistedStateExists, CURRENT_CRATE_VERSION}, 5 | project_paths::ParsedProjectPaths, 6 | }; 7 | use anyhow::{Context, Result}; 8 | 9 | pub async fn run_codegen(project_paths: &ParsedProjectPaths) -> Result<()> { 10 | //Manage purging of gengerated folder 11 | match PersistedStateExists::get_persisted_state_file(project_paths) { 12 | PersistedStateExists::Exists(ps) if ps.envio_version != CURRENT_CRATE_VERSION => { 13 | println!( 14 | "Envio version '{}' does not match the previous version '{}' used in the \ 15 | generated directory", 16 | CURRENT_CRATE_VERSION, &ps.envio_version 17 | ); 18 | println!("Purging generated directory",); 19 | commands::codegen::remove_files_except_git(&project_paths.generated) 20 | .await 21 | .context("Failed purging generated")?; 22 | } 23 | _ => (), 24 | }; 25 | 26 | let config = 27 | SystemConfig::parse_from_project_files(project_paths).context("Failed parsing config")?; 28 | 29 | commands::codegen::run_codegen(&config, project_paths).await?; 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /codegenerator/cli/src/fuel/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod abi; 2 | pub mod address; 3 | -------------------------------------------------------------------------------- /codegenerator/cli/src/hbs_templating/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod codegen_templates; 2 | pub mod contract_import_templates; 3 | pub mod hbs_dir_generator; 4 | pub mod init_templates; 5 | -------------------------------------------------------------------------------- /codegenerator/cli/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod cli_args; 2 | pub use cli_args::clap_definitions; 3 | pub use cli_args::init_config; 4 | mod commands; 5 | pub mod config_parsing; 6 | pub mod constants; 7 | mod evm; 8 | pub mod executor; 9 | mod fuel; 10 | mod hbs_templating; 11 | mod persisted_state; 12 | mod project_paths; 13 | mod rescript_types; 14 | pub mod scripts; 15 | mod service_health; 16 | mod template_dirs; 17 | mod utils; 18 | -------------------------------------------------------------------------------- /codegenerator/cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use clap::Parser; 3 | use envio::{clap_definitions::CommandLineArgs, executor}; 4 | 5 | #[tokio::main] 6 | async fn main() -> Result<()> { 7 | let command_line_args = CommandLineArgs::parse(); 8 | executor::execute(command_line_args) 9 | .await 10 | .context("Failed cli execution")?; 11 | 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /codegenerator/cli/src/scripts/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod print_missing_networks; 2 | -------------------------------------------------------------------------------- /codegenerator/cli/src/utils/file_system.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use std::{fs, path::PathBuf}; 3 | 4 | pub async fn write_file_string_to_system(file_string: String, fs_file_path: PathBuf) -> Result<()> { 5 | let file_path_str = fs_file_path.to_str().unwrap_or("unknown file path"); 6 | // Create the directory if it doesn't exist 7 | if let Some(parent_dir) = fs_file_path.parent() { 8 | fs::create_dir_all(parent_dir) 9 | .with_context(|| format!("Failed to create directory for {} file", file_path_str))?; 10 | } 11 | fs::write(&fs_file_path, file_string) 12 | .with_context(|| format!("Failed to write {} file", file_path_str))?; 13 | 14 | Ok(()) 15 | } 16 | -------------------------------------------------------------------------------- /codegenerator/cli/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod file_system; 2 | pub mod normalized_list; 3 | pub mod text; 4 | pub mod unique_hashmap; 5 | -------------------------------------------------------------------------------- /codegenerator/cli/src/utils/unique_hashmap.rs: -------------------------------------------------------------------------------- 1 | use anyhow::anyhow; 2 | use std::collections::HashMap; 3 | 4 | pub fn try_insert(map: &mut HashMap, key: K, val: V) -> anyhow::Result<()> 5 | where 6 | K: std::cmp::Eq + std::hash::Hash + std::fmt::Display, 7 | { 8 | match map.get(&key) { 9 | //If value exists, error without updating 10 | Some(_) => Err(anyhow!("{} already exists, cannot have duplicates", key,)), 11 | None => { 12 | //If not insert it 13 | map.insert(key, val); 14 | Ok(()) 15 | } 16 | } 17 | } 18 | 19 | pub fn from_vec_no_duplicates(v: Vec<(K, V)>) -> anyhow::Result> 20 | where 21 | K: std::cmp::Eq + std::hash::Hash + std::fmt::Display, 22 | { 23 | let mut map = HashMap::new(); 24 | 25 | for (key, val) in v { 26 | try_insert(&mut map, key, val)? 27 | } 28 | 29 | Ok(map) 30 | } 31 | -------------------------------------------------------------------------------- /codegenerator/cli/tarpaulin.toml: -------------------------------------------------------------------------------- 1 | [cli] 2 | "target-dir" = "tarpaulin_target" 3 | "skip-clean" = true 4 | ignored = true 5 | packages = ["envio"] 6 | lib = true 7 | 8 | [report] 9 | out = ["Html"] 10 | "output-dir" = "./" 11 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/codegen/index.d.ts.hbs: -------------------------------------------------------------------------------- 1 | export { 2 | {{#each codegen_contracts as |contract|}} 3 | {{contract.name.capitalized}}, 4 | {{/each}} 5 | } from "./src/Handlers.gen"; 6 | export type * from "./src/Types.gen"; 7 | import { 8 | {{#each codegen_contracts as |contract|}} 9 | {{contract.name.capitalized}}, 10 | {{/each}} 11 | MockDb, 12 | Addresses 13 | } from "./src/TestHelpers.gen"; 14 | 15 | export const TestHelpers = { 16 | {{#each codegen_contracts as |contract|}} 17 | {{contract.name.capitalized}}, 18 | {{/each}} 19 | MockDb, 20 | Addresses 21 | }; 22 | 23 | export { 24 | {{#each gql_enums as |enum|}} 25 | {{enum.name.capitalized}}, 26 | {{/each}} 27 | } from "./src/Enum.gen"; 28 | 29 | export {default as BigDecimal} from 'bignumber.js'; 30 | export type {LoaderContext, HandlerContext} from './src/Types.ts'; 31 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/codegen/persisted_state.envio.json.hbs: -------------------------------------------------------------------------------- 1 | {{persisted_state}} 2 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/codegen/src/ContextEnv.res.hbs: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | let getContractRegisterContext = (~eventItem, ~onRegister) => { 4 | // TODO: only add contracts we've registered for the event in the config 5 | {{#each codegen_contracts as |contract| }} 6 | add{{contract.name.capitalized}}: (contractAddress: Address.t) => { 7 | {{#if is_evm_ecosystem}} {{!-- TODO: Add validation for Fuel --}} 8 | // Even though it's the Address.t type on ReScript side, for TS side it's a string. 9 | // So we need to ensure that it's a valid checksummed address. 10 | let contractAddress = contractAddress->Address.Evm.fromAddressOrThrow 11 | {{/if}} 12 | 13 | onRegister(~eventItem, ~contractAddress, ~contractName=Enums.ContractType.{{contract.name.capitalized}}) 14 | }, 15 | {{/each}} 16 | }->(Utils.magic: Types.contractRegistrations => Internal.contractRegisterContext) 17 | 18 | let getContractRegisterArgs = (eventItem: Internal.eventItem, ~onRegister): Internal.contractRegisterArgs => { 19 | event: eventItem.event, 20 | context: getContractRegisterContext(~eventItem, ~onRegister), 21 | } 22 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/codegen/src/Handlers.res.hbs: -------------------------------------------------------------------------------- 1 | {{#each codegen_contracts as | contract |}} 2 | @genType 3 | module {{contract.name.capitalized}} = { 4 | {{#each contract.codegen_events as | event |}} 5 | module {{event.name}} = Types.MakeRegister(Types.{{contract.name.capitalized}}.{{event.name}}) 6 | {{/each}} 7 | } 8 | 9 | {{/each}} 10 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/codegen/src/Path.res.hbs: -------------------------------------------------------------------------------- 1 | let relativePathToRootFromGenerated = "{{relative_path_to_root_from_generated}}" -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/codegen/src/db/Enums.res.hbs: -------------------------------------------------------------------------------- 1 | module ContractType = { 2 | @genType 3 | type t = 4 | {{#each codegen_contracts as | contract |}} 5 | | @as("{{contract.name.capitalized}}") {{contract.name.capitalized}} 6 | {{/each}} 7 | 8 | let name = "CONTRACT_TYPE" 9 | let variants = [ 10 | {{#each codegen_contracts as | contract |}} 11 | {{contract.name.capitalized}}, 12 | {{/each}} 13 | ] 14 | let config = Internal.makeEnumConfig(~name, ~variants) 15 | } 16 | 17 | module EntityType = { 18 | @genType 19 | type t = 20 | {{#each entities as | entity |}} 21 | | @as("{{entity.name.original}}") {{entity.name.capitalized}} 22 | {{/each}} 23 | | @as("dynamic_contract_registry") DynamicContractRegistry 24 | 25 | let name = "ENTITY_TYPE" 26 | let variants = [ 27 | {{#each entities as | entity |}} 28 | {{entity.name.capitalized}}, 29 | {{/each}} 30 | DynamicContractRegistry, 31 | ] 32 | let config = Internal.makeEnumConfig(~name, ~variants) 33 | } 34 | {{#each gql_enums as | enum |}} 35 | 36 | module {{enum.name.capitalized}} = { 37 | @genType 38 | type t = 39 | {{#each enum.params as | param | }} 40 | | @as("{{param.original}}") {{param.capitalized}} 41 | {{/each}} 42 | 43 | let name = "{{enum.name.capitalized}}" 44 | let variants = [ 45 | {{#each enum.params as | param | }} 46 | {{param.capitalized}}, 47 | {{/each}} 48 | ] 49 | let config = Internal.makeEnumConfig(~name, ~variants) 50 | } 51 | {{/each}} 52 | 53 | let allEnums = ([ 54 | ContractType.config->Internal.fromGenericEnumConfig, 55 | EntityType.config->Internal.fromGenericEnumConfig, 56 | {{#each gql_enums as | enum |}} 57 | {{enum.name.capitalized}}.config->Internal.fromGenericEnumConfig, 58 | {{/each}} 59 | ]) 60 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/contract_import_templates/javascript/src/EventHandlers.js.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | * Please refer to https://docs.envio.dev for a thorough guide on all Envio indexer features 3 | */ 4 | const { 5 | {{#each imported_contracts as |contract|}} 6 | {{contract.name.capitalized}}, 7 | {{/each}} 8 | } = require("generated"); 9 | {{#each imported_contracts as |contract|}} 10 | {{#each contract.imported_events as |event|}} 11 | 12 | {{contract.name.capitalized}}.{{event.name}}.handler(async ({event, context}) => { 13 | const entity = { 14 | id: {{event.entity_id_from_event_code}}, 15 | {{#each event.params as |param|}} 16 | {{param.entity_key.uncapitalized}}: event.params.{{param.event_key.uncapitalized}}{{#if param.tuple_param_accessor_indexes}} 17 | {{#each param.tuple_param_accessor_indexes as |index|}} 18 | [{{index}}] 19 | {{/each}} 20 | {{/if ~}} 21 | , 22 | {{/each}} 23 | }; 24 | 25 | context.{{contract.name.capitalized}}_{{event.name}}.set(entity); 26 | }); 27 | 28 | {{/each}} 29 | {{/each}} 30 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/contract_import_templates/javascript/test/Test.js.hbs: -------------------------------------------------------------------------------- 1 | 2 | {{#with imported_contracts.[0] as | contract |}} 3 | const assert = require("assert"); 4 | const { TestHelpers } = require("generated"); 5 | const { MockDb, {{contract.name.capitalized}} } = TestHelpers; 6 | {{/with}} 7 | 8 | {{#with imported_contracts.[0] as | contract |}} 9 | {{#with contract.imported_events.[0] as | event |}} 10 | describe("{{contract.name.capitalized}} contract {{event.name}} event tests", () => { 11 | // Create mock db 12 | const mockDb = MockDb.createMockDb(); 13 | 14 | // Creating mock for {{contract.name.capitalized}} contract {{event.name}} event 15 | const event = {{event.create_mock_code}}; 16 | 17 | it("{{contract.name.capitalized}}_{{event.name}} is created correctly", async () => { 18 | // Processing the event 19 | const mockDbUpdated = await {{contract.name.capitalized}}.{{event.name}}.processEvent({ 20 | event, 21 | mockDb, 22 | }); 23 | 24 | // Getting the actual entity from the mock database 25 | let actual{{contract.name.capitalized}}{{event.name}} = mockDbUpdated.entities.{{contract.name.capitalized}}_{{event.name}}.get( 26 | {{event.entity_id_from_event_code}} 27 | ); 28 | 29 | // Creating the expected entity 30 | const expected{{contract.name.capitalized}}{{event.name}} = { 31 | id:{{event.entity_id_from_event_code}}, 32 | {{#each event.params as |param|}} 33 | {{param.js_name}}: event.params.{{param.js_name}}, 34 | {{/each}} 35 | }; 36 | // Asserting that the entity in the mock database is the same as the expected entity 37 | assert.deepEqual( 38 | actual{{contract.name.capitalized}}{{event.name}}, 39 | expected{{contract.name.capitalized}}{{event.name}}, 40 | "Actual {{contract.name.capitalized}}{{event.name}} should be the same as the expected{{contract.name.capitalized}}{{event.name}}" 41 | ); 42 | }); 43 | }); 44 | {{/with}} 45 | {{/with}} 46 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/contract_import_templates/rescript/src/EventHandlers.res.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | * Please refer to https://docs.envio.dev for a thorough guide on all Envio indexer features 3 | */ 4 | {{#each imported_contracts as |contract|}} 5 | {{#each contract.imported_events as |event|}} 6 | 7 | Handlers.{{contract.name.capitalized}}.{{event.name}}.handler(async ({event, context}) => { 8 | let entity: Types.{{contract.name.uncapitalized}}_{{event.name}} = { 9 | id: {{event.entity_id_from_event_code}}, 10 | {{#each event.params as |param|}} 11 | {{param.entity_key.uncapitalized}}: event.params.{{param.event_key.uncapitalized}} 12 | {{#if param.tuple_param_accessor_indexes}} 13 | {{#each param.tuple_param_accessor_indexes as |index|}} 14 | ->Utils.Tuple.get({{index}})->Belt.Option.getUnsafe 15 | {{/each}} 16 | {{/if}} 17 | {{#if param.is_eth_address}} 18 | ->Address.toString 19 | {{/if~}} 20 | , 21 | {{/each}} 22 | } 23 | 24 | context.{{contract.name.uncapitalized}}_{{event.name}}.set(entity) 25 | }) 26 | {{/each}} 27 | {{/each}} 28 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/contract_import_templates/rescript/test/Test.res.hbs: -------------------------------------------------------------------------------- 1 | open RescriptMocha 2 | 3 | open Belt 4 | open TestHelpers 5 | 6 | {{#with imported_contracts.[0] as | contract |}} 7 | {{#with contract.imported_events.[0] as | event |}} 8 | describe("{{contract.name.capitalized}} contract {{event.name}} event tests", () => { 9 | // Create mock db 10 | let mockDb = MockDb.createMockDb() 11 | 12 | // Creating mock for {{contract.name.capitalized}} contract {{event.name}} event 13 | let event = {{event.create_mock_code}}; 14 | 15 | Async.it("{{contract.name.capitalized}}_{{event.name}} is created correctly", async () => { 16 | // Processing the event 17 | let mockDbUpdated = await {{contract.name.capitalized}}.{{event.name}}.processEvent({ 18 | event, 19 | mockDb, 20 | }) 21 | 22 | // Getting the actual entity from the mock database 23 | let actual{{contract.name.capitalized}}{{event.name}} = 24 | mockDbUpdated.entities.{{contract.name.uncapitalized}}_{{event.name}}.get( 25 | {{event.entity_id_from_event_code}}, 26 | )->Option.getExn 27 | 28 | // Creating the expected entity 29 | let expected{{contract.name.capitalized}}{{event.name}}: Types.{{contract.name.uncapitalized}}_{{event.name}} = { 30 | id: {{event.entity_id_from_event_code}}, 31 | {{#each event.params as |param|}} 32 | {{param.res_name}}: event.params.{{param.res_name}}{{#if param.is_eth_address}}->Address.toString{{/if}}, 33 | {{/each}} 34 | } 35 | //Assert the expected {{contract.name.capitalized}} {{event.name}} entity 36 | Assert.deepEqual( 37 | actual{{contract.name.capitalized}}{{event.name}}, 38 | expected{{contract.name.capitalized}}{{event.name}}, 39 | ~message="Actual {{contract.name.capitalized}}_{{event.name}} should be the same as the expected {{contract.name.capitalized}}_{{event.name}}", 40 | ) 41 | }) 42 | }) 43 | {{/with}} 44 | {{/with}} 45 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/contract_import_templates/shared/.env.hbs: -------------------------------------------------------------------------------- 1 | # To create or update a token visit https://envio.dev/app/api-tokens 2 | {{#if envio_api_token}} 3 | ENVIO_API_TOKEN="{{envio_api_token}}" 4 | {{else}} 5 | # Uncomment the line below and set a valid token 6 | # ENVIO_API_TOKEN="" 7 | {{/if}} 8 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/contract_import_templates/shared/schema.graphql.hbs: -------------------------------------------------------------------------------- 1 | {{#each imported_contracts as |contract|}} 2 | {{#each contract.imported_events as |event|}} 3 | type {{contract.name.capitalized}}_{{event.name}} { 4 | id: ID! 5 | {{#each event.params as |param|}} 6 | {{param.entity_key.uncapitalized}}: {{param.graphql_type}} 7 | {{/each}} 8 | } 9 | 10 | {{/each}} 11 | {{/each}} -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/contract_import_templates/typescript/src/EventHandlers.ts.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | * Please refer to https://docs.envio.dev for a thorough guide on all Envio indexer features 3 | */ 4 | import { 5 | {{#each imported_contracts as |contract|}} 6 | {{contract.name.capitalized}}, 7 | {{#each contract.imported_events as |event|}} 8 | {{!--These are the entity types--}} 9 | {{contract.name.capitalized}}_{{event.name}}, 10 | {{/each}} 11 | {{/each}} 12 | } from "generated"; 13 | {{#each imported_contracts as |contract|}} 14 | {{#each contract.imported_events as |event|}} 15 | 16 | {{contract.name.capitalized}}.{{event.name}}.handler(async ({ event, context }) => { 17 | const entity: {{contract.name.capitalized}}_{{event.name}} = { 18 | id: {{event.entity_id_from_event_code}}, 19 | {{#each event.params as |param|}} 20 | {{param.entity_key.uncapitalized}}: event.params.{{param.event_key.uncapitalized}}{{#if 21 | param.tuple_param_accessor_indexes 22 | }} 23 | {{#each param.tuple_param_accessor_indexes as |index|}} 24 | [{{index}}] 25 | {{/each}} 26 | {{/if}}, 27 | {{/each}} 28 | }; 29 | 30 | context.{{contract.name.capitalized}}_{{event.name}}.set(entity); 31 | }); 32 | {{/each}} 33 | {{/each}} 34 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/init_templates/shared/.env.hbs: -------------------------------------------------------------------------------- 1 | # To create or update a token visit https://envio.dev/app/api-tokens 2 | {{#if envio_api_token}} 3 | ENVIO_API_TOKEN="{{envio_api_token}}" 4 | {{else}} 5 | # Uncomment the line below and set a valid token 6 | # ENVIO_API_TOKEN="" 7 | {{/if}} 8 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/init_templates/shared/package.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{project_name}}", 3 | "version": "0.1.0", 4 | "scripts": { 5 | {{#if is_typescript}} 6 | "clean": "tsc --clean", 7 | "build": "tsc --build", 8 | "watch": "tsc --watch", 9 | "mocha": "ts-mocha test/**/*.ts", 10 | {{/if}} 11 | {{#if is_rescript}} 12 | "clean": "rescript clean", 13 | "build": "rescript", 14 | "watch": "rescript -w", 15 | "mocha": "pnpm build && mocha", 16 | {{/if}} 17 | "codegen": "envio codegen", 18 | "dev": "{{#if is_rescript}}pnpm build && {{/if}}envio dev", 19 | "test": "pnpm mocha", 20 | {{#if is_rescript}} 21 | "start": "pnpm build && node generated/src/Index.bs.js" 22 | {{else}} 23 | {{#if is_typescript}} 24 | "start": "ts-node generated/src/Index.bs.js" 25 | {{else}} 26 | "start": "node generated/src/Index.bs.js" 27 | {{/if}} 28 | {{/if}} 29 | }, 30 | "devDependencies": { 31 | {{#if is_rescript}} 32 | "@rescript/react": "0.12.1", 33 | "rescript": "11.1.3", 34 | {{/if}} 35 | {{#if is_typescript}} 36 | "@types/chai": "^4.3.11", 37 | "@types/mocha": "10.0.6", 38 | "@types/node": "20.8.8", 39 | "ts-mocha": "^10.0.0", 40 | "ts-node": "10.9.1", 41 | "typescript": "5.2.2", 42 | "chai": "4.3.10", 43 | {{/if}} 44 | "mocha": "10.2.0" 45 | }, 46 | "dependencies": { 47 | "envio": "{{envio_version}}" 48 | }, 49 | "optionalDependencies": { 50 | "generated": "{{relative_path_from_root_to_generated}}" 51 | }, 52 | "engines": { 53 | "node": ">=18.0.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/subgraph_migration_templates/javascript/src/EventHandlers.js.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | *Please refer to https://docs.envio.dev for a thorough guide on all Envio indexer features* 3 | */ 4 | 5 | const { 6 | {{#each imported_contracts as |contract|}} 7 | {{contract.name.capitalized}}{{#unless @last}},{{/unless}} 8 | {{/each}} 9 | } = require("../generated/src/Handlers.bs.js"); 10 | 11 | {{#each imported_contracts as |contract|}} 12 | {{#each contract.imported_events as |event|}} 13 | {{contract.name.capitalized}}.{{event.name}}.loader(({event, context}) => { 14 | // Load requiredEntities here 15 | }); 16 | 17 | {{contract.name.capitalized}}.{{event.name}}.handler(({event, context}) => { 18 | // Retrieve loaded requiredEntities and perform event handling logic here 19 | }); 20 | 21 | {{/each}} 22 | {{/each}} 23 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/subgraph_migration_templates/rescript/src/EventHandlers.res.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | *Please refer to https://docs.envio.dev for a thorough guide on all Envio indexer features* 3 | */ 4 | 5 | {{#each imported_contracts as |contract|}} 6 | {{#each contract.imported_events as |event|}} 7 | Handlers.{{contract.name.capitalized}}.{{event.name}}.loader(({event, context}) => { 8 | // Load requiredEntities here 9 | () 10 | }) 11 | 12 | Handlers.{{contract.name.capitalized}}.{{event.name}}.handler(({event, context}) => { 13 | // Retrieve loaded requiredEntities and perform event handling logic here 14 | () 15 | }) 16 | 17 | {{/each}} 18 | {{/each}} 19 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/dynamic/subgraph_migration_templates/typescript/src/EventHandlers.ts.hbs: -------------------------------------------------------------------------------- 1 | /* 2 | *Please refer to https://docs.envio.dev for a thorough guide on all Envio indexer features* 3 | */ 4 | 5 | import { 6 | {{#each imported_contracts as |contract|}} 7 | {{contract.name.capitalized}} 8 | {{/each}} 9 | } from "../generated/src/Handlers.gen"; 10 | 11 | {{#each imported_contracts as |contract|}} 12 | {{#each contract.imported_events as |event|}} 13 | {{contract.name.capitalized}}.{{event.name}}.loader(({ event, context }) => { 14 | // Load requiredEntities here 15 | }); 16 | 17 | {{contract.name.capitalized}}.{{event.name}}.handler(({ event, context }) => { 18 | // Retrieve loaded requiredEntities and perform event handling logic here 19 | }); 20 | 21 | {{/each}} 22 | {{/each}} 23 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/blank_template/javascript/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enviodev/hyperindex/ee5707baaf7ae747023a958f417c57632c80694a/codegenerator/cli/templates/static/blank_template/javascript/.gitkeep -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/blank_template/rescript/rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "EnvioIndexer", 3 | "version": "0.1.0", 4 | "sources": [ 5 | { 6 | "dir": "src", 7 | "subdirs": true 8 | }, 9 | { 10 | "dir": "test", 11 | "subdirs": true 12 | } 13 | ], 14 | "package-specs": { 15 | "module": "commonjs", 16 | "in-source": true 17 | }, 18 | "jsx": { 19 | "version": 4 20 | }, 21 | "suffix": ".bs.js", 22 | "bs-dependencies": ["generated", "envio"] 23 | } 24 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/blank_template/rescript/test/Test.res: -------------------------------------------------------------------------------- 1 | open RescriptMocha 2 | open Belt 3 | open TestHelpers 4 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/blank_template/shared/.env.example: -------------------------------------------------------------------------------- 1 | # To create or update a token visit https://envio.dev/app/api-tokens 2 | ENVIO_API_TOKEN="" 3 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/blank_template/shared/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/* 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | .bsb.lock 27 | /node_modules/ 28 | benchmarks/ 29 | artifacts 30 | cache 31 | generated 32 | logs 33 | *.bs.js 34 | *.bs.mjs 35 | *.gen.ts 36 | build 37 | .env 38 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/blank_template/shared/README.md: -------------------------------------------------------------------------------- 1 | ## Envio Indexer 2 | 3 | *Please refer to the [documentation website](https://docs.envio.dev) for a thorough guide on all [Envio](https://envio.dev) indexer features* 4 | 5 | ### Run 6 | 7 | ```bash 8 | pnpm dev 9 | ``` 10 | 11 | Visit http://localhost:8080 to see the GraphQL Playground, local password is `testing`. 12 | 13 | ### Generate files from `config.yaml` or `schema.graphql` 14 | 15 | ```bash 16 | pnpm codegen 17 | ``` 18 | 19 | ### Pre-requisites 20 | 21 | - [Node.js (use v18 or newer)](https://nodejs.org/en/download/current) 22 | - [pnpm (use v8 or newer)](https://pnpm.io/installation) 23 | - [Docker desktop](https://www.docker.com/products/docker-desktop/) 24 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/blank_template/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", //required for use with BigInt types 4 | "lib": [ 5 | "es2020" 6 | ], 7 | "allowJs": true, 8 | "checkJs": false, 9 | "outDir": "build", 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true, 15 | "module": "CommonJS" 16 | }, 17 | "include": [ 18 | "src", 19 | "test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | /lib/ 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | .bsb.lock 27 | /node_modules/ 28 | /benchmarks/ 29 | *.bs.js 30 | *.bs.mjs 31 | logs/* 32 | *BenchmarkCache.json 33 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true # Since we want specific versions of envio to be completely stable. 2 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | This file serves as an entry point when referencing generated as a node module 3 | */ 4 | 5 | const handlers = require("./src/Handlers.bs"); 6 | const TestHelpers = require("./src/TestHelpers.bs"); 7 | const BigDecimal = require("bignumber.js"); 8 | 9 | module.exports = { 10 | ...handlers, 11 | BigDecimal, 12 | TestHelpers, 13 | }; 14 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generated", 3 | "version": "0.1.0", 4 | "sources": [ 5 | { 6 | "dir": "src", 7 | "subdirs": true 8 | } 9 | ], 10 | "gentypeconfig": { 11 | "shims": { 12 | "Js": "Js" 13 | }, 14 | "generatedFileExtension": ".gen.ts", 15 | "debug": { 16 | "all": false, 17 | "basic": false 18 | } 19 | }, 20 | "package-specs": { 21 | "module": "commonjs", 22 | "in-source": true 23 | }, 24 | "jsx": { 25 | "version": 4 26 | }, 27 | "suffix": ".bs.js", 28 | "bs-dependencies": [ 29 | "rescript-envsafe", 30 | "rescript-schema", 31 | "@rescript/react", 32 | "envio" 33 | ], 34 | "bsc-flags": ["-open RescriptSchema"] 35 | } 36 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/GqlDbCustomTypes.res: -------------------------------------------------------------------------------- 1 | // Can be deleted on a breaking release (V3) 2 | 3 | module Float = { 4 | @genType 5 | type t = float 6 | } 7 | 8 | module Int = { 9 | @genType 10 | type t = int 11 | } 12 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/Js.shim.ts: -------------------------------------------------------------------------------- 1 | export type Json_t = 2 | | string 3 | | boolean 4 | | number 5 | | null 6 | | { [key: string]: Json_t } 7 | | Json_t[]; 8 | 9 | export type t = unknown; 10 | 11 | export type Exn_t = Error; 12 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/LoadLayer.resi: -------------------------------------------------------------------------------- 1 | type t 2 | 3 | type fieldValue 4 | 5 | let make: ( 6 | ~loadEntitiesByIds: ( 7 | array, 8 | ~entityConfig: Internal.entityConfig, 9 | ~logger: Pino.t=?, 10 | ) => promise>, 11 | ~loadEntitiesByField: ( 12 | ~operator: TableIndices.Operator.t, 13 | ~entityConfig: Internal.entityConfig, 14 | ~fieldName: string, 15 | ~fieldValue: fieldValue, 16 | ~fieldValueSchema: S.t, 17 | ~logger: Pino.t=?, 18 | ) => promise>, 19 | ) => t 20 | 21 | let makeWithDbConnection: unit => t 22 | 23 | let loadById: ( 24 | t, 25 | ~entityConfig: Internal.entityConfig, 26 | ~inMemoryStore: InMemoryStore.t, 27 | ~shouldGroup: bool, 28 | ~eventItem: Internal.eventItem, 29 | ~entityId: string, 30 | ) => promise> 31 | 32 | let loadByField: ( 33 | t, 34 | ~operator: TableIndices.Operator.t, 35 | ~entityConfig: Internal.entityConfig, 36 | ~inMemoryStore: InMemoryStore.t, 37 | ~fieldName: string, 38 | ~fieldValueSchema: RescriptSchema.S.t<'fieldValue>, 39 | ~shouldGroup: bool, 40 | ~eventItem: Internal.eventItem, 41 | ~fieldValue: 'fieldValue, 42 | ) => promise> 43 | 44 | let loadEffect: ( 45 | t, 46 | ~effect: Internal.effect, 47 | ~effectArgs: Internal.effectArgs, 48 | ~inMemoryStore: InMemoryStore.t, 49 | ~shouldGroup: bool, 50 | ) => promise 51 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/PersistedState.res: -------------------------------------------------------------------------------- 1 | type t = { 2 | @as("envio_version") envioVersion: string, 3 | @as("config_hash") configHash: string, 4 | @as("schema_hash") schemaHash: string, 5 | @as("handler_files_hash") handlerFilesHash: string, 6 | @as("abi_files_hash") abiFilesHash: string, 7 | } 8 | 9 | let schema = S.schema(s => { 10 | envioVersion: s.matches(S.string), 11 | configHash: s.matches(S.string), 12 | schemaHash: s.matches(S.string), 13 | handlerFilesHash: s.matches(S.string), 14 | abiFilesHash: s.matches(S.string), 15 | }) 16 | 17 | external requireJson: string => Js.Json.t = "require" 18 | let getPersistedState = () => 19 | try { 20 | let json = requireJson("../persisted_state.envio.json") 21 | let parsed = json->S.parseJsonOrThrow(schema) 22 | Ok(parsed) 23 | } catch { 24 | | exn => Error(exn) 25 | } 26 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/TestHelpers_MockAddresses.res: -------------------------------------------------------------------------------- 1 | /* 2 | Note this file should remain top level since there are default types 3 | that can point to TestHelpers_MockAddresses.defaultAddress 4 | */ 5 | @genType 6 | let mockAddresses = [ 7 | "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", 8 | "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", 9 | "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", 10 | "0x90F79bf6EB2c4f870365E785982E1f101E93b906", 11 | "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", 12 | "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", 13 | "0x976EA74026E726554dB657fA54763abd0C3a0aa9", 14 | "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", 15 | "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f", 16 | "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720", 17 | "0xBcd4042DE499D14e55001CcbB24a551F3b954096", 18 | "0x71bE63f3384f5fb98995898A86B02Fb2426c5788", 19 | "0xFABB0ac9d68B0B445fB7357272Ff202C5651694a", 20 | "0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec", 21 | "0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097", 22 | "0xcd3B766CCDd6AE721141F452C550Ca635964ce71", 23 | "0x2546BcD3c84621e976D8185a91A922aE77ECEc30", 24 | "0xbDA5747bFD65F08deb54cb465eB87D40e51B197E", 25 | "0xdD2FD4581271e230360230F9337D5c0430Bf44C0", 26 | "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199", 27 | ]->Belt.Array.map(Address.Evm.fromStringOrThrow) 28 | @genType 29 | let defaultAddress = 30 | mockAddresses[0] 31 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/bindings/Dotenv.res: -------------------------------------------------------------------------------- 1 | type config = {path?: string} 2 | type envRes 3 | 4 | @module("dotenv") external config: config => envRes = "config" 5 | 6 | module Utils = { 7 | type require = {resolve: string => string} 8 | external require: require = "require" 9 | 10 | let getEnvFilePath = () => 11 | switch require.resolve(`../../${Path.relativePathToRootFromGenerated}/.env`) { 12 | | path => Some(path) 13 | | exception _exn => None 14 | } 15 | } 16 | 17 | let initialize = () => config({path: ?Utils.getEnvFilePath()})->ignore 18 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/bindings/Ethers.gen.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Reexport the types to keep backward compatibility 3 | */ 4 | 5 | /* eslint-disable */ 6 | /* tslint:disable */ 7 | 8 | import type { t as Address_t } from "envio/src/Address.gen"; 9 | export type { 10 | Addresses_mockAddresses, 11 | Addresses_defaultAddress, 12 | Addresses, 13 | } from "envio/src/bindings/Ethers.gen"; 14 | 15 | export type ethAddress = Address_t; 16 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/bindings/Lodash.res: -------------------------------------------------------------------------------- 1 | // TODO: Remove this file once we have our own impl of cloneDeep or it is no longer needed 2 | @module("./vendored-lodash-fns.js") external cloneDeep: 'a => 'a = "cloneDeep" 3 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/bindings/OpaqueTypes.ts: -------------------------------------------------------------------------------- 1 | export type EthersAddress = string; 2 | export type Address = string; 3 | export type Nullable = null | T; 4 | export type SingleOrMultiple = T | T[]; 5 | export type HandlerWithOptions = (fn: Fn, opt?: Opts) => void; 6 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/bindings/Yargs.res: -------------------------------------------------------------------------------- 1 | type arg = string 2 | 3 | type parsedArgs<'a> = 'a 4 | 5 | @module external yargs: array => parsedArgs<'a> = "yargs/yargs" 6 | @module("yargs/helpers") external hideBin: array => array = "hideBin" 7 | 8 | @get external argv: parsedArgs<'a> => 'a = "argv" 9 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/db/Db.res: -------------------------------------------------------------------------------- 1 | // This is a module with all the global configuration of the DB 2 | // Ideally it should be moved to the config and passed with it 3 | 4 | let config: Postgres.poolConfig = { 5 | host: Env.Db.host, 6 | port: Env.Db.port, 7 | username: Env.Db.user, 8 | password: Env.Db.password, 9 | database: Env.Db.database, 10 | ssl: Env.Db.ssl, 11 | // TODO: think how we want to pipe these logs to pino. 12 | onnotice: ?(Env.userLogLevel == #warn || Env.userLogLevel == #error ? None : Some(_str => ())), 13 | transform: {undefined: Null}, 14 | max: 2, 15 | } 16 | let sql = Postgres.makeSql(~config) 17 | let publicSchema = Env.Db.publicSchema 18 | 19 | let allEntityTables: array = Entities.allEntities->Belt.Array.map(entityConfig => { 20 | entityConfig.table 21 | }) 22 | 23 | let allEntityHistoryTables: array = [] 24 | let allEntityHistory: array< 25 | EntityHistory.t, 26 | > = Entities.allEntities->Belt.Array.map(entityConfig => { 27 | let entityHistory = entityConfig.entityHistory->EntityHistory.castInternal 28 | allEntityHistoryTables->Js.Array2.push(entityHistory.table)->ignore 29 | entityHistory 30 | }) 31 | 32 | let allStaticTables: array = [ 33 | TablesStatic.EventSyncState.table, 34 | TablesStatic.ChainMetadata.table, 35 | TablesStatic.PersistedState.table, 36 | TablesStatic.EndOfBlockRangeScannedData.table, 37 | TablesStatic.RawEvents.table, 38 | ] 39 | 40 | let schema = Schema.make(allEntityTables) 41 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/db/Migrations.res: -------------------------------------------------------------------------------- 1 | let sql = Db.sql 2 | let unsafe = Postgres.unsafe 3 | 4 | let deleteAllTables: unit => promise = async () => { 5 | Logging.trace("Dropping all tables") 6 | let query = ` 7 | DO $$ 8 | BEGIN 9 | DROP SCHEMA IF EXISTS ${Env.Db.publicSchema} CASCADE; 10 | CREATE SCHEMA ${Env.Db.publicSchema}; 11 | GRANT ALL ON SCHEMA ${Env.Db.publicSchema} TO ${Env.Db.user}; 12 | GRANT ALL ON SCHEMA ${Env.Db.publicSchema} TO public; 13 | END $$;` 14 | 15 | await sql->unsafe(query) 16 | } 17 | 18 | type t 19 | @module external process: t = "process" 20 | 21 | type exitCode = | @as(0) Success | @as(1) Failure 22 | @send external exit: (t, exitCode) => unit = "exit" 23 | 24 | let runUpMigrations = async ( 25 | ~shouldExit, 26 | // Reset is used for db-setup 27 | ~reset=false, 28 | ) => { 29 | let exitCode = try { 30 | await Config.codegenPersistence->Persistence.init(~skipIsInitializedCheck=true, ~reset) 31 | Success 32 | } catch { 33 | | _ => Failure 34 | } 35 | if shouldExit { 36 | process->exit(exitCode) 37 | } 38 | exitCode 39 | } 40 | 41 | let runDownMigrations = async (~shouldExit) => { 42 | let exitCode = ref(Success) 43 | await deleteAllTables()->Promise.catch(err => { 44 | exitCode := Failure 45 | err 46 | ->ErrorHandling.make(~msg="EE804: Error dropping entity tables") 47 | ->ErrorHandling.log 48 | Promise.resolve() 49 | }) 50 | if shouldExit { 51 | process->exit(exitCode.contents) 52 | } 53 | exitCode.contents 54 | } 55 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/eventFetching/NetworkSources.res: -------------------------------------------------------------------------------- 1 | open Belt 2 | 3 | type rpc = { 4 | url: string, 5 | sourceFor: Source.sourceFor, 6 | syncConfig?: Config.syncConfigOptions, 7 | } 8 | 9 | let evm = ( 10 | ~chain, 11 | ~contracts: array, 12 | ~hyperSync, 13 | ~allEventSignatures, 14 | ~shouldUseHypersyncClientDecoder, 15 | ~rpcs: array, 16 | ) => { 17 | let eventRouter = 18 | contracts 19 | ->Belt.Array.flatMap(contract => contract.events) 20 | ->EventRouter.fromEvmEventModsOrThrow(~chain) 21 | 22 | let sources = switch hyperSync { 23 | | Some(endpointUrl) => [ 24 | HyperSyncSource.make({ 25 | chain, 26 | contracts, 27 | endpointUrl, 28 | allEventSignatures, 29 | eventRouter, 30 | shouldUseHypersyncClientDecoder: Env.Configurable.shouldUseHypersyncClientDecoder->Option.getWithDefault( 31 | shouldUseHypersyncClientDecoder, 32 | ), 33 | }), 34 | ] 35 | | _ => [] 36 | } 37 | rpcs->Js.Array2.forEach(({?syncConfig, url, sourceFor}) => { 38 | let _ = sources->Js.Array2.push( 39 | RpcSource.make({ 40 | chain, 41 | sourceFor, 42 | contracts, 43 | syncConfig: Config.getSyncConfig(syncConfig->Option.getWithDefault({})), 44 | url, 45 | eventRouter, 46 | }), 47 | ) 48 | }) 49 | 50 | sources 51 | } 52 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/globalState/GlobalStateManager.resi: -------------------------------------------------------------------------------- 1 | type t 2 | 3 | let make: (~stateUpdatedHook: GlobalState.t => unit=?, GlobalState.t) => t 4 | let dispatchAction: (~stateId: int=?, t, GlobalState.action) => unit 5 | let dispatchTask: (t, GlobalState.task) => unit 6 | let getState: t => GlobalState.t 7 | let setState: (t, GlobalState.t) => unit 8 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/ink/components/BufferedProgressBar.res: -------------------------------------------------------------------------------- 1 | open Ink 2 | open Belt 3 | @react.component 4 | let make = (~loaded, ~buffered=?, ~outOf, ~barWidth=36, ~loadingColor=Style.Secondary) => { 5 | let maxCount = barWidth 6 | 7 | let loadedFraction = loaded->Int.toFloat /. outOf->Int.toFloat 8 | let loadedCount = Pervasives.min( 9 | Js.Math.floor_float(maxCount->Js.Int.toFloat *. loadedFraction)->Belt.Float.toInt, 10 | maxCount, 11 | ) 12 | 13 | let bufferedCount = buffered->Option.mapWithDefault(loadedCount, buffered => { 14 | let bufferedFraction = buffered->Int.toFloat /. outOf->Int.toFloat 15 | Pervasives.min( 16 | Js.Math.floor_float(maxCount->Js.Int.toFloat *. bufferedFraction)->Belt.Float.toInt, 17 | maxCount, 18 | ) 19 | }) 20 | let loadedFraction = loadedFraction > 0.0 ? loadedFraction : 0.0 21 | let loadedPercentageStr = (loadedFraction *. 100.)->Int.fromFloat->Int.toString ++ "% " 22 | 23 | let loadedPercentageStrCount = loadedPercentageStr->String.length 24 | let loadedSpaces = Pervasives.max(loadedCount - loadedPercentageStrCount, 0) 25 | let loadedCount = Pervasives.max(loadedCount, loadedPercentageStrCount) 26 | let bufferedCount = Pervasives.max(bufferedCount, loadedCount) 27 | 28 | 29 | 30 | {" "->Js.String2.repeat(loadedSpaces)->React.string} 31 | {loadedPercentageStr->React.string} 32 | 33 | 34 | {" "->Js.String2.repeat(bufferedCount - loadedCount)->React.string} 35 | 36 | 37 | {" "->Js.String2.repeat(maxCount - bufferedCount)->React.string} 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/codegen/src/ink/components/Messages.res: -------------------------------------------------------------------------------- 1 | open Belt 2 | open Ink 3 | module Message = { 4 | @react.component 5 | let make = (~message: CustomHooks.InitApi.message) => { 6 | CustomHooks.InitApi.toTheme}> 7 | {message.content->React.string} 8 | 9 | } 10 | } 11 | 12 | module Notifications = { 13 | @react.component 14 | let make = (~children) => { 15 | <> 16 | 17 | {"Notifications:"->React.string} 18 | {children} 19 | 20 | } 21 | } 22 | 23 | @react.component 24 | let make = (~config) => { 25 | let messages = CustomHooks.useMessages(~config) 26 | <> 27 | {switch messages { 28 | | Data([]) | Loading => React.null //Don't show anything while loading or no messages 29 | | Data(messages) => 30 | 31 | {messages 32 | ->Array.mapWithIndex((i, message) => {Int.toString} message />}) 33 | ->React.array} 34 | 35 | | Err(_) => 36 | 37 | 38 | 39 | }} 40 | 41 | } 42 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/erc20_template/javascript/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=./node_modules/envio/evm.schema.json 2 | name: erc20indexer 3 | description: ERC-20 indexer 4 | networks: 5 | - id: 1 # Ethereum Mainnet 6 | start_block: 10861674 7 | contracts: 8 | - name: ERC20 9 | address: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984" #UNI 10 | handler: src/EventHandlers.js 11 | events: 12 | - event: "Approval(address indexed owner, address indexed spender, uint256 value)" 13 | - event: "Transfer(address indexed from, address indexed to, uint256 value)" 14 | # Unordered multichain mode allows you to index events from multiple chains 15 | # in realtime but doesn't guarentee ordering between chains 16 | # https://docs.envio.dev/docs/HyperIndex/multichain-indexing#unordered-multichain-mode 17 | unordered_multichain_mode: true 18 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/erc20_template/rescript/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=./node_modules/envio/evm.schema.json 2 | name: erc20indexer 3 | description: ERC-20 indexer 4 | networks: 5 | - id: 1 # Ethereum Mainnet 6 | start_block: 10861674 7 | contracts: 8 | - name: ERC20 9 | address: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984" #UNI 10 | handler: src/EventHandlers.bs.js 11 | events: 12 | - event: "Approval(address indexed owner, address indexed spender, uint256 value)" 13 | - event: "Transfer(address indexed from, address indexed to, uint256 value)" 14 | # Unordered multichain mode allows you to index events from multiple chains 15 | # in realtime but doesn't guarentee ordering between chains 16 | # https://docs.envio.dev/docs/HyperIndex/multichain-indexing#unordered-multichain-mode 17 | unordered_multichain_mode: true 18 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/erc20_template/rescript/rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "erc20", 3 | "version": "0.1.0", 4 | "sources": [ 5 | { 6 | "dir": "src", 7 | "subdirs": true 8 | }, 9 | { 10 | "dir": "test", 11 | "subdirs": true 12 | } 13 | ], 14 | "package-specs": { 15 | "module": "commonjs", 16 | "in-source": true 17 | }, 18 | "jsx": { 19 | "version": 4 20 | }, 21 | "suffix": ".bs.js", 22 | "bs-dependencies": ["generated", "envio"] 23 | } 24 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/erc20_template/rescript/src/EventHandlers.bs.js: -------------------------------------------------------------------------------- 1 | // NOTE: this file will be over-written by the rescript compiler. 2 | // This is merely a template file, and isn't needed if the codegeneration step is working. 3 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/erc20_template/shared/.env.example: -------------------------------------------------------------------------------- 1 | # To create or update a token visit https://envio.dev/app/api-tokens 2 | ENVIO_API_TOKEN="" 3 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/erc20_template/shared/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/* 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | .bsb.lock 27 | node_modules/ 28 | build/ 29 | benchmarks/ 30 | artifacts 31 | cache 32 | *.bs.js 33 | generated 34 | .env 35 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/erc20_template/shared/README.md: -------------------------------------------------------------------------------- 1 | ## Envio ERC20 Template 2 | 3 | *Please refer to the [documentation website](https://docs.envio.dev) for a thorough guide on all [Envio](https://envio.dev) indexer features* 4 | 5 | ### Run 6 | 7 | ```bash 8 | pnpm dev 9 | ``` 10 | 11 | Visit http://localhost:8080 to see the GraphQL Playground, local password is `testing`. 12 | 13 | ### Generate files from `config.yaml` or `schema.graphql` 14 | 15 | ```bash 16 | pnpm codegen 17 | ``` 18 | 19 | ### Pre-requisites 20 | 21 | - [Node.js (use v18 or newer)](https://nodejs.org/en/download/current) 22 | - [pnpm (use v8 or newer)](https://pnpm.io/installation) 23 | - [Docker desktop](https://www.docker.com/products/docker-desktop/) 24 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/erc20_template/shared/schema.graphql: -------------------------------------------------------------------------------- 1 | type Account { 2 | # id is the address of the account 3 | id: ID! 4 | # approvals are a list of approvals that this account has given 5 | approvals: [Approval!]! @derivedFrom(field: "owner") 6 | # account balance of tokens 7 | balance: BigInt! 8 | } 9 | 10 | type Approval { 11 | # id is the owner address and spender address [owner-spender] 12 | id: ID! 13 | # amount is the amount of tokens approved 14 | amount: BigInt! 15 | # owner is the account that approved the tokens 16 | owner: Account! 17 | # spender is the account that is approved to spend the tokens 18 | spender: Account! 19 | } 20 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/erc20_template/typescript/build/src/EventHandlers.js: -------------------------------------------------------------------------------- 1 | // NOTE: this file will be over-written by the typescript compiler. 2 | // This is merely a template file, and isn't needed if the codegeneration step is working. 3 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/erc20_template/typescript/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=./node_modules/envio/evm.schema.json 2 | name: erc20indexer 3 | description: ERC-20 indexer 4 | networks: 5 | - id: 1 # Ethereum Mainnet 6 | start_block: 10861674 7 | contracts: 8 | - name: ERC20 9 | address: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984" #UNI 10 | handler: src/EventHandlers.ts 11 | events: 12 | - event: "Approval(address indexed owner, address indexed spender, uint256 value)" 13 | - event: "Transfer(address indexed from, address indexed to, uint256 value)" 14 | # Unordered multichain mode allows you to index events from multiple chains 15 | # in realtime but doesn't guarentee ordering between chains 16 | # https://docs.envio.dev/docs/HyperIndex/multichain-indexing#unordered-multichain-mode 17 | unordered_multichain_mode: true 18 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/erc20_template/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", //required for use with BigInt types 4 | "lib": [ 5 | "es2020" 6 | ], 7 | "allowJs": true, 8 | "checkJs": false, 9 | "outDir": "build", 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true, 15 | "module": "CommonJS" 16 | }, 17 | "include": [ 18 | "src", 19 | "test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/javascript/README.md: -------------------------------------------------------------------------------- 1 | ## Envio Greeter Template 2 | 3 | *Please refer to the [documentation website](https://docs.envio.dev) for a thorough guide on all [Envio](https://envio.dev) indexer features* 4 | 5 | ### Run 6 | 7 | ```bash 8 | pnpm dev 9 | ``` 10 | 11 | Visit http://localhost:8080 to see the GraphQL Playground, local password is `testing`. 12 | 13 | ### Generate files from `config.yaml` or `schema.graphql` 14 | 15 | ```bash 16 | pnpm codegen 17 | ``` 18 | 19 | ### Pre-requisites 20 | 21 | - [Node.js (use v18 or newer)](https://nodejs.org/en/download/current) 22 | - [pnpm (use v8 or newer)](https://pnpm.io/installation) 23 | - [Docker desktop](https://www.docker.com/products/docker-desktop/) 24 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/javascript/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=./node_modules/envio/evm.schema.json 2 | name: Greeter 3 | description: Greeter indexer 4 | #Global contract definitions that must contain all definitions except 5 | #addresses. Now you can share a single handler/abi/event definitions 6 | #for contracts across multiple chains 7 | contracts: 8 | - name: Greeter 9 | abi_file_path: ./abis/greeter-abi.json 10 | handler: ./src/EventHandlers.js 11 | events: 12 | - event: NewGreeting 13 | - event: ClearGreeting 14 | networks: 15 | - id: 137 # Polygon 16 | start_block: 45336336 17 | contracts: 18 | - name: Greeter #A reference to the global contract definition 19 | address: "0x9D02A17dE4E68545d3a58D3a20BbBE0399E05c9c" 20 | - id: 59144 # Linea 21 | start_block: 367801 22 | contracts: 23 | - name: Greeter #A reference to the global contract definition 24 | address: "0xdEe21B97AB77a16B4b236F952e586cf8408CF32A" 25 | # Unordered multichain mode allows you to index events from multiple chains 26 | # in realtime but doesn't guarentee ordering between chains 27 | # https://docs.envio.dev/docs/HyperIndex/multichain-indexing#unordered-multichain-mode 28 | unordered_multichain_mode: true 29 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/javascript/src/EventHandlers.js: -------------------------------------------------------------------------------- 1 | const { Greeter } = require("generated"); 2 | 3 | // Handler for the NewGreeting event 4 | Greeter.NewGreeting.handler(async ({ event, context }) => { 5 | const userId = event.params.user; // The id for the User entity 6 | const latestGreeting = event.params.greeting; // The greeting string that was added 7 | const currentUserEntity = await context.User.get(userId); // Optional user entity that may already exist 8 | 9 | // Update or create a new User entity 10 | const userEntity = currentUserEntity 11 | ? { 12 | id: userId, 13 | latestGreeting, 14 | numberOfGreetings: currentUserEntity.numberOfGreetings + 1, 15 | greetings: [...currentUserEntity.greetings, latestGreeting], 16 | } 17 | : { 18 | id: userId, 19 | latestGreeting, 20 | numberOfGreetings: 1, 21 | greetings: [latestGreeting], 22 | }; 23 | 24 | context.User.set(userEntity); // Set the User entity in the DB 25 | }); 26 | 27 | // Handler for the ClearGreeting event 28 | Greeter.ClearGreeting.handler(async ({ event, context }) => { 29 | const userId = event.params.user; // The id for the User entity 30 | const currentUserEntity = await context.User.get(userId); // Optional user entity that may already exist 31 | 32 | if (currentUserEntity) { 33 | context.User.set({ 34 | ...currentUserEntity, 35 | latestGreeting: "", // Clear the latestGreeting 36 | }); 37 | } 38 | }); 39 | 40 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/rescript/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=./node_modules/envio/evm.schema.json 2 | name: Greeter 3 | description: Greeter indexer 4 | #Global contract definitions that must contain all definitions except 5 | #addresses. Now you can share a single handler/abi/event definitions 6 | #for contracts across multiple chains 7 | contracts: 8 | - name: Greeter 9 | abi_file_path: ./abis/greeter-abi.json 10 | handler: ./src/EventHandlers.bs.js 11 | events: 12 | - event: NewGreeting 13 | - event: ClearGreeting 14 | networks: 15 | - id: 137 # Polygon 16 | start_block: 45336336 17 | contracts: 18 | - name: Greeter #A reference to the global contract definition 19 | address: 0x9D02A17dE4E68545d3a58D3a20BbBE0399E05c9c 20 | - id: 59144 # Linea 21 | start_block: 367801 22 | contracts: 23 | - name: Greeter #A reference to the global contract definition 24 | address: 0xdEe21B97AB77a16B4b236F952e586cf8408CF32A 25 | # Unordered multichain mode allows you to index events from multiple chains 26 | # in realtime but doesn't guarentee ordering between chains 27 | # https://docs.envio.dev/docs/HyperIndex/multichain-indexing#unordered-multichain-mode 28 | unordered_multichain_mode: true 29 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/rescript/rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "greeter", 3 | "version": "0.1.0", 4 | "sources": [ 5 | { 6 | "dir": "src", 7 | "subdirs": true 8 | }, 9 | { 10 | "dir": "test", 11 | "subdirs": true 12 | } 13 | ], 14 | "package-specs": { 15 | "module": "commonjs", 16 | "in-source": true 17 | }, 18 | "jsx": { 19 | "version": 4 20 | }, 21 | "suffix": ".bs.js", 22 | "bs-dependencies": ["generated", "envio"] 23 | } 24 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/rescript/src/EventHandlers.res: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | // Handler for the NewGreeting event 4 | Handlers.Greeter.NewGreeting.handler(async ({event, context}) => { 5 | let userId = event.params.user->Address.toString // The id for the User entity 6 | let latestGreeting = event.params.greeting // The greeting string that was added 7 | let maybeCurrentUserEntity = await context.user.get(userId) // Optional User entity that may already exist 8 | 9 | // Update or create a new User entity 10 | let userEntity: Entities.User.t = switch maybeCurrentUserEntity { 11 | | Some(existingUserEntity) => { 12 | id: userId, 13 | latestGreeting, 14 | numberOfGreetings: existingUserEntity.numberOfGreetings + 1, 15 | greetings: existingUserEntity.greetings->Belt.Array.concat([latestGreeting]), 16 | } 17 | | None => { 18 | id: userId, 19 | latestGreeting, 20 | numberOfGreetings: 1, 21 | greetings: [latestGreeting], 22 | } 23 | } 24 | 25 | context.user.set(userEntity) // Set the User entity in the DB 26 | }) 27 | 28 | // Handler for the ClearGreeting event 29 | Handlers.Greeter.ClearGreeting.handler(async ({event, context}) => { 30 | let userId = event.params.user->Address.toString // The id for the User entity 31 | let maybeCurrentUserEntity = await context.user.get(userId) // Optional User entity that may already exist 32 | 33 | switch maybeCurrentUserEntity { 34 | | Some(existingUserEntity) => 35 | context.user.set({...existingUserEntity, latestGreeting: ""}) // Clear the latestGreeting 36 | | None => () 37 | } 38 | }) 39 | 40 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/shared/.env.example: -------------------------------------------------------------------------------- 1 | # To create or update a token visit https://envio.dev/app/api-tokens 2 | ENVIO_API_TOKEN="" 3 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/shared/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/* 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | .bsb.lock 27 | /node_modules/ 28 | benchmarks/ 29 | artifacts 30 | cache 31 | build 32 | *.bs.js 33 | generated 34 | .env 35 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/shared/schema.graphql: -------------------------------------------------------------------------------- 1 | type User { 2 | id: ID! 3 | greetings: [String!]! 4 | latestGreeting: String! 5 | numberOfGreetings: Int! 6 | } 7 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/typescript/README.md: -------------------------------------------------------------------------------- 1 | ## Envio Greeter Template 2 | 3 | *Please refer to the [documentation website](https://docs.envio.dev) for a thorough guide on all [Envio](https://envio.dev) indexer features* 4 | 5 | ### Run 6 | 7 | ```bash 8 | pnpm dev 9 | ``` 10 | 11 | Visit http://localhost:8080 to see the GraphQL Playground, local password is `testing`. 12 | 13 | ### Generate files from `config.yaml` or `schema.graphql` 14 | 15 | ```bash 16 | pnpm codegen 17 | ``` 18 | 19 | ### Pre-requisites 20 | 21 | - [Node.js (use v18 or newer)](https://nodejs.org/en/download/current) 22 | - [pnpm (use v8 or newer)](https://pnpm.io/installation) 23 | - [Docker desktop](https://www.docker.com/products/docker-desktop/) 24 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/typescript/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=./node_modules/envio/evm.schema.json 2 | name: Greeter 3 | description: Greeter indexer 4 | #Global contract definitions that must contain all definitions except 5 | #addresses. Now you can share a single handler/abi/event definitions 6 | #for contracts across multiple chains 7 | contracts: 8 | - name: Greeter 9 | abi_file_path: ./abis/greeter-abi.json 10 | handler: ./src/EventHandlers.ts 11 | events: 12 | - event: NewGreeting 13 | - event: ClearGreeting 14 | networks: 15 | - id: 137 # Polygon 16 | start_block: 45336336 17 | contracts: 18 | - name: Greeter #A reference to the global contract definition 19 | address: 0x9D02A17dE4E68545d3a58D3a20BbBE0399E05c9c 20 | - id: 59144 # Linea 21 | start_block: 367801 22 | contracts: 23 | - name: Greeter #A reference to the global contract definition 24 | address: 0xdEe21B97AB77a16B4b236F952e586cf8408CF32A 25 | # Unordered multichain mode allows you to index events from multiple chains 26 | # in realtime but doesn't guarentee ordering between chains 27 | # https://docs.envio.dev/docs/HyperIndex/multichain-indexing#unordered-multichain-mode 28 | unordered_multichain_mode: true 29 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/typescript/src/EventHandlers.ts: -------------------------------------------------------------------------------- 1 | import { Greeter, User } from "generated"; 2 | 3 | // Handler for the NewGreeting event 4 | Greeter.NewGreeting.handler(async ({ event, context }) => { 5 | const userId = event.params.user; 6 | const latestGreeting = event.params.greeting; 7 | const currentUserEntity: User | undefined = await context.User.get(userId); 8 | 9 | // Update or create a new User entity 10 | const userEntity: User = currentUserEntity 11 | ? { 12 | id: userId, 13 | latestGreeting, 14 | numberOfGreetings: currentUserEntity.numberOfGreetings + 1, 15 | greetings: [...currentUserEntity.greetings, latestGreeting], 16 | } 17 | : { 18 | id: userId, 19 | latestGreeting, 20 | numberOfGreetings: 1, 21 | greetings: [latestGreeting], 22 | }; 23 | 24 | context.User.set(userEntity); 25 | }); 26 | 27 | // Handler for the ClearGreeting event 28 | Greeter.ClearGreeting.handler(async ({ event, context }) => { 29 | const userId = event.params.user; 30 | const currentUserEntity: User | undefined = await context.User.get(userId); 31 | 32 | if (currentUserEntity) { 33 | // Clear the latestGreeting 34 | context.User.set({ 35 | ...currentUserEntity, 36 | latestGreeting: "", 37 | }); 38 | } 39 | }); 40 | 41 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeter_template/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", //required for use with BigInt types 4 | "lib": [ 5 | "es2020" 6 | ], 7 | "allowJs": true, 8 | "checkJs": false, 9 | "outDir": "build", 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true, 15 | "module": "CommonJS" 16 | }, 17 | "include": [ 18 | "src", 19 | "test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/javascript/README.md: -------------------------------------------------------------------------------- 1 | ## Envio Greeter Template on FuelVM 2 | 3 | _Please refer to the [documentation website](https://docs.envio.dev) for a thorough guide on all Envio indexer features_ 4 | 5 | ### Run 6 | 7 | ```bash 8 | pnpm dev 9 | ``` 10 | 11 | Visit http://localhost:8080 to see the GraphQL Playground, local password is `testing`. 12 | 13 | ### Generate files from `config.yaml` or `schema.graphql` 14 | 15 | ```bash 16 | pnpm codegen 17 | ``` 18 | 19 | ### Pre-requisites 20 | 21 | - [Node.js (use v18 or newer)](https://nodejs.org/en/download/current) 22 | - [pnpm (use v8 or newer)](https://pnpm.io/installation) 23 | - [Docker desktop](https://www.docker.com/products/docker-desktop/) 24 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/javascript/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=./node_modules/envio/fuel.schema.json 2 | name: Fuel Greeter Indexer 3 | ecosystem: fuel 4 | networks: 5 | - id: 0 6 | start_block: 0 7 | contracts: 8 | - name: Greeter 9 | address: 0xb9bc445e5696c966dcf7e5d1237bd03c04e3ba6929bdaedfeebc7aae784c3a0b 10 | abi_file_path: abis/greeter-abi.json 11 | handler: ./src/EventHandlers.js 12 | events: 13 | - name: NewGreeting 14 | - name: ClearGreeting 15 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/javascript/src/EventHandlers.js: -------------------------------------------------------------------------------- 1 | const { Greeter } = require("generated"); 2 | 3 | // Handler for the NewGreeting event 4 | Greeter.NewGreeting.handler(async ({ event, context }) => { 5 | const userId = event.params.user.bits; // The id for the User entity 6 | const latestGreeting = event.params.greeting.value; // The greeting string that was added 7 | const currentUserEntity = await context.User.get(userId); // Optional user entity that may already exist 8 | 9 | // Update or create a new User entity 10 | const userEntity = currentUserEntity 11 | ? { 12 | id: userId, 13 | latestGreeting, 14 | numberOfGreetings: currentUserEntity.numberOfGreetings + 1, 15 | greetings: [...currentUserEntity.greetings, latestGreeting], 16 | } 17 | : { 18 | id: userId, 19 | latestGreeting, 20 | numberOfGreetings: 1, 21 | greetings: [latestGreeting], 22 | }; 23 | 24 | context.User.set(userEntity); // Set the User entity in the DB 25 | }); 26 | 27 | // Handler for the ClearGreeting event 28 | Greeter.ClearGreeting.handler(async ({ event, context }) => { 29 | const userId = event.params.user.bits; // The id for the User entity 30 | const currentUserEntity = await context.User.get(userId); // Optional user entity that may already exist 31 | 32 | if (currentUserEntity) { 33 | context.User.set({ 34 | ...currentUserEntity, 35 | latestGreeting: "", // Clear the latestGreeting 36 | }); 37 | } 38 | }); 39 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/rescript/README.md: -------------------------------------------------------------------------------- 1 | ## Envio Greeter Template on FuelVM 2 | 3 | _Please refer to the [documentation website](https://docs.envio.dev) for a thorough guide on all Envio indexer features_ 4 | 5 | ### Run 6 | 7 | ```bash 8 | pnpm dev 9 | ``` 10 | 11 | Visit http://localhost:8080 to see the GraphQL Playground, local password is `testing`. 12 | 13 | ### Generate files from `config.yaml` or `schema.graphql` 14 | 15 | ```bash 16 | pnpm codegen 17 | ``` 18 | 19 | ### Pre-requisites 20 | 21 | - [Node.js (use v18 or newer)](https://nodejs.org/en/download/current) 22 | - [pnpm (use v8 or newer)](https://pnpm.io/installation) 23 | - [Docker desktop](https://www.docker.com/products/docker-desktop/) 24 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/rescript/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=./node_modules/envio/fuel.schema.json 2 | name: Fuel Greeter Indexer 3 | ecosystem: fuel 4 | networks: 5 | - id: 0 6 | start_block: 0 7 | contracts: 8 | - name: Greeter 9 | address: 0xb9bc445e5696c966dcf7e5d1237bd03c04e3ba6929bdaedfeebc7aae784c3a0b 10 | abi_file_path: abis/greeter-abi.json 11 | handler: ./src/EventHandlers.bs.js 12 | events: 13 | - name: NewGreeting 14 | - name: ClearGreeting 15 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/rescript/rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "greeter", 3 | "version": "0.1.0", 4 | "sources": [ 5 | { 6 | "dir": "src", 7 | "subdirs": true 8 | }, 9 | { 10 | "dir": "test", 11 | "subdirs": true 12 | } 13 | ], 14 | "package-specs": { 15 | "module": "commonjs", 16 | "in-source": true 17 | }, 18 | "jsx": { 19 | "version": 4 20 | }, 21 | "suffix": ".bs.js", 22 | "bs-dependencies": ["generated", "envio"] 23 | } 24 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/rescript/src/EventHandlers.res: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | // Handler for the NewGreeting event 4 | Handlers.Greeter.NewGreeting.handler(async ({event, context}) => { 5 | let userId = event.params.user.bits // The id for the User entity 6 | let latestGreeting = event.params.greeting.value // The greeting string that was added 7 | let maybeCurrentUserEntity = await context.user.get(userId) // Optional User entity that may already exist 8 | 9 | // Update or create a new User entity 10 | let userEntity: Entities.User.t = switch maybeCurrentUserEntity { 11 | | Some(existingUserEntity) => { 12 | id: userId, 13 | latestGreeting, 14 | numberOfGreetings: existingUserEntity.numberOfGreetings + 1, 15 | greetings: existingUserEntity.greetings->Belt.Array.concat([latestGreeting]), 16 | } 17 | | None => { 18 | id: userId, 19 | latestGreeting, 20 | numberOfGreetings: 1, 21 | greetings: [latestGreeting], 22 | } 23 | } 24 | 25 | context.user.set(userEntity) // Set the User entity in the DB 26 | }) 27 | 28 | // Handler for the ClearGreeting event 29 | Handlers.Greeter.ClearGreeting.handler(async ({event, context}) => { 30 | let userId = event.params.user.bits // The id for the User entity 31 | let maybeCurrentUserEntity = await context.user.get(userId) // Optional User entity that may already exist 32 | 33 | switch maybeCurrentUserEntity { 34 | | Some(existingUserEntity) => context.user.set({...existingUserEntity, latestGreeting: ""}) // Clear the latestGreeting 35 | | None => () 36 | } 37 | }) 38 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/shared/.env.example: -------------------------------------------------------------------------------- 1 | # To create or update a token visit https://envio.dev/app/api-tokens 2 | ENVIO_API_TOKEN="" 3 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/shared/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/* 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | .bsb.lock 27 | /node_modules/ 28 | benchmarks/ 29 | artifacts 30 | cache 31 | build 32 | *.bs.js 33 | generated 34 | .env 35 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/shared/schema.graphql: -------------------------------------------------------------------------------- 1 | type User { 2 | id: ID! 3 | greetings: [String!]! 4 | latestGreeting: String! 5 | numberOfGreetings: Int! 6 | } 7 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/typescript/README.md: -------------------------------------------------------------------------------- 1 | ## Envio Greeter Template on FuelVM 2 | 3 | _Please refer to the [documentation website](https://docs.envio.dev) for a thorough guide on all Envio indexer features_ 4 | 5 | ### Run 6 | 7 | ```bash 8 | pnpm dev 9 | ``` 10 | 11 | Visit http://localhost:8080 to see the GraphQL Playground, local password is `testing`. 12 | 13 | ### Generate files from `config.yaml` or `schema.graphql` 14 | 15 | ```bash 16 | pnpm codegen 17 | ``` 18 | 19 | ### Pre-requisites 20 | 21 | - [Node.js (use v18 or newer)](https://nodejs.org/en/download/current) 22 | - [pnpm (use v8 or newer)](https://pnpm.io/installation) 23 | - [Docker desktop](https://www.docker.com/products/docker-desktop/) 24 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/typescript/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=./node_modules/envio/fuel.schema.json 2 | name: Fuel Greeter Indexer 3 | ecosystem: fuel 4 | networks: 5 | - id: 0 6 | start_block: 0 7 | contracts: 8 | - name: Greeter 9 | address: 0xb9bc445e5696c966dcf7e5d1237bd03c04e3ba6929bdaedfeebc7aae784c3a0b 10 | abi_file_path: abis/greeter-abi.json 11 | handler: ./src/EventHandlers.ts 12 | events: 13 | - name: NewGreeting 14 | - name: ClearGreeting 15 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/typescript/src/EventHandlers.ts: -------------------------------------------------------------------------------- 1 | import { Greeter, User } from "generated"; 2 | 3 | // Handler for the NewGreeting event 4 | Greeter.NewGreeting.handler(async ({ event, context }) => { 5 | const userId = event.params.user.bits; 6 | const latestGreeting = event.params.greeting.value; 7 | const currentUserEntity: User | undefined = await context.User.get(userId); 8 | 9 | // Update or create a new User entity 10 | const userEntity: User = currentUserEntity 11 | ? { 12 | id: userId, 13 | latestGreeting, 14 | numberOfGreetings: currentUserEntity.numberOfGreetings + 1, 15 | greetings: [...currentUserEntity.greetings, latestGreeting], 16 | } 17 | : { 18 | id: userId, 19 | latestGreeting, 20 | numberOfGreetings: 1, 21 | greetings: [latestGreeting], 22 | }; 23 | 24 | context.User.set(userEntity); 25 | }); 26 | 27 | // Handler for the ClearGreeting event 28 | Greeter.ClearGreeting.handler(async ({ event, context }) => { 29 | const userId = event.params.user.bits; 30 | const currentUserEntity: User | undefined = await context.User.get(userId); 31 | 32 | if (currentUserEntity) { 33 | // Clear the latestGreeting 34 | context.User.set({ 35 | ...currentUserEntity, 36 | latestGreeting: "", 37 | }); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /codegenerator/cli/templates/static/greeteronfuel_template/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", //required for use with BigInt types 4 | "lib": [ 5 | "es2020" 6 | ], 7 | "allowJs": true, 8 | "checkJs": false, 9 | "outDir": "build", 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true, 15 | "module": "CommonJS" 16 | }, 17 | "include": [ 18 | "src", 19 | "test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /codegenerator/cli/test/abis/Contract1.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": false, 7 | "name": "id", 8 | "type": "uint256" 9 | }, 10 | { 11 | "indexed": false, 12 | "name": "owner", 13 | "type": "address" 14 | }, 15 | { 16 | "indexed": false, 17 | "name": "displayName", 18 | "type": "string" 19 | }, 20 | { 21 | "indexed": false, 22 | "name": "imageUrl", 23 | "type": "string" 24 | } 25 | ], 26 | "name": "NewGravatar", 27 | "type": "event" 28 | }, 29 | { 30 | "anonymous": false, 31 | "inputs": [ 32 | { 33 | "indexed": false, 34 | "name": "id", 35 | "type": "uint256" 36 | }, 37 | { 38 | "indexed": false, 39 | "name": "owner", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "name": "displayName", 45 | "type": "string" 46 | }, 47 | { 48 | "indexed": false, 49 | "name": "imageUrl", 50 | "type": "string" 51 | } 52 | ], 53 | "name": "UpdatedGravatar", 54 | "type": "event" 55 | } 56 | ] 57 | -------------------------------------------------------------------------------- /codegenerator/cli/test/abis/Contract2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": false, 7 | "name": "id", 8 | "type": "uint256" 9 | }, 10 | { 11 | "indexed": false, 12 | "name": "owner", 13 | "type": "address" 14 | }, 15 | { 16 | "indexed": false, 17 | "name": "displayName", 18 | "type": "string" 19 | }, 20 | { 21 | "indexed": false, 22 | "name": "imageUrl", 23 | "type": "string" 24 | } 25 | ], 26 | "name": "NewGravatar", 27 | "type": "event" 28 | }, 29 | { 30 | "anonymous": false, 31 | "inputs": [ 32 | { 33 | "indexed": false, 34 | "name": "id", 35 | "type": "uint256" 36 | }, 37 | { 38 | "indexed": false, 39 | "name": "owner", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "name": "displayName", 45 | "type": "string" 46 | }, 47 | { 48 | "indexed": false, 49 | "name": "imageUrl", 50 | "type": "string" 51 | } 52 | ], 53 | "name": "UpdatedGravatar", 54 | "type": "event" 55 | } 56 | ] 57 | -------------------------------------------------------------------------------- /codegenerator/cli/test/abis/nested-abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [ 3 | { 4 | "anonymous": false, 5 | "inputs": [ 6 | { 7 | "indexed": false, 8 | "name": "id", 9 | "type": "uint256" 10 | }, 11 | { 12 | "indexed": false, 13 | "name": "owner", 14 | "type": "address" 15 | }, 16 | { 17 | "indexed": false, 18 | "name": "displayName", 19 | "type": "string" 20 | }, 21 | { 22 | "indexed": false, 23 | "name": "imageUrl", 24 | "type": "string" 25 | } 26 | ], 27 | "name": "NewGravatar", 28 | "type": "event" 29 | }, 30 | { 31 | "anonymous": false, 32 | "inputs": [ 33 | { 34 | "indexed": false, 35 | "name": "id", 36 | "type": "uint256" 37 | }, 38 | { 39 | "indexed": false, 40 | "name": "owner", 41 | "type": "address" 42 | }, 43 | { 44 | "indexed": false, 45 | "name": "displayName", 46 | "type": "string" 47 | }, 48 | { 49 | "indexed": false, 50 | "name": "imageUrl", 51 | "type": "string" 52 | } 53 | ], 54 | "name": "UpdatedGravatar", 55 | "type": "event" 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/config1.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../npm/envio/evm.schema.json 2 | name: config1 3 | schema: ../schemas/schema.graphql 4 | description: Gravatar for Ethereum 5 | networks: 6 | - id: 1 7 | rpc_config: 8 | url: https://eth.com # RPC URL that will be used to subscribe to blockchain data on this network 9 | initial_block_interval: 10000 10 | backoff_multiplicative: 0.8 11 | acceleration_additive: 2000 12 | interval_ceiling: 10000 13 | backoff_millis: 5000 14 | query_timeout_millis: 20000 15 | start_block: 0 16 | contracts: 17 | - name: Contract1 18 | abi_file_path: ../abis/Contract1.json 19 | handler: ./src/EventHandler.js 20 | address: "0x2E645469f354BB4F5c8a05B3b30A929361cf77eC" 21 | events: 22 | - event: "NewGravatar" 23 | - event: "UpdatedGravatar" 24 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/config2.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../npm/envio/evm.schema.json 2 | name: config2 3 | description: Gravatar for Ethereum 4 | networks: 5 | - id: 1 6 | rpc_config: 7 | url: https://eth.com # RPC URL that will be used to subscribe to blockchain data on this network 8 | initial_block_interval: 10000 9 | backoff_multiplicative: 0.8 10 | acceleration_additive: 2000 11 | interval_ceiling: 10000 12 | backoff_millis: 5000 13 | query_timeout_millis: 20000 14 | start_block: 0 15 | contracts: 16 | - name: Contract1 17 | abi_file_path: ../abis/Contract1.json 18 | handler: ./src/EventHandler.js 19 | address: ["0x2E645469f354BB4F5c8a05B3b30A929361cf77eC"] 20 | events: 21 | - event: "NewGravatar" 22 | - event: "UpdatedGravatar" 23 | - id: 2 24 | rpc_config: 25 | url: 26 | - https://eth.com # RPC URL that will be used to subscribe to blockchain data on this network 27 | - https://eth.com/fallback # Fallback RPC URL 28 | initial_block_interval: 10000 29 | backoff_multiplicative: 0.8 30 | acceleration_additive: 2000 31 | interval_ceiling: 10000 32 | backoff_millis: 5000 33 | query_timeout_millis: 20000 34 | start_block: 0 35 | contracts: 36 | - name: Contract2 37 | abi_file_path: ../abis/Contract2.json 38 | handler: ./src/EventHandler.js 39 | address: "0x1E645469f354BB4F5c8a05B3b30A929361cf77eC" 40 | events: 41 | - event: "NewGravatar" 42 | - event: "UpdatedGravatar" 43 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/config3.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../npm/envio/evm.schema.json 2 | name: config3 3 | schema: ../schemas/schema.graphql 4 | description: Gravatar for Ethereum 5 | networks: 6 | - id: 1 7 | start_block: 0 8 | rpc: https://fallback.eth.com// 9 | contracts: 10 | - name: Contract1 11 | abi_file_path: ../abis/Contract1.json 12 | handler: ./src/EventHandler.js 13 | address: "0x2E645469f354BB4F5c8a05B3b30A929361cf77eC" 14 | events: 15 | - event: "NewGravatar" 16 | - event: "UpdatedGravatar" 17 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/config4.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../npm/envio/evm.schema.json 2 | name: config4 3 | description: Gravatar for Ethereum 4 | networks: 5 | - id: 1 6 | hypersync_config: 7 | url: https://myskar.com # RPC URL that will be used to subscribe to blockchain data on this network 8 | start_block: 0 9 | contracts: [] 10 | - id: 137 #Polygon will use skar 11 | start_block: 0 12 | contracts: [] 13 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/config5.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../npm/envio/evm.schema.json 2 | name: config4 3 | description: Gravatar for Ethereum 4 | networks: 5 | - id: 2 #ID does not exist and no config provided 6 | start_block: 0 7 | contracts: [] 8 | - id: 1564830818 #SKALECalypso 9 | start_block: 0 10 | contracts: [] 11 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/dynamic-address-config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../npm/envio/evm.schema.json 2 | name: config1 3 | schema: ../schemas/schema.graphql 4 | contracts: 5 | - name: Contract2 6 | abi_file_path: ../abis/Contract1.json 7 | handler: ./src/EventHandler.js 8 | events: 9 | - event: "NewGravatar" 10 | - event: "UpdatedGravatar" 11 | networks: 12 | - id: 1 13 | start_block: 0 14 | contracts: 15 | - name: Contract1 16 | abi_file_path: ../abis/Contract1.json 17 | handler: ./src/EventHandler.js 18 | events: 19 | - event: "NewGravatar" 20 | - event: "UpdatedGravatar" 21 | - id: 137 22 | start_block: 0 23 | contracts: 24 | - name: Contract2 25 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/factory-contract-config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../npm/envio/evm.schema.json 2 | name: uniswap indexer 3 | description: My Awesome Contract 4 | networks: 5 | - id: 1 6 | start_block: 0 7 | contracts: 8 | - name: Factory 9 | address: "0x1F98431c8aD98523631AE4a59f267346ea31F984" 10 | handler: src/EventHandlers.ts 11 | events: 12 | - event: PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool) 13 | - name: Pool 14 | handler: src/EventHandlers.ts 15 | events: 16 | - event: Swap(address indexed sender,address indexed recipient,int256 amount0,int256 amount1,uint160 sqrtPriceX96,uint128 liquidity,int24 tick) 17 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/fuel-config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../npm/envio/fuel.schema.json 2 | name: Fuel indexer 3 | ecosystem: fuel 4 | networks: 5 | - id: 0 6 | start_block: 0 7 | contracts: 8 | - name: Greeter 9 | address: 0x4a2ce054e3e94155f7092f7365b212f7f45105b74819c623744ebcc5d065c6ac 10 | abi_file_path: ../abis/greeter-abi.json 11 | handler: ./src/EventHandlers.js 12 | events: 13 | - name: NewGreeting 14 | - name: ClearGreeting 15 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/graph-manifest.yaml: -------------------------------------------------------------------------------- 1 | dataSources: 2 | - kind: ethereum/contract 3 | mapping: 4 | abis: 5 | - file: 6 | /: /ipfs/QmXFxbbdQaLzqKYwr5hmp2WX3qrdW17KhBtdDQCu4NVenj 7 | name: FiatTokenV1 8 | apiVersion: 0.0.4 9 | entities: 10 | - User 11 | - Minter 12 | - UserCounter 13 | - MinterCounter 14 | - TransferCounter 15 | - TotalSupply 16 | eventHandlers: 17 | - event: "Burn(indexed address,uint256)" 18 | handler: handleBurn 19 | - event: "Mint(indexed address,indexed address,uint256)" 20 | handler: handleMint 21 | - event: "Transfer(indexed address,indexed address,uint256)" 22 | handler: handleTransfer 23 | file: 24 | /: /ipfs/Qma4SdxZ9BSPqjWfK3JM8UFsBKUNQydHpxvYaj1cLauNNh 25 | kind: ethereum/events 26 | language: wasm/assemblyscript 27 | name: FiatTokenV1 28 | network: mainnet 29 | source: 30 | abi: FiatTokenV1 31 | address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" 32 | startBlock: 6082465 33 | description: USDC 34 | repository: "https://github.com/centrehq/usdc-subgraph" 35 | schema: 36 | file: 37 | /: /ipfs/QmZ81YMckH8LxaLd9MnaGugvbvC9Mto3Ye3Vz4ydWE7npt 38 | specVersion: 0.0.2 39 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/gravatar-with-required-entities.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../npm/envio/evm.schema.json 2 | name: gravatar-with-required-entities 3 | schema: ../schemas/gravatar-schema.graphql 4 | description: Gravatar for Ethereum 5 | networks: 6 | - id: 1 7 | start_block: 0 8 | contracts: 9 | - name: Contract1 10 | abi_file_path: ../abis/Contract1.json 11 | handler: ./src/EventHandler.js 12 | address: "0x2E645469f354BB4F5c8a05B3b30A929361cf77eC" 13 | events: 14 | - event: "NewGravatar" 15 | - event: "UpdatedGravatar" 16 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/invalid-multiple-sync-config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../npm/envio/evm.schema.json 2 | name: config 3 | description: Gravatar for Ethereum 4 | networks: 5 | - id: 1 6 | hypersync_config: 7 | url: https://myskar.com # RPC URL that will be used to subscribe to blockchain data on this network 8 | rpc_config: 9 | url: https://eth.com # RPC URL that will be used to subscribe to blockchain data on this network 10 | start_block: 0 11 | contracts: [] 12 | - id: 59144 #Linea 13 | start_block: 0 14 | contracts: [] 15 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/nested-abi.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../npm/envio/evm.schema.json 2 | name: config3 3 | schema: ../schemas/schema.graphql 4 | description: Gravatar for Ethereum with nested ABI 5 | networks: 6 | - id: 1 7 | rpc_config: 8 | url: https://eth-mainnet.g.alchemy.com/v2/demo 9 | initial_block_interval: 10000 10 | backoff_multiplicative: 0.8 11 | acceleration_additive: 2000 12 | interval_ceiling: 10000 13 | backoff_millis: 5000 14 | query_timeout_millis: 20000 15 | start_block: 0 16 | contracts: 17 | - name: Contract3 18 | abi_file_path: ../abis/nested-abi.json 19 | handler: ./src/EventHandler.js 20 | address: "0x2E645469f354BB4F5c8a05B3b30A929361cf77eC" 21 | events: 22 | - event: "NewGravatar" 23 | - event: "UpdatedGravatar" 24 | field_selection: 25 | transaction_fields: 26 | - !from 27 | - !value 28 | block_fields: 29 | - !difficulty 30 | -------------------------------------------------------------------------------- /codegenerator/cli/test/configs/schema.graphql: -------------------------------------------------------------------------------- 1 | #this is a default schema for configs in this folder that do 2 | #not have a defined schema path 3 | 4 | type EmptyEntity { 5 | id: ID! 6 | emptyField: ID! 7 | } 8 | -------------------------------------------------------------------------------- /codegenerator/cli/test/schemas/gravatar-schema.graphql: -------------------------------------------------------------------------------- 1 | type Gravatar { 2 | id: ID! 3 | } 4 | -------------------------------------------------------------------------------- /codegenerator/cli/test/schemas/schema-with-directive.graphql: -------------------------------------------------------------------------------- 1 | type User { 2 | id: ID! 3 | address: String! 4 | balance: BigInt! 5 | tokenBalances: [TokenBalance!]! @derivedFrom(field: "user") 6 | } 7 | 8 | type TokenBalance { 9 | id: ID! 10 | name: String! 11 | symbol: String! 12 | balance: BigInt! 13 | user: User! 14 | } 15 | -------------------------------------------------------------------------------- /codegenerator/cli/test/schemas/schema.graphql: -------------------------------------------------------------------------------- 1 | #test schema 2 | 3 | type EmptyEntity { 4 | id: ID! 5 | emptyField: ID! 6 | } 7 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "integration_tests" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.75" 10 | envio = { path = "../cli" } 11 | reqwest = "0.11.22" 12 | strum = "0.26" 13 | tempfile = "3.2.0" 14 | tokio = { version = "1.28.2", features = [ 15 | "macros", 16 | "process", 17 | "rt-multi-thread", 18 | "time", 19 | "full", 20 | "test-util", 21 | ] } 22 | 23 | [features] 24 | integration_tests = [] 25 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/WildcardUniFactory.js: -------------------------------------------------------------------------------- 1 | // Test that wildcard events with filters work. No address is applied to the wildcard event. 2 | // and only DAI pools should be indexed. 3 | const assert = require("assert"); 4 | const { 5 | fetchQueryWithTestCallback, 6 | } = require("./graphqlFetchWithTestCallback"); 7 | const { chainMetadataTests } = require("./databaseTestHelpers"); 8 | 9 | const DAI_ADDRESS = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; 10 | const maxRetryMessage = 11 | "Max retries reached - if you have changed the shape of the chain_metadata table update this test and the UI before releasing."; 12 | 13 | const pollGraphQL = async () => { 14 | const poolCreatedQuery = `query { 15 | PoolCreated { 16 | token0 17 | token1 18 | } 19 | } 20 | `; 21 | 22 | console.log("Starting endblock restart tests for dynamic contracts"); 23 | // TODO: make this use promises rather than callbacks. 24 | fetchQueryWithTestCallback( 25 | poolCreatedQuery, 26 | maxRetryMessage, 27 | ({ PoolCreated }) => { 28 | try { 29 | assert( 30 | PoolCreated.length > 1, 31 | "Should return at least 1 PoolCreated event" 32 | ); 33 | 34 | PoolCreated.forEach((event) => { 35 | assert( 36 | event.token0 === DAI_ADDRESS || event.token1 === DAI_ADDRESS, 37 | "Should have DAI address in either token0 or token1" 38 | ); 39 | }); 40 | } catch (err) { 41 | //gotta love javascript 42 | err.shouldExitOnFailure = true; 43 | throw err; 44 | } 45 | console.log("Finished running dynamic contract chain_metadata tests"); 46 | } 47 | ); 48 | }; 49 | 50 | pollGraphQL(); 51 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/evm_Greeter.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | const { 3 | fetchQueryWithTestCallback, 4 | } = require("./graphqlFetchWithTestCallback"); 5 | 6 | const maxRetryFailureMessage = 7 | "Max retries reached - either increase the timeout (maxRetries) or check for other bugs."; 8 | 9 | const pollGraphQL = async () => { 10 | const userEntityQuery = ` 11 | { 12 | User_by_pk(id: "0xf28eA36e3E68Aff0e8c9bFF8037ba2150312ac48") { 13 | id 14 | greetings 15 | numberOfGreetings 16 | } 17 | } 18 | `; 19 | 20 | console.log("[js context] Starting running test Greeter - user entity check"); 21 | fetchQueryWithTestCallback( 22 | userEntityQuery, 23 | maxRetryFailureMessage, 24 | ({ User_by_pk: user }) => { 25 | let shouldExitOnFailure = false; 26 | try { 27 | assert(!!user, "greeting should not be null or undefined"); 28 | assert( 29 | user.greetings.slice(0, 5).join(",") === 30 | // First "gm Linea,assah dude" are from Linea chain 31 | // The rest should be from Polygon 32 | "gm Linea,assah dude,gm,gn,gm paris", 33 | "First 5 greetings should be 'gm Linea,assah dude,gm,gn,gm paris'" 34 | ); 35 | assert(user.numberOfGreetings >= 3, "numberOfGreetings should be >= 3"); 36 | console.log("Second test passed."); 37 | } catch (err) { 38 | //gotta love javascript 39 | err.shouldExitOnFailure = shouldExitOnFailure; 40 | throw err; 41 | } 42 | } 43 | ); 44 | }; 45 | 46 | pollGraphQL(); 47 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/fuel_Greeter.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | const { 3 | fetchQueryWithTestCallback, 4 | } = require("./graphqlFetchWithTestCallback"); 5 | 6 | const maxRetryFailureMessage = 7 | "Max retries reached - either increase the timeout (maxRetries) or check for other bugs."; 8 | 9 | const pollGraphQL = async () => { 10 | const userEntityQuery = ` 11 | { 12 | User_by_pk(id: "0x2072fe0e4c1cf1fe7ba3c4569992908fe4d7aecc9655c9d0c4da9285ade32c5f") { 13 | id 14 | greetings 15 | numberOfGreetings 16 | } 17 | } 18 | `; 19 | 20 | console.log("[js context] Starting running test Greeter - user entity check"); 21 | fetchQueryWithTestCallback( 22 | userEntityQuery, 23 | maxRetryFailureMessage, 24 | ({ User_by_pk: user }) => { 25 | let shouldExitOnFailure = false; 26 | try { 27 | assert(!!user, "greeting should not be null or undefined"); 28 | assert( 29 | user.greetings.slice(0, 3).toString() === 30 | "Hi envio,NotHello,Hi Again", 31 | "First 3 greetings should be 'Hi envio,NotHello,Hi Again'" 32 | ); 33 | assert(user.numberOfGreetings >= 3, "numberOfGreetings should be >= 3"); 34 | console.log("Second test passed."); 35 | } catch (err) { 36 | //gotta love javascript 37 | err.shouldExitOnFailure = shouldExitOnFailure; 38 | throw err; 39 | } 40 | } 41 | ); 42 | }; 43 | 44 | pollGraphQL(); 45 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/runAll.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e # Exit on error 4 | 5 | cargo run #calls the function run_all_init_combinations in main.rs to create all templates 6 | 7 | # NOTE: this test must be run from the root `integration_tests` folder via `./tests/runAll.sh` 8 | 9 | cp -R -f ./tests/test_indexers ./integration_test_output 10 | 11 | export TEMPLATE="evm_Erc20" 12 | LANGUAGE="JavaScript" ./tests/runSingle.sh 13 | LANGUAGE="TypeScript" ./tests/runSingle.sh 14 | LANGUAGE="ReScript" ./tests/runSingle.sh 15 | 16 | export TEMPLATE="evm_Greeter" 17 | LANGUAGE="JavaScript" ./tests/runSingle.sh 18 | LANGUAGE="TypeScript" ./tests/runSingle.sh 19 | LANGUAGE="ReScript" ./tests/runSingle.sh 20 | 21 | export TEMPLATE="fuel_Greeter" 22 | LANGUAGE="JavaScript" ./tests/runSingle.sh 23 | LANGUAGE="TypeScript" ./tests/runSingle.sh 24 | LANGUAGE="ReScript" ./tests/runSingle.sh 25 | 26 | export TEMPLATE="test_indexers/test_exits" 27 | export TEST_FILE="EndblockSuccess" 28 | CONFIG_FILE="config.yaml" SHOULD_FAIL=false TEST_RESTART=false ./tests/testIndexerExits.sh 29 | CONFIG_FILE="config-broken.yaml" SHOULD_FAIL=true TEST_RESTART=false ./tests/testIndexerExits.sh 30 | 31 | export TEMPLATE="test_indexers/dynamic_contracts" 32 | export TEST_FILE="DynamicContracts" 33 | CONFIG_FILE="config-dynamic-contracts.yaml" SHOULD_FAIL=false TEST_RESTART=true ./tests/testIndexerExits.sh 34 | 35 | export TEMPLATE="test_indexers/wildcard-uni-factory" 36 | export TEST_FILE="WildcardUniFactory" 37 | CONFIG_FILE="config.yaml" SHOULD_FAIL=false ./tests/testIndexerExits.sh 38 | 39 | export TEMPLATE="test_indexers/wildcard-uni-factory" 40 | export TEST_FILE="WildcardUniFactory" 41 | ENVIO_PG_PUBLIC_SCHEMA="custom" CONFIG_FILE="config.yaml" SHOULD_FAIL=false ./tests/testIndexerExits.sh 42 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/dynamic_contracts/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/* 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | .bsb.lock 27 | /node_modules/ 28 | benchmarks/ 29 | artifacts 30 | cache 31 | generated 32 | logs 33 | *.bs.js 34 | *.bs.mjs 35 | *.gen.ts 36 | build 37 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/dynamic_contracts/config-dynamic-contracts.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../../../cli/npm/envio/evm.schema.json 2 | name: kwenta-factory-example 3 | field_selection: 4 | transaction_fields: 5 | - hash 6 | - transactionIndex 7 | networks: 8 | - id: 10 9 | start_block: 4556306 10 | end_block: 6942099 11 | contracts: 12 | - name: FuturesMarketManager 13 | address: 14 | - 0xc704c9AA89d1ca60F67B3075d05fBb92b3B00B3B 15 | handler: src/DynamicContractHandlers.ts 16 | events: 17 | - event: MarketAdded(address market, bytes32 indexed asset, bytes32 indexed marketKey) 18 | - name: FuturesMarket 19 | address: 20 | - 0x9F1C2f0071Bc3b31447AEda9fA3A68d651eB4632 21 | handler: src/DynamicContractHandlers.ts 22 | events: 23 | - event: CacheUpdated(bytes32 name, address destination) 24 | - event: FundingRecomputed(int256 funding, uint256 index, uint256 timestamp) 25 | - event: FuturesTracking(bytes32 indexed trackingCode, bytes32 baseAsset, bytes32 marketKey, int256 sizeDelta, uint256 fee) 26 | - event: MarginTransferred(address indexed account, int256 marginDelta) 27 | - event: NextPriceOrderRemoved(address indexed account, uint256 currentRoundId, int256 sizeDelta, uint256 targetRoundId, uint256 commitDeposit, uint256 keeperDeposit, bytes32 trackingCode) 28 | - event: NextPriceOrderSubmitted(address indexed account, int256 sizeDelta, uint256 targetRoundId, uint256 commitDeposit, uint256 keeperDeposit, bytes32 trackingCode) 29 | - event: PositionLiquidated(uint256 indexed id, address indexed account, address indexed liquidator, int256 size, uint256 price, uint256 fee) 30 | - event: PositionModified(uint256 indexed id, address indexed account, uint256 margin, int256 size, int256 tradeSize, uint256 lastPrice, uint256 fundingIndex, uint256 fee) 31 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/dynamic_contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kwenta-factory-example", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "clean": "tsc --clean", 6 | "build": "tsc --build", 7 | "watch": "tsc --watch", 8 | "mocha": "ts-mocha test/**/*.ts", 9 | "codegen": "envio codegen", 10 | "dev": "envio dev", 11 | "test": "pnpm mocha", 12 | "start": "ts-node generated/src/Index.bs.js" 13 | }, 14 | "devDependencies": { 15 | "@types/chai": "^4.3.11", 16 | "@types/mocha": "10.0.6", 17 | "@types/node": "20.8.8", 18 | "ts-mocha": "^10.0.0", 19 | "ts-node": "10.9.1", 20 | "typescript": "5.2.2", 21 | "mocha": "10.2.0" 22 | }, 23 | "dependencies": { 24 | "chai": "4.3.10", 25 | "envio": "file:../../../../cli/npm/envio" 26 | }, 27 | "optionalDependencies": { 28 | "generated": "./generated" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/dynamic_contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", //required for use with BigInt types 4 | "lib": [ 5 | "es2020" 6 | ], 7 | "allowJs": true, 8 | "checkJs": false, 9 | "outDir": "build", 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true, 15 | "module": "CommonJS" 16 | }, 17 | "include": [ 18 | "src", 19 | "test", 20 | "generated" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/test_exits/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/* 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | .bsb.lock 27 | /node_modules/ 28 | benchmarks/ 29 | artifacts 30 | cache 31 | generated 32 | logs 33 | *.bs.js 34 | *.bs.mjs 35 | *.gen.ts 36 | build 37 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/test_exits/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-exits", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "clean": "tsc --clean", 6 | "build": "tsc --build", 7 | "watch": "tsc --watch", 8 | "codegen": "envio codegen", 9 | "start": "ts-node generated/src/Index.bs.js" 10 | }, 11 | "devDependencies": { 12 | "@types/node": "^18.16.1", 13 | "typescript": "^5.0.4" 14 | }, 15 | "dependencies": { 16 | "ts-node": "^10.9.1", 17 | "envio": "file:../../../../cli/npm/envio" 18 | }, 19 | "optionalDependencies": { 20 | "generated": "./generated" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/test_exits/schema.graphql: -------------------------------------------------------------------------------- 1 | type Swap @entity { 2 | id: ID! 3 | recipient: String! 4 | sender: String! 5 | amount0: BigInt! 6 | amount1: BigInt! 7 | sqrtPriceX96: BigInt! 8 | liquidity: BigInt! 9 | tick: BigInt! 10 | blockNumber: Int! 11 | blockTimestamp: Int! 12 | transactionHash: String! 13 | } 14 | 15 | type EventTracker @entity { 16 | id: ID! 17 | count: Int! 18 | } 19 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/test_exits/src/BrokenHandler.ts: -------------------------------------------------------------------------------- 1 | // Empty handler 2 | let x = 3 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/test_exits/src/EventHandler.ts: -------------------------------------------------------------------------------- 1 | /* 2 | *Please refer to https://docs.envio.dev for a thorough guide on all Envio indexer features* 3 | */ 4 | 5 | import { SwapContract } from "generated"; 6 | 7 | SwapContract.Swap.handler(async ({ event, context }) => { 8 | context.EventTracker.set({ 9 | id: "eventTracker", 10 | count: 1, 11 | }); 12 | 13 | context.Swap.set({ 14 | id: event.transaction.hash + event.logIndex, 15 | recipient: event.params.recipient, 16 | sender: event.params.sender, 17 | amount0: event.params.amount0, 18 | amount1: event.params.amount1, 19 | sqrtPriceX96: event.params.sqrtPriceX96, 20 | liquidity: event.params.liquidity, 21 | tick: event.params.tick, 22 | blockNumber: event.block.number, 23 | blockTimestamp: event.block.timestamp, 24 | transactionHash: event.transaction.hash, 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/test_exits/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", //required for use with BigInt types 4 | "module": "commonjs", 5 | "lib": ["es6"], 6 | "allowJs": true, 7 | "checkJs": false, 8 | "outDir": "build", 9 | "rootDirs": ["src", "generated"], 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/wildcard-uni-factory/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/* 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | .bsb.lock 27 | /node_modules/ 28 | benchmarks/ 29 | artifacts 30 | cache 31 | build 32 | *.bs.js 33 | generated 34 | .env 35 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/wildcard-uni-factory/README.md: -------------------------------------------------------------------------------- 1 | ## Envio Greeter Template 2 | 3 | *Please refer to the [documentation website](https://docs.envio.dev) for a thorough guide on all [Envio](https://envio.dev) indexer features* 4 | 5 | ### Run 6 | 7 | ```bash 8 | pnpm dev 9 | ``` 10 | 11 | Visit http://localhost:8080 to see the GraphQL Playground, local password is `testing`. 12 | 13 | ### Generate files from `config.yaml` or `schema.graphql` 14 | 15 | ```bash 16 | pnpm codegen 17 | ``` 18 | 19 | ### Pre-requisites 20 | 21 | - [Node.js (use v18 or newer)](https://nodejs.org/en/download/current) 22 | - [pnpm (use v8 or newer)](https://pnpm.io/installation) 23 | - [Docker desktop](https://www.docker.com/products/docker-desktop/) 24 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/wildcard-uni-factory/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=./node_modules/envio/evm.schema.json 2 | name: wildcard-uni-factory 3 | field_selection: 4 | transaction_fields: 5 | - hash 6 | networks: 7 | - id: 1 8 | start_block: 0 9 | end_block: 20741509 10 | contracts: 11 | - name: UniswapV3Factory 12 | handler: ./src/EventHandlers.ts 13 | events: 14 | - event: event PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool) 15 | rollback_on_reorg: false 16 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/wildcard-uni-factory/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "envio-indexer", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "clean": "tsc --clean", 6 | "build": "tsc --build", 7 | "watch": "tsc --watch", 8 | "mocha": "ts-mocha test/**/*.ts", 9 | "codegen": "envio codegen", 10 | "dev": "envio dev", 11 | "test": "pnpm mocha", 12 | "start": "ts-node generated/src/Index.bs.js" 13 | }, 14 | "devDependencies": { 15 | "@types/chai": "^4.3.11", 16 | "@types/mocha": "10.0.6", 17 | "@types/node": "20.8.8", 18 | "ts-mocha": "^10.0.0", 19 | "ts-node": "10.9.1", 20 | "typescript": "5.2.2", 21 | "chai": "4.3.10", 22 | "mocha": "10.2.0" 23 | }, 24 | "dependencies": { 25 | "envio": "file:../../../../cli/npm/envio" 26 | }, 27 | "optionalDependencies": { 28 | "generated": "./generated" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/wildcard-uni-factory/schema.graphql: -------------------------------------------------------------------------------- 1 | type PoolCreated { 2 | id: ID! 3 | token0: String! 4 | token1: String! 5 | fee: BigInt! 6 | tickSpacing: BigInt! 7 | pool: String! 8 | } 9 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/wildcard-uni-factory/src/EventHandlers.ts: -------------------------------------------------------------------------------- 1 | import { UniswapV3Factory } from "generated"; 2 | 3 | const DAI_ADDRESS = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; 4 | UniswapV3Factory.PoolCreated.handler( 5 | async ({ event, context }) => { 6 | context.PoolCreated.set({ 7 | id: event.transaction.hash + event.logIndex.toString(), 8 | token0: event.params.token0, 9 | token1: event.params.token1, 10 | fee: event.params.fee, 11 | tickSpacing: event.params.tickSpacing, 12 | pool: event.params.pool, 13 | }); 14 | }, 15 | { 16 | wildcard: true, 17 | eventFilters: [{ token0: DAI_ADDRESS }, { token1: DAI_ADDRESS }], 18 | }, 19 | ); 20 | -------------------------------------------------------------------------------- /codegenerator/integration_tests/tests/test_indexers/wildcard-uni-factory/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", //required for use with BigInt types 4 | "lib": [ 5 | "es2020" 6 | ], 7 | "allowJs": true, 8 | "checkJs": false, 9 | "outDir": "build", 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true, 15 | "module": "CommonJS" 16 | }, 17 | "include": [ 18 | "src", 19 | "test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /internal_docs/EventProcessor.md: -------------------------------------------------------------------------------- 1 | # Event Processor 2 | 3 | High level digram of event processor loop: 4 | 5 | ```mermaid 6 | flowchart TB 7 | subgraph cmq[Chain Manager With Fetchers] 8 | subgraph cfs[Chain fetchers] 9 | cf1[\Chain Fetcher 1/] 10 | cf2[\Chain Fetcher 2/] 11 | cf3[\Chain Fetcher 3/] 12 | end 13 | ei[["Event fetcher interface"]]o==ocfs 14 | end 15 | ei-.->1[Request batch from multi-chain Queue] 16 | subgraph ep[Event Processor] 17 | classDef start stroke:#f00 18 | start>START]:::start 19 | start-->1 20 | 1-->2["Run loaders on all events"] 21 | 2--"a dynamic contract was found"-->2a["Finish running the loader batch and search for any more dynamic contracts that may occur. 22 | Don't run/execute handlers on the remainder of the batch until events from dynamic contracts are loaded, 23 | the events will be deferred to the arbitrary events queue and included in a later batch."] 24 | 2a-->1 25 | 3["Execute db queries from loaders in a batch"] 26 | 2a-->3 27 | 2-->3 28 | 3-->4["Run all handlers in batch"] 29 | 4-->5["Save updated entities to db"] 30 | 5--"restart process at next batch"-->1 31 | end 32 | 2a-.->ei 33 | 3-.->dbi 34 | 5-.->dbi 35 | subgraph storage persistance 36 | dbi[["database interface"]]o==odb[(Database)] 37 | end 38 | ``` 39 | -------------------------------------------------------------------------------- /internal_docs/IndexerStructure.md: -------------------------------------------------------------------------------- 1 | # Indexer Structure 2 | 3 | The indexer runs two top level long running processes. 4 | 5 | This is the 'event fetcher' (housed in the ChainManager) which fetches events continuously from different sources such as an RPC or Skar and the 'event processor'. 6 | 7 | ```mermaid 8 | graph TD 9 | A[EventSyncing.start] -->|create| B[ChainManager] 10 | A -->|run| C[startProcessingEventsOnQueue] 11 | B -->|run| D[startFetchers] 12 | B -->|takes in| C 13 | ``` 14 | 15 | TODO: Currently the 'event processor' takes in the ChainManager, it would be better if the event processor only interacted with the ChainManager (and the queue that it pushes to) via connector of sorts which is a simplified interface. For example we call it `EventRetrievalAdaptor` or `EventRetrievalInterface`, this interface would restrict the possible interactions with the Queue that can be made from outside the system. 16 | 17 | ## Proposed: 18 | 19 | ```mermaid 20 | graph TD 21 | A[EventSyncing.start] -->|make| B[ChainManager] 22 | A -->|run| C[startProcessingEventsOnQueue] 23 | B -->|returns| D[EventRetrievalAdaptor] 24 | D -->|run| E[startFetchers] 25 | D -->|takes in| C 26 | ``` 27 | 28 | Notes: The EventProcessor doesn't rely on the ChainManager directly, this is known as Inversion of Control (IoC); the benefit is that larger changes can be made to the ChainManager and its subcomponents without worry that they will effect EventProcessor if the adaptor remains intact. The Dependency Rule says that the direction of source code dependencies is inwards whene the inner most part are the core business rules. 29 | 30 | Inside the `startFetchers` function, it calls out to a ChainFetcher for each process to manage the process of fetching events from that chain. 31 | 32 | -------------------------------------------------------------------------------- /internal_docs/README.md: -------------------------------------------------------------------------------- 1 | # Indexer 2 | 3 | ## Essential points to remember: 4 | - **Code is King**: Always assume these docs are out of date. The codebase is the definitive source of truth, NOT THE DOCS. 5 | - **Purpose of Documentation**: These docs are designed to aid in understanding, onboarding new team members, and to help inform technical decisions. They are not exhaustive and should not replace hands-on experience with the codebase. 6 | - **Not for Public Consumption**: This documentation is for internal use only. It contains proprietary information and should not be shared outside of our organization. 7 | - **Continuous Evolution**: As our product and technology evolve, so will our code. Documentation might not always keep pace with rapid changes. 8 | - **Feedback Loop**: If you spot discrepancies or areas of improvement in the documentation, please provide feedback. This is a collaborative effort, and your insights are valuable. 9 | - **Limitations**: While we strive for accuracy, remember that no documentation can capture the intricacies and nuances of the actual code. Always refer to the code for the most detailed and up-to-date information. 10 | - **Use with Caution**: Relying solely on this documentation without cross-referencing the code can lead to misunderstandings. Always validate any assumptions with the actual codebase. 11 | - **See it, say it, sort it**: (London Underground) - The docs will work best if everyone actively contributes. 12 | - **Be concise**: Please avoid long wafty paragraphs - bullet points are great. 13 | - **TODOs are ok**: Please use TODOs in the docs like we do in the code. If there are features we want in the code that 14 | - **Not a place for discussion**: Keep discussions in github discussions (or sometimes in discord - although less searchable than github discussions) - link to the discussion from docs if relevant, but remember *be concise*. 15 | - Keep docs up to date with major code changes. 16 | 17 | 18 | -------------------------------------------------------------------------------- /scenarios/erc20_multichain_factory/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/* 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | .bsb.lock 27 | node_modules/ 28 | build/ 29 | benchmarks/ 30 | artifacts 31 | cache 32 | *.bs.js 33 | generated 34 | .env 35 | -------------------------------------------------------------------------------- /scenarios/erc20_multichain_factory/.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec": [ 3 | "test/**/*.bs.js" 4 | ], 5 | "timeout": 30000, //Mostly integration tests so just precaution to stop ci from breaking 6 | "exit": true 7 | } 8 | -------------------------------------------------------------------------------- /scenarios/erc20_multichain_factory/README.md: -------------------------------------------------------------------------------- 1 | ## Multichain ERC20 scenario test for rollbacks 2 | -------------------------------------------------------------------------------- /scenarios/erc20_multichain_factory/config.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=../../codegenerator/cli/npm/envio/evm.schema.json 2 | name: erc20indexer 3 | description: ERC-20 indexer 4 | rollback_on_reorg: true 5 | field_selection: 6 | transaction_fields: 7 | - hash 8 | - transactionIndex 9 | block_fields: 10 | - gasUsed #just an example to test codegen 11 | contracts: 12 | - name: ERC20 13 | handler: src/EventHandlers.bs.js 14 | events: 15 | - event: "Approval(address indexed owner, address indexed spender, uint256 value)" 16 | - event: "Transfer(address indexed from, address indexed to, uint256 value)" 17 | - name: ERC20Factory 18 | handler: src/EventHandlers.bs.js 19 | events: 20 | - event: "TokenCreated(address indexed token)" 21 | - event: "DeleteUser(address indexed user)" 22 | networks: 23 | - id: 1 # Ethereum Mainnet 24 | start_block: 0 25 | contracts: 26 | - name: ERC20 27 | address: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984" #UNI 28 | - name: ERC20Factory 29 | address: "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199" #Dummy address that does not actually produce this event 30 | - id: 137 # Polygon 31 | start_block: 0 32 | contracts: 33 | - name: ERC20 34 | address: "0xb33EaAd8d922B1083446DC23f610c2567fB5180f" #UNI 35 | - name: ERC20Factory 36 | address: "0xdD2FD4581271e230360230F9337D5c0430Bf44C0" #Dummy address that does not actually produce this event 37 | -------------------------------------------------------------------------------- /scenarios/erc20_multichain_factory/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "erc20-multi-chain-factory", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "clean": "rescript clean", 6 | "build": "rescript", 7 | "watch": "rescript -w", 8 | "test": "pnpm build && mocha", 9 | "codegen": "cargo run --manifest-path ../../codegenerator/cli/Cargo.toml -- codegen", 10 | "dev": "pnpm build && envio dev", 11 | "start": "pnpm build && node generated/src/Index.bs.js" 12 | }, 13 | "dependencies": { 14 | "envio": "file:../../codegenerator/cli/npm/envio", 15 | "ethers": "6.8.0", 16 | "helpers": "../helpers", 17 | "mocha": "10.2.0", 18 | "rescript": "11.1.3", 19 | "rescript-schema": "9.3.0" 20 | }, 21 | "optionalDependencies": { 22 | "generated": "./generated" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /scenarios/erc20_multichain_factory/pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - ./generated 3 | - ../helpers 4 | -------------------------------------------------------------------------------- /scenarios/erc20_multichain_factory/rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "erc20", 3 | "version": "0.1.0", 4 | "sources": [ 5 | { 6 | "dir": "src", 7 | "subdirs": true 8 | }, 9 | { 10 | "dir": "test", 11 | "subdirs": true 12 | } 13 | ], 14 | "gentypeconfig": { 15 | "language": "typescript", 16 | "shims": { 17 | "Js": "Js" 18 | }, 19 | "generatedFileExtension": ".gen.ts", 20 | "debug": { 21 | "all": false, 22 | "basic": false 23 | } 24 | }, 25 | "package-specs": { 26 | "module": "commonjs", 27 | "in-source": true 28 | }, 29 | "jsx": { 30 | "version": 4 31 | }, 32 | "suffix": ".bs.js", 33 | "bs-dependencies": ["generated", "rescript-schema", "helpers", "envio"], 34 | "bsc-flags": ["-open RescriptSchema"] 35 | } 36 | -------------------------------------------------------------------------------- /scenarios/erc20_multichain_factory/schema.graphql: -------------------------------------------------------------------------------- 1 | type Account { 2 | # id is the address of the account 3 | id: ID! 4 | # account balance of tokens 5 | tokens: [AccountToken!]! @derivedFrom(field: "account") 6 | # approvals are a list of approvals that this account has given 7 | approvals: [Approval!]! @derivedFrom(field: "owner") 8 | } 9 | 10 | type AccountToken { 11 | #token address-account address 12 | id: ID! 13 | account: Account! 14 | tokenAddress: String! 15 | balance: BigInt! @index 16 | } 17 | 18 | type Approval { 19 | # id is the token address, owner address and spender address [token-owner-spender] 20 | id: ID! 21 | # amount is the amount of tokens approved 22 | amount: BigInt! 23 | # owner is the account that approved the tokens 24 | owner: Account! 25 | # spender is the account that is approved to spend the tokens 26 | spender: Account! 27 | #token related to the given approval 28 | tokenAddress: String! 29 | } 30 | -------------------------------------------------------------------------------- /scenarios/erc20_multichain_factory/test/DbHelpers.res: -------------------------------------------------------------------------------- 1 | let _sqlConfig = Db.config 2 | 3 | @@warning("-21") 4 | let resetPostgresClient: unit => unit = () => { 5 | // This is a hack to reset the postgres client between tests. postgres.js seems to cache some types, and if tests clear the DB you need to also reset sql. 6 | %raw("require('../generated/src/db/Db.bs.js').sql = require('postgres')(_sqlConfig)") 7 | } 8 | 9 | let runUpDownMigration = async () => { 10 | resetPostgresClient() 11 | (await Migrations.runUpMigrations(~shouldExit=false, ~reset=true))->ignore 12 | } 13 | -------------------------------------------------------------------------------- /scenarios/erc20_multichain_factory/test/MockChainData.res: -------------------------------------------------------------------------------- 1 | module Indexer = { 2 | module ErrorHandling = ErrorHandling 3 | module Types = Types 4 | module Config = Config 5 | module Source = Source 6 | module FetchState = FetchState 7 | } 8 | 9 | include Helpers.ChainMocking.Make(Indexer) 10 | -------------------------------------------------------------------------------- /scenarios/fuel_test/.env.example: -------------------------------------------------------------------------------- 1 | # To create or update a token visit https://envio.dev/app/api-tokens 2 | ENVIO_API_TOKEN="" 3 | -------------------------------------------------------------------------------- /scenarios/fuel_test/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/* 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | .bsb.lock 27 | /node_modules/ 28 | benchmarks/ 29 | artifacts 30 | cache 31 | build 32 | *.bs.js 33 | generated 34 | .env 35 | -------------------------------------------------------------------------------- /scenarios/fuel_test/README.md: -------------------------------------------------------------------------------- 1 | ## Envio Greeter Template on FuelVM 2 | 3 | _Please refer to the [documentation website](https://docs.envio.dev) for a thorough guide on all Envio indexer features_ 4 | 5 | ### Run 6 | 7 | ```bash 8 | pnpm dev 9 | ``` 10 | 11 | Visit http://localhost:8080 to see the GraphQL Playground, local password is `testing`. 12 | 13 | ### Generate files from `config.yaml` or `schema.graphql` 14 | 15 | ```bash 16 | pnpm codegen 17 | ``` 18 | 19 | ### Pre-requisites 20 | 21 | - [Node.js (use v18 or newer)](https://nodejs.org/en/download/current) 22 | - [pnpm (use v8 or newer)](https://pnpm.io/installation) 23 | - [Docker desktop](https://www.docker.com/products/docker-desktop/) 24 | -------------------------------------------------------------------------------- /scenarios/fuel_test/contracts/all-events/.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | target -------------------------------------------------------------------------------- /scenarios/fuel_test/contracts/all-events/Forc.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "all-events" 3 | source = "member" 4 | dependencies = ["std"] 5 | 6 | [[package]] 7 | name = "core" 8 | source = "path+from-root-4BA3A18A2D620E67" 9 | 10 | [[package]] 11 | name = "std" 12 | source = "git+https://github.com/fuellabs/sway?tag=v0.64.0#2156bfbbee01ffb85bfca2aae8f185f8e7c715a4" 13 | dependencies = ["core"] 14 | -------------------------------------------------------------------------------- /scenarios/fuel_test/contracts/all-events/Forc.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | authors = ["Dmitry Zakharov"] 3 | entry = "main.sw" 4 | license = "Apache-2.0" 5 | name = "all-events" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /scenarios/fuel_test/contracts/interaction-tools/.env.example: -------------------------------------------------------------------------------- 1 | MNENOMIC= 2 | PROVIDER_URL=testnet.fuel.network 3 | BASE_ASSET_ADDRESS=0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 -------------------------------------------------------------------------------- /scenarios/fuel_test/contracts/interaction-tools/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .env -------------------------------------------------------------------------------- /scenarios/fuel_test/contracts/interaction-tools/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "interaction-tools" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | dotenv = "0.15.0" 8 | fuels = "0.66.5" 9 | rand = "0.8.5" 10 | tokio = "1.37.0" 11 | -------------------------------------------------------------------------------- /scenarios/fuel_test/contracts/ts-interaction-tools/.env.example: -------------------------------------------------------------------------------- 1 | MNEMONIC= 2 | PROVIDER_URL=testnet.fuel.network 3 | BASE_ASSET_ADDRESS=0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07 4 | -------------------------------------------------------------------------------- /scenarios/fuel_test/contracts/ts-interaction-tools/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | src/contract -------------------------------------------------------------------------------- /scenarios/fuel_test/contracts/ts-interaction-tools/README.md: -------------------------------------------------------------------------------- 1 | 2 | Deployed contract address: 3 | 4 | 0x1eb55edd0dff3ccdb916075e29ba516eea39b9d99b7935070f9fa1c018cb2391 5 | 6 | ## To generate the contract ts bindings run 7 | 8 | `pnpm i -g fuels` 9 | 10 | `pnpm fuels typegen -i /all-events-abi.json -o -c ` 11 | 12 | * Use as src/contract dir 13 | * This cmd only worked for me using the absolute path to the abi file, not the relative path. -------------------------------------------------------------------------------- /scenarios/fuel_test/contracts/ts-interaction-tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-interaction-tools", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "deploy-contract": "ts-node ./src/deploy.ts", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@types/dotenv": "^8.2.0", 15 | "typescript": "5.2.2" 16 | }, 17 | "dependencies": { 18 | "dotenv": "16.4.5", 19 | "fuels": "^0.94.8" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /scenarios/fuel_test/contracts/ts-interaction-tools/src/@types/env.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | interface ProcessEnv { 3 | MNEMONIC?: string; 4 | PROVIDER_URL?: string; 5 | BASE_ASSET_ADDRESS?: string; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /scenarios/fuel_test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fuel_test", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "ts:clean": "tsc --clean", 6 | "ts:build": "tsc --build", 7 | "ts:watch": "tsc --watch", 8 | "ts:test": "tsc --noEmit", 9 | "res:clean": "rescript clean", 10 | "res:build": "rescript", 11 | "res:watch": "rescript -w", 12 | "mocha": "ts-mocha test/**/*.[jt]s", 13 | "codegen": "cargo run --manifest-path ../../codegenerator/cli/Cargo.toml -- codegen", 14 | "dev": "cargo run --manifest-path ../../codegenerator/cli/Cargo.toml -- dev", 15 | "test": "rescript && pnpm mocha", 16 | "start": "ts-node generated/src/Index.bs.js" 17 | }, 18 | "devDependencies": { 19 | "@types/chai": "^4.3.11", 20 | "@types/mocha": "10.0.6", 21 | "@types/node": "20.8.8", 22 | "ts-mocha": "^10.0.0", 23 | "ts-node": "10.9.1", 24 | "typescript": "5.2.2", 25 | "mocha": "10.2.0" 26 | }, 27 | "dependencies": { 28 | "chai": "4.3.10", 29 | "envio": "file:../../codegenerator/cli/npm/envio", 30 | "rescript-schema": "9.3.0", 31 | "ts-expect": "1.3.0", 32 | "rescript": "11.1.3" 33 | }, 34 | "optionalDependencies": { 35 | "generated": "./generated" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /scenarios/fuel_test/rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fuel_test", 3 | "sources": [ 4 | { 5 | "dir": "src", 6 | "subdirs": true 7 | }, 8 | { 9 | "dir": "test", 10 | "subdirs": true 11 | } 12 | ], 13 | "suffix": ".bs.js", 14 | "jsx": { 15 | "version": 4 16 | }, 17 | "package-specs": { 18 | "module": "commonjs", 19 | "in-source": true 20 | }, 21 | "bs-dependencies": ["generated", "envio", "rescript-schema"] 22 | } 23 | -------------------------------------------------------------------------------- /scenarios/fuel_test/schema.graphql: -------------------------------------------------------------------------------- 1 | type User { 2 | id: ID! 3 | greetings: [String!]! 4 | latestGreeting: String! 5 | numberOfGreetings: Int! 6 | } 7 | -------------------------------------------------------------------------------- /scenarios/fuel_test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", //required for use with BigInt types 4 | "lib": [ 5 | "es2020" 6 | ], 7 | "allowJs": true, 8 | "checkJs": false, 9 | "outDir": "build", 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true, 15 | "module": "CommonJS" 16 | }, 17 | "include": [ 18 | "src", 19 | "test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /scenarios/helpers/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules/ 3 | /lib/ 4 | .bsb.lock 5 | .merlin 6 | *.bs.js 7 | *.res.js 8 | -------------------------------------------------------------------------------- /scenarios/helpers/README.md: -------------------------------------------------------------------------------- 1 | # ReScript Project Template 2 | 3 | - [Installation](../../README.md) 4 | 5 | Official ReScript starter template. 6 | 7 | ## Installation 8 | 9 | ```sh 10 | npm install 11 | ``` 12 | 13 | ## Build 14 | 15 | - Build: `npm run res:build` 16 | - Clean: `npm run res:clean` 17 | - Build & watch: `npm run res:dev` 18 | 19 | ## Run 20 | 21 | ```sh 22 | node src/Demo.res.js 23 | ``` 24 | -------------------------------------------------------------------------------- /scenarios/helpers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helpers", 3 | "version": "0.0.0", 4 | "namespace": "Helpers", 5 | "scripts": { 6 | "build": "rescript", 7 | "clean": "rescript clean", 8 | "watch": "rescript -w" 9 | }, 10 | "keywords": [ 11 | "rescript" 12 | ], 13 | "author": "", 14 | "license": "MIT", 15 | "dependencies": { 16 | "rescript": "11.1.3", 17 | "rescript-schema": "9.3.0", 18 | "envio": "file:../../codegenerator/cli/npm/envio" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /scenarios/helpers/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: false 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | rescript: 9 | specifier: 11.1.0 10 | version: 11.1.0 11 | rescript-schema: 12 | specifier: 6.4.0 13 | version: 6.4.0(rescript@11.1.0) 14 | 15 | packages: 16 | 17 | /rescript-schema@6.4.0(rescript@11.1.0): 18 | resolution: {integrity: sha512-rBHtZOEVskKKAf7jfJicuTA1xsQRu2qBdB73FVtPsUSmtgIYpGGKk4YSURQss7WYQEHRds+zPjFLJWzeAnwsLg==} 19 | peerDependencies: 20 | rescript: 11.x 21 | dependencies: 22 | rescript: 11.1.0 23 | dev: false 24 | 25 | /rescript@11.1.0: 26 | resolution: {integrity: sha512-9la2Dv+ACylQ77I8s4spPu1JnLZXbH5WgxcLHLLUBWgFFSiv0wXqgzWztrBIZqwFgVX5BYcwldUqUVcEzdCyHg==} 27 | engines: {node: '>=10'} 28 | hasBin: true 29 | requiresBuild: true 30 | dev: false 31 | -------------------------------------------------------------------------------- /scenarios/helpers/rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helpers", 3 | "namespace": "Helpers", 4 | "sources": { 5 | "dir": "src", 6 | "subdirs": true 7 | }, 8 | "package-specs": { 9 | "module": "commonjs", 10 | "in-source": true 11 | }, 12 | "suffix": ".res.js", 13 | "bs-dependencies": ["rescript-schema", "envio"], 14 | "bsc-flags": ["-open Belt"] 15 | } 16 | -------------------------------------------------------------------------------- /scenarios/test_codegen/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/* 22 | *.mlast 23 | *.mliast 24 | .vscode 25 | .merlin 26 | .bsb.lock 27 | /node_modules/ 28 | benchmarks/ 29 | cache 30 | build 31 | *.bs.js 32 | generated 33 | logs 34 | build 35 | .nyc_output 36 | -------------------------------------------------------------------------------- /scenarios/test_codegen/.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | // Specify "require" for CommonJS 3 | "require": "ts-node/register", 4 | "extensions": ["ts", "tsx"], 5 | "spec": ["test/**/*.ts", "test/**/*.bs.js"], 6 | "timeout": 30000, //Mostly integration tests so just precaution to stop ci from breaking 7 | "exit": true 8 | } 9 | -------------------------------------------------------------------------------- /scenarios/test_codegen/.npmrc: -------------------------------------------------------------------------------- 1 | strict-peer-dependencies=false 2 | auto-install-peers=true 3 | # Needed for ts build folder to have 4 | # access to rescript node_modules 5 | shamefully-hoist=true -------------------------------------------------------------------------------- /scenarios/test_codegen/README.md: -------------------------------------------------------------------------------- 1 | ## Testing 2 | 3 | ### Run full tests 4 | 5 | - install packages, run codegen and bring up docker: 6 | - `pnpm i` 7 | 8 | - `pnpm codegen` 9 | 10 | - `pnpm docker-up` 11 | 12 | - Make sure to gen ts types in contracts dir `(cd contracts && npx hardhat compile)` 13 | 14 | - Then run the tests and confirm all pass: 15 | - `pnpm test` 16 | 17 | 18 | ### Clean up 19 | 20 | run `pnpm docker-down` -------------------------------------------------------------------------------- /scenarios/test_codegen/contracts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | 7 | #Hardhat files 8 | cache 9 | artifacts/@openzeppelin 10 | artifacts/build-info 11 | artifacts/contracts/**/*.dbg.json 12 | 13 | deployments/localhost 14 | 15 | typechain-types 16 | -------------------------------------------------------------------------------- /scenarios/test_codegen/contracts/README.md: -------------------------------------------------------------------------------- 1 | # Deploy basic gravatar contract 2 | 3 | deploy to local hardhat network 4 | 5 | ``` 6 | pnpm i 7 | pnpm hardhat-node 8 | 9 | ``` 10 | 11 | # Tasks 12 | 13 | Create a new gravatar for a specific user in the accounts list 14 | 15 | ``` 16 | pnpm task:new-gravatar --name --image --user-index 17 | ``` 18 | -------------------------------------------------------------------------------- /scenarios/test_codegen/contracts/contracts/NftFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | import "./SimpleNft.sol"; 3 | 4 | contract NftFactory { 5 | event SimpleNftCreated( 6 | string name, 7 | string symbol, 8 | uint256 maxSupply, 9 | address contractAddress 10 | ); 11 | 12 | constructor() {} 13 | 14 | function createNft( 15 | string memory name, 16 | string memory symbol, 17 | uint256 maxSupply 18 | ) external { 19 | SimpleNft simpleNft = new SimpleNft(name, symbol, maxSupply); 20 | emit SimpleNftCreated(name, symbol, maxSupply, address(simpleNft)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /scenarios/test_codegen/contracts/contracts/SimpleNft.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 4 | import "@openzeppelin/contracts/utils/Counters.sol"; 5 | 6 | contract SimpleNft is ERC721 { 7 | using Counters for Counters.Counter; 8 | 9 | Counters.Counter private _supply; 10 | uint256 public maxSupply; 11 | 12 | constructor( 13 | string memory name, 14 | string memory symbol, 15 | uint256 _maxSupply 16 | ) ERC721(name, symbol) { 17 | _supply.increment(); 18 | maxSupply = _maxSupply; 19 | } 20 | 21 | function mint(uint256 _quantity) public { 22 | require( 23 | (_quantity + _supply.current()) <= maxSupply, 24 | "Max supply exceeded" 25 | ); 26 | _mintLoop(msg.sender, _quantity); 27 | } 28 | 29 | function _mintLoop(address _to, uint256 _quantity) internal { 30 | for (uint256 i = 0; i < _quantity; ++i) { 31 | _safeMint(_to, _supply.current()); 32 | _supply.increment(); 33 | } 34 | } 35 | 36 | function totalSupply() public view returns (uint256) { 37 | return _supply.current() - 1; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /scenarios/test_codegen/contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hardhat", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "@FloatCapital", 6 | "license": "MIT", 7 | "scripts": { 8 | "compile": "hardhat compile", 9 | "deploy-gravatar": "hardhat deploy", 10 | "test": "npx hardhat test", 11 | "hardhat": "hardhat", 12 | "task:new-gravatar": "npx hardhat new-gravatar", 13 | "task:update-gravatar-name": "npx hardhat update-gravatar-name", 14 | "task:update-gravatar-image": "npx hardhat update-gravatar-image", 15 | "hardhat-node": "npx hardhat node" 16 | }, 17 | "dependencies": { 18 | "@nomiclabs/hardhat-ethers": "^3.0.0-beta.0", 19 | "@openzeppelin/contracts": "^4.5.0", 20 | "ethers": "^6.3.0", 21 | "hardhat": "^2.14.0", 22 | "hardhat-abi-exporter": "^2.10.1", 23 | "hardhat-deploy": "^0.11.26" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /scenarios/test_codegen/contracts/secretsManager.example.ts: -------------------------------------------------------------------------------- 1 | export const mnemonic = ""; 2 | 3 | -------------------------------------------------------------------------------- /scenarios/test_codegen/contracts/tasks/index.js: -------------------------------------------------------------------------------- 1 | require("./newGravatarTask"); 2 | require("./updateGravatarNameTask"); 3 | require("./updateGravatarImageTask"); 4 | -------------------------------------------------------------------------------- /scenarios/test_codegen/contracts/tasks/newGravatarTask.js: -------------------------------------------------------------------------------- 1 | task("new-gravatar", "Create new gravatar") 2 | .addParam("name", "gravatar display name", undefined, types.string) 3 | .addParam("image", "gravatar image url", undefined, types.string) 4 | .addParam( 5 | "userIndex", // this is --user-index whe running via command line 6 | "user to create new gravatar from accounts", 7 | undefined, 8 | types.int 9 | ) 10 | .setAction(async ({ name, image, userIndex }) => { 11 | const accounts = await ethers.getSigners(); 12 | const user = accounts[userIndex % accounts.length]; 13 | if (userIndex >= accounts.length) { 14 | console.warn(`There are only ${accounts.length} accounts in the network, you are actually using account index ${userIndex % accounts.length}`); 15 | } 16 | 17 | console.log(`Using account ${user.address} to create new gravatar.`); 18 | 19 | const Gravatar = await deployments.get("GravatarRegistry"); 20 | 21 | const gravatar = await ethers.getContractAt( 22 | "GravatarRegistry", 23 | Gravatar.address 24 | ); 25 | 26 | const usersCurrentGravatarId = await gravatar.ownerToGravatar(user.address) 27 | const alreadyHasAGravatar = (usersCurrentGravatarId != 0); 28 | if (alreadyHasAGravatar) { 29 | console.log(`User's already has a gravatar, you can update it, your gravatar has id ${usersCurrentGravatarId}.`); 30 | return; 31 | } 32 | 33 | const newGravatar1Tx = await gravatar 34 | .connect(user) 35 | .createGravatar(name, image); 36 | 37 | await newGravatar1Tx.wait(); 38 | 39 | let gravatarCheck = await gravatar.getGravatar(user.address); 40 | console.log("gravatar created", gravatarCheck); 41 | }); 42 | -------------------------------------------------------------------------------- /scenarios/test_codegen/contracts/tasks/updateGravatarImageTask.js: -------------------------------------------------------------------------------- 1 | task("update-gravatar-image", "Update existing gravatar image") 2 | .addParam("image", "gravatar image url", undefined, types.string) 3 | .addParam( 4 | "userIndex", // this is --user-index whe running via command line 5 | "user updating existing gravatar from accounts", 6 | undefined, 7 | types.int 8 | ) 9 | .setAction(async ({ image, userIndex }) => { 10 | const accounts = await ethers.getSigners(); 11 | const user = accounts[userIndex % accounts.length]; 12 | if (userIndex >= accounts.length) { 13 | console.warn(`There are only ${accounts.length} accounts in the network, you are actually using account index ${userIndex % accounts.length}`); 14 | } 15 | 16 | console.log(`Using account ${user.address} to update the image of your gravatar.`); 17 | 18 | const Gravatar = await deployments.get("GravatarRegistry"); 19 | 20 | const gravatar = await ethers.getContractAt( 21 | "GravatarRegistry", 22 | Gravatar.address 23 | ); 24 | 25 | const usersCurrentGravatarId = await gravatar.ownerToGravatar(user.address) 26 | const userDoesntHaveGravatar = (usersCurrentGravatarId == 0); 27 | if (userDoesntHaveGravatar) { 28 | console.log(`You need to create a gravatar firt before you can update it.`); 29 | return; 30 | } 31 | const updateGravatarImageTx = await gravatar 32 | .connect(user) 33 | .updateGravatarImage(image); 34 | await updateGravatarImageTx.wait(); 35 | 36 | let gravatarCheck = await gravatar.getGravatar(user.address); 37 | console.log("Gravatar image updated", gravatarCheck); 38 | }); 39 | -------------------------------------------------------------------------------- /scenarios/test_codegen/contracts/tasks/updateGravatarNameTask.js: -------------------------------------------------------------------------------- 1 | task("update-gravatar-name", "Update existing gravatar name") 2 | .addParam("name", "gravatar display name", undefined, types.string) 3 | .addParam( 4 | "userIndex", // this is --user-index whe running via command line 5 | "user updating existing gravatar from accounts", 6 | undefined, 7 | types.int 8 | ) 9 | .setAction(async ({ name, userIndex }) => { 10 | const accounts = await ethers.getSigners(); 11 | const user = accounts[userIndex % accounts.length]; 12 | if (userIndex >= accounts.length) { 13 | console.warn(`There are only ${accounts.length} accounts in the network, you are actually using account index ${userIndex % accounts.length}`); 14 | } 15 | 16 | console.log(`Using account ${user.address} to update the name of your gravatar.`); 17 | 18 | const Gravatar = await deployments.get("GravatarRegistry"); 19 | 20 | const gravatar = await ethers.getContractAt( 21 | "GravatarRegistry", 22 | Gravatar.address 23 | ); 24 | 25 | const usersCurrentGravatarId = await gravatar.ownerToGravatar(user.address) 26 | const userDoesntHaveGravatar = (usersCurrentGravatarId == 0); 27 | if (userDoesntHaveGravatar) { 28 | console.log(`You need to create a gravatar firt before you can update it.`); 29 | return; 30 | } 31 | const updateGravatarNameTx = await gravatar 32 | .connect(user) 33 | .updateGravatarName(name); 34 | await updateGravatarNameTx.wait(); 35 | 36 | let gravatarCheck = await gravatar.getGravatar(user.address); 37 | console.log("Gravatar name updated", gravatarCheck); 38 | }); 39 | -------------------------------------------------------------------------------- /scenarios/test_codegen/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-ethers"; 2 | import "hardhat-deploy"; 3 | import { HardhatUserConfig } from "hardhat/config"; 4 | 5 | import "hardhat-abi-exporter"; 6 | 7 | import "@typechain/hardhat"; 8 | import "./contracts/tasks"; 9 | import { TypechainUserConfig } from "@typechain/hardhat/dist/types"; 10 | 11 | let secertsConfig; 12 | try { 13 | secertsConfig = require("./contracts/secretsManager.ts"); 14 | } catch (e) { 15 | console.error( 16 | "You are using the example secrets manager, please copy this file if you want to use it" 17 | ); 18 | secertsConfig = require("./contracts/secretsManager.example.ts"); 19 | } 20 | 21 | const { mnemonic } = secertsConfig; 22 | let typeChainConfig: TypechainUserConfig = { 23 | target: "ethers-v6", 24 | }; 25 | 26 | const config: HardhatUserConfig = { 27 | solidity: "0.8.19", 28 | paths: { 29 | root: "contracts", 30 | }, 31 | typechain: typeChainConfig, 32 | abiExporter: { 33 | path: "./abis", 34 | clear: true, 35 | flat: true, 36 | spacing: 2, 37 | }, 38 | networks: { 39 | hardhat: { 40 | chainId: 1337, 41 | accounts: { 42 | mnemonic: "test test test test test test test test test test test test", 43 | }, 44 | }, 45 | }, 46 | }; 47 | 48 | export default config; 49 | -------------------------------------------------------------------------------- /scenarios/test_codegen/pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - ./* 3 | - ./generated/* 4 | - ../helpers 5 | -------------------------------------------------------------------------------- /scenarios/test_codegen/rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gravatar", 3 | "version": "0.1.0", 4 | "sources": [ 5 | { 6 | "dir": "src", 7 | "subdirs": true 8 | }, 9 | { 10 | "dir": "test", 11 | "subdirs": true 12 | } 13 | ], 14 | "suffix": ".bs.js", 15 | "package-specs": { 16 | "module": "commonjs", 17 | "in-source": true 18 | }, 19 | "gentypeconfig": { 20 | "language": "typescript", 21 | "generatedFileExtension": ".gen.ts", 22 | "module": "commonjs", 23 | "debug": { 24 | "all": false, 25 | "basic": false 26 | } 27 | }, 28 | "jsx": { 29 | "version": 4 30 | }, 31 | "bs-dependencies": [ 32 | "@rescript/react", 33 | "generated", 34 | "rescript-nodejs", 35 | "rescript-schema", 36 | "helpers", 37 | "envio" 38 | ], 39 | "bsc-flags": ["-open RescriptSchema"] 40 | } 41 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/Config_test.res: -------------------------------------------------------------------------------- 1 | open RescriptMocha 2 | 3 | describe("getGeneratedByChainId Test", () => { 4 | it("getGeneratedByChainId should return the correct config", () => { 5 | let configYaml = ConfigYAML.getGeneratedByChainId(1) 6 | Assert.deepEqual( 7 | configYaml, 8 | { 9 | syncSource: HyperSync({endpointUrl: "https://1.hypersync.xyz"}), 10 | startBlock: 1, 11 | confirmedBlockThreshold: 200, 12 | contracts: Js.Dict.fromArray([ 13 | ( 14 | "Noop", 15 | { 16 | ConfigYAML.name: "Noop", 17 | abi: %raw(`[{ 18 | anonymous: false, 19 | inputs: [], 20 | name: "EmptyEvent", 21 | type: "event" 22 | }]`), 23 | addresses: ["0x0B2f78c5BF6D9C12Ee1225D5F374aa91204580c3"], 24 | events: ["EmptyEvent"], 25 | }, 26 | ), 27 | ]), 28 | }, 29 | ) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/Encoders_schema_test.res: -------------------------------------------------------------------------------- 1 | open RescriptMocha 2 | 3 | type testRecord = {optNumber: option} 4 | let testRecordSchema = S.object(s => { 5 | optNumber: s.field("optNumber", S.nullable(S.int)), 6 | }) 7 | 8 | describe("nullable encodes and decodes successfully", () => { 9 | let mock1 = { 10 | optNumber: Some(1), 11 | } 12 | let mock2 = { 13 | optNumber: None, 14 | } 15 | 16 | let mock1raw = `{"optNumber":1}` 17 | let mock2raw = `{"optNumber":null}` 18 | let mock3raw = `{}` 19 | it("flat encodes some type", () => { 20 | Assert.deepEqual(mock1->S.reverseConvertToJsonStringOrThrow(testRecordSchema), mock1raw) 21 | }) 22 | it("encodes None as null", () => { 23 | // TODO: Test if it's a problem not to send null 24 | Assert.deepEqual(mock2->S.reverseConvertToJsonStringOrThrow(testRecordSchema), mock3raw) 25 | }) 26 | it("decodes null as None", () => { 27 | Assert.deepEqual(mock2raw->S.parseJsonStringOrThrow(testRecordSchema), mock2) 28 | }) 29 | it("decodes undefined as None", () => { 30 | Assert.deepEqual(mock3raw->S.parseJsonStringOrThrow(testRecordSchema), mock2) 31 | }) 32 | it("decodes val as Some", () => { 33 | Assert.deepEqual(mock1raw->S.parseJsonStringOrThrow(testRecordSchema), mock1) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/Integration_ts_helpers.gen.ts: -------------------------------------------------------------------------------- 1 | /* TypeScript file generated from Integration_ts_helpers.res by genType. */ 2 | 3 | /* eslint-disable */ 4 | /* tslint:disable */ 5 | 6 | const Integration_ts_helpersJS = require('./Integration_ts_helpers.bs.js'); 7 | 8 | import type {t as Address_t} from 'envio/src/Address.gen'; 9 | 10 | export abstract class chainConfig { protected opaque!: any }; /* simulate opaque types */ 11 | 12 | export abstract class chainManager { protected opaque!: any }; /* simulate opaque types */ 13 | 14 | export const getLocalChainConfig: (nftFactoryContractAddress:Address_t) => chainConfig = Integration_ts_helpersJS.getLocalChainConfig as any; 15 | 16 | export const makeChainManager: (cfg:chainConfig) => chainManager = Integration_ts_helpersJS.makeChainManager as any; 17 | 18 | export const startProcessing: (config:unknown, cfg:chainConfig, chainManager:chainManager) => void = Integration_ts_helpersJS.startProcessing as any; 19 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/__mocks__/DbStub.res: -------------------------------------------------------------------------------- 1 | let deleteDictKey = (_dict: dict<'a>, _key: string) => %raw(`delete _dict[_key]`) 2 | 3 | let databaseDict: dict = Js.Dict.empty() 4 | 5 | let getGravatarDb = (~id: string) => { 6 | Js.Dict.get(databaseDict, id) 7 | } 8 | 9 | let setGravatarDb = (~gravatar: Entities.Gravatar.t) => { 10 | Js.Dict.set(databaseDict, gravatar.id, gravatar) 11 | } 12 | 13 | let batchSetGravatar = (batch: array) => { 14 | batch 15 | ->Belt.Array.forEach(entity => { 16 | setGravatarDb(~gravatar=entity) 17 | }) 18 | ->Promise.resolve 19 | } 20 | 21 | let batchDeleteGravatar = (batch: array) => { 22 | batch 23 | ->Belt.Array.forEach(entity => { 24 | deleteDictKey(databaseDict, entity.id) 25 | }) 26 | ->Promise.resolve 27 | } 28 | 29 | let readGravatarEntities = (entityReads: array): promise> => { 30 | entityReads 31 | ->Belt.Array.keepMap(id => { 32 | getGravatarDb(~id)->Belt.Option.map(gravatar => gravatar) 33 | }) 34 | ->Promise.resolve 35 | } 36 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/__mocks__/MockEntities.res: -------------------------------------------------------------------------------- 1 | let gravatarEntity1: Entities.Gravatar.t = { 2 | id: "1001", 3 | owner_id: "0x123", 4 | displayName: "gravatar1", 5 | imageUrl: "https://gravatar1.com", 6 | updatesCount: BigInt.fromInt(0), 7 | size: LARGE, 8 | } 9 | 10 | let gravatarEntity2: Entities.Gravatar.t = { 11 | id: "1002", 12 | owner_id: "0x678", 13 | displayName: "gravatar2", 14 | imageUrl: "https://gravatar2.com", 15 | updatesCount: BigInt.fromInt(1), 16 | size: MEDIUM, 17 | } 18 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/helpers/DbHelpers.res: -------------------------------------------------------------------------------- 1 | let _sqlConfig = Db.config 2 | 3 | @@warning("-21") 4 | let resetPostgresClient: unit => unit = () => { 5 | // This is a hack to reset the postgres client between tests. postgres.js seems to cache some types, and if tests clear the DB you need to also reset sql. 6 | %raw("require('../../generated/src/db/Db.bs.js').sql = require('postgres')(_sqlConfig)") 7 | } 8 | 9 | let runUpDownMigration = async () => { 10 | resetPostgresClient() 11 | (await Migrations.runUpMigrations(~shouldExit=false, ~reset=true))->ignore 12 | } 13 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/helpers/LiveGravatarTask.res: -------------------------------------------------------------------------------- 1 | @module("./taskLiveGravatarTxs.js") 2 | external liveGravatarTxs: SetupRpcNode.gravatarContract => Promise.t = "default" 3 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/helpers/SetupRpcNode.res: -------------------------------------------------------------------------------- 1 | type gravatarContract 2 | type deployment = {gravatar: gravatarContract} 3 | 4 | @module("./setupNodeAndContracts.js") 5 | external runBasicGravatarTransactions: gravatarContract => Promise.t = "default" 6 | 7 | @module("./setupNodeAndContracts.js") 8 | external deployContracts: unit => Promise.t = "deployContracts" 9 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/helpers/node-and-contracts.ts: -------------------------------------------------------------------------------- 1 | import hre from "hardhat"; 2 | import { 3 | NftFactory, 4 | NftFactory__factory, 5 | SimpleNft__factory, 6 | } from "../../contracts/typechain-types"; 7 | import { ContractTransactionResponse } from "ethers"; 8 | 9 | export enum Users { 10 | Deployer, 11 | User1, 12 | User2, 13 | User3, 14 | } 15 | 16 | type NftArgs = { 17 | name: string; 18 | symbol: string; 19 | supply: number; 20 | }; 21 | 22 | export const createNftFromFactory = async ( 23 | nftFactory: NftFactory & { 24 | deploymentTransaction(): ContractTransactionResponse; 25 | }, 26 | nftArgs: NftArgs 27 | ) => { 28 | let { name, symbol, supply } = nftArgs; 29 | 30 | return await nftFactory.createNft(name, symbol, supply); 31 | }; 32 | 33 | export const mintSimpleNft = async ( 34 | userIndex: Users, 35 | simpleNftContractAddress: string, 36 | quantity: number 37 | ) => { 38 | const ethers = hre.ethers; 39 | const accounts = await ethers.getSigners(); 40 | const user = accounts[userIndex]; 41 | 42 | console.log(`Using account ${user.address} to mint NFT.`); 43 | 44 | const contractAddress = ethers.getAddress(simpleNftContractAddress); 45 | const simpleNftContract = SimpleNft__factory.connect(contractAddress, user); 46 | const mintNftTx = await simpleNftContract.mint(quantity); 47 | 48 | await mintNftTx.wait(); 49 | return mintNftTx; 50 | }; 51 | 52 | export const getSimpleNftTotalSupply = async ( 53 | simpleNftContractAddress: string 54 | ) => { 55 | const contractAddress = hre.ethers.getAddress(simpleNftContractAddress); 56 | const simpleNftContract = SimpleNft__factory.connect(contractAddress); 57 | 58 | return await simpleNftContract.totalSupply(); 59 | }; 60 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/helpers/taskLiveGravatarTxs.js: -------------------------------------------------------------------------------- 1 | const hre = require("hardhat"); 2 | 3 | const liveGravatarTxs = async (gravatar) => { 4 | await hre.run("compile"); 5 | let accounts = await hre.ethers.getSigners(); 6 | 7 | console.log("live gravatar task"); 8 | 9 | console.log(gravatar); 10 | 11 | const user2 = accounts[2]; 12 | 13 | const newGravatarTx = await gravatar 14 | .connect(user2) 15 | .createGravatar("Live Subscribing Gravatar", "https://welldoitlive.com"); 16 | 17 | const updateGravatarNameTx = await gravatar 18 | .connect(user2) 19 | .updateGravatarName("First Update Gravatar"); 20 | 21 | // Wait for all transactions to complete. 22 | await newGravatarTx.wait(); 23 | await updateGravatarNameTx.wait(); 24 | }; 25 | 26 | module.exports.default = liveGravatarTxs 27 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/helpers/utils.ts: -------------------------------------------------------------------------------- 1 | import { runUpMigrations } from "../../generated/src/db/Migrations.bs"; 2 | import Postgres from "postgres"; 3 | import { config } from "../../generated/src/db/Db.bs"; 4 | 5 | export const createSql = () => Postgres(config); 6 | 7 | const originalConsoleLog = console.log; 8 | 9 | export const disableConsoleLog = () => { 10 | console.log = () => undefined; 11 | }; 12 | 13 | export const enableConsoleLog = () => { 14 | console.log = originalConsoleLog; 15 | }; 16 | 17 | export const runMigrationsNoExit = async () => { 18 | await runUpMigrations(false, true); 19 | }; 20 | 21 | export const runFunctionNoLogs = async (func: () => any) => { 22 | disableConsoleLog(); 23 | await func(); 24 | enableConsoleLog(); 25 | }; 26 | 27 | export const runMigrationsNoLogs = () => runFunctionNoLogs(runMigrationsNoExit); 28 | 29 | export enum EventVariants { 30 | NftFactoryContract_SimpleNftCreatedEvent, 31 | SimpleNftContract_TransferEvent, 32 | } 33 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/lib_tests/SingleOrMultiple_test.res: -------------------------------------------------------------------------------- 1 | open RescriptMocha 2 | module SingleOrMultiple = Types.SingleOrMultiple 3 | type tupleWithArrays = (array, array) 4 | 5 | describe("Single or Multiple", () => { 6 | it("Single nested", () => { 7 | let tupleWithArrays: tupleWithArrays = ([1n, 2n], ["test", "test2"]) 8 | let single: SingleOrMultiple.t = SingleOrMultiple.single(tupleWithArrays) 9 | let multiple: SingleOrMultiple.t = SingleOrMultiple.multiple([tupleWithArrays]) 10 | 11 | let expectedNormalized = [tupleWithArrays] 12 | 13 | let normalizedSingle = SingleOrMultiple.normalizeOrThrow(single, ~nestedArrayDepth=2) 14 | let normalizedMultiple = SingleOrMultiple.normalizeOrThrow(multiple, ~nestedArrayDepth=2) 15 | 16 | Assert.deepEqual( 17 | multiple->Utils.magic, 18 | expectedNormalized, 19 | ~message="Multiple should be the same as normalized", 20 | ) 21 | Assert.deepEqual(normalizedSingle, expectedNormalized, ~message="Single should be normalized") 22 | Assert.deepEqual( 23 | normalizedMultiple, 24 | expectedNormalized, 25 | ~message="Multiple should be normalized", 26 | ) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/manual/README.md: -------------------------------------------------------------------------------- 1 | # Manual tests 2 | 3 | Some tests are best run manually - such as checking that logs still look good after an update. 4 | 5 | ## Running the tests 6 | 7 | Run the following to test the output: 8 | 9 | ```bash 10 | # default setup (same as `console-pretty`): 11 | node test/manual/LogTesting.bs.js # LOG_STRATEGY=console-pretty 12 | # ecs-file 13 | LOG_STRATEGY="ecs-file" node test/manual/LogTesting.bs.js 14 | # ecs-console 15 | LOG_STRATEGY="ecs-console" node test/manual/LogTesting.bs.js 16 | # file-only 17 | LOG_STRATEGY="file-only" node test/manual/LogTesting.bs.js 18 | # console-raw 19 | LOG_STRATEGY="console-raw" node test/manual/LogTesting.bs.js 20 | # both-prettyconsole 21 | LOG_STRATEGY="both-prettyconsole" node test/manual/LogTesting.bs.js 22 | ``` 23 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/rollback/ChainDataHelpers.res: -------------------------------------------------------------------------------- 1 | open Belt 2 | let makeBlock = (~blockNumber, ~blockTimestamp, ~blockHash) => 3 | { 4 | number: blockNumber, 5 | hash: blockHash, 6 | timestamp: blockTimestamp, 7 | }->(Utils.magic: Types.Block.t => Internal.eventBlock) 8 | 9 | let makeTransaction = (~transactionIndex, ~transactionHash) => 10 | { 11 | transactionIndex, 12 | hash: transactionHash, 13 | }->(Utils.magic: Types.Transaction.t => Internal.eventTransaction) 14 | 15 | module Gravatar = { 16 | let contractName = "Gravatar" 17 | let chainConfig = 18 | RegisterHandlers.registerAllHandlers().chainMap->ChainMap.get(MockConfig.chain1337) 19 | let contract = chainConfig.contracts->Js.Array2.find(c => c.name == contractName)->Option.getExn 20 | let defaultAddress = contract.addresses[0]->Option.getExn 21 | 22 | let makeEventConstructorWithDefaultSrcAddress = 23 | MockChainData.makeEventConstructor( 24 | ~srcAddress=defaultAddress, 25 | ~makeBlock, 26 | ~makeTransaction, 27 | ... 28 | ) 29 | 30 | module NewGravatar = { 31 | let mkEventConstr = params => 32 | makeEventConstructorWithDefaultSrcAddress( 33 | ~eventConfig=Types.Gravatar.NewGravatar.register(), 34 | ~params=params->(Utils.magic: Types.Gravatar.NewGravatar.eventArgs => Internal.eventParams), 35 | ... 36 | ) 37 | } 38 | 39 | module UpdatedGravatar = { 40 | let mkEventConstr = params => 41 | makeEventConstructorWithDefaultSrcAddress( 42 | ~eventConfig=Types.Gravatar.UpdatedGravatar.register(), 43 | ~params=params->( 44 | Utils.magic: Types.Gravatar.UpdatedGravatar.eventArgs => Internal.eventParams 45 | ), 46 | ... 47 | ) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /scenarios/test_codegen/test/rollback/MockChainData.res: -------------------------------------------------------------------------------- 1 | module Indexer = { 2 | module ErrorHandling = ErrorHandling 3 | module Types = Types 4 | module Config = Config 5 | module Source = Source 6 | module FetchState = FetchState 7 | } 8 | 9 | include Helpers.ChainMocking.Make(Indexer) 10 | -------------------------------------------------------------------------------- /scenarios/test_codegen/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "lib": ["es6"], 6 | "allowJs": true, 7 | "checkJs": false, 8 | "outDir": "build", 9 | "rootDirs": ["src", "generated", "test", "contracts/typechain-types"], 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true 15 | }, 16 | "include": ["src", "generated", "test"], 17 | "exclude": ["node_modules", "generated/node_modules"], 18 | "files": ["./hardhat.config.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /sync.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enviodev/hyperindex/ee5707baaf7ae747023a958f417c57632c80694a/sync.gif --------------------------------------------------------------------------------