Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/cli/CommandLineHelp.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ Initialize Evm indexer from an example template

* `-t`, `--template <TEMPLATE>` — Name of the template to be used in initialization

Possible values: `greeter`, `erc20`, `feature-factory`
Possible values: `greeter`, `erc20`, `feature-external-calls`, `feature-factory`



Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/cli_args/init_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub mod evm {
pub enum Template {
Greeter,
Erc20,
#[strum(serialize = "Feature: External Calls")]
FeatureExternalCalls,
#[strum(serialize = "Feature: Factory Contract")]
FeatureFactory,
}
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/cli_args/interactive_init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ enum EvmInitOption {
TemplateErc20,
#[strum(serialize = "Template: Greeter")]
TemplateGreeter,
#[strum(serialize = "Feature: External Calls")]
FeatureExternalCalls,
#[strum(serialize = "Feature: Factory Contract")]
FeatureFactory,
}
Expand All @@ -54,6 +56,7 @@ impl EvmInitOption {
Self::ContractImportLocal,
Self::TemplateErc20,
Self::TemplateGreeter,
Self::FeatureExternalCalls,
Self::FeatureFactory,
],
}
Expand Down Expand Up @@ -230,6 +233,9 @@ async fn prompt_evm_init_option(language: &Language) -> Result<Ecosystem> {
}
EvmInitOption::TemplateErc20 => evm::InitFlow::Template(evm::Template::Erc20),
EvmInitOption::TemplateGreeter => evm::InitFlow::Template(evm::Template::Greeter),
EvmInitOption::FeatureExternalCalls => {
evm::InitFlow::Template(evm::Template::FeatureExternalCalls)
}
EvmInitOption::FeatureFactory => evm::InitFlow::Template(evm::Template::FeatureFactory),
};

Expand Down
9 changes: 9 additions & 0 deletions packages/cli/src/executor/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,21 @@ pub async fn run_init_args(init_args: InitArgs, project_paths: &ProjectPaths) ->

let envio_version = get_envio_version()?;

let extra_dependencies = match &init_config.ecosystem {
Ecosystem::Evm {
init_flow:
init_config::evm::InitFlow::Template(init_config::evm::Template::FeatureExternalCalls),
} => vec![("viem".to_string(), "^2.0.0".to_string())],
_ => vec![],
};

let hbs_template = InitTemplates::new(
init_config.name.clone(),
&init_config.language,
&parsed_project_paths,
envio_version.clone(),
init_config.api_token,
extra_dependencies,
)
.context("Failed creating init templates")?;

Expand Down
5 changes: 5 additions & 0 deletions packages/cli/src/hbs_templating/init_templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct InitTemplates {
//Used for the package.json reference to generated in handlers
relative_path_from_root_to_generated: String,
envio_api_token: Option<String>,
extra_dependencies: Vec<(String, String)>,
}

impl InitTemplates {
Expand All @@ -25,6 +26,7 @@ impl InitTemplates {
project_paths: &ParsedProjectPaths,
envio_version: String,
envio_api_token: Option<String>,
extra_dependencies: Vec<(String, String)>,
) -> anyhow::Result<Self> {
//Take the absolute paths of project root and generated, diff them to get
//relative path from root to generated and add a leading dot. So in a default project, if your
Expand All @@ -50,6 +52,7 @@ impl InitTemplates {
envio_version,
relative_path_from_root_to_generated,
envio_api_token,
extra_dependencies,
};

Ok(template)
Expand All @@ -69,6 +72,7 @@ mod test {
&ParsedProjectPaths::default(),
"latest".to_string(),
None,
vec![],
)
.unwrap();

Expand All @@ -79,6 +83,7 @@ mod test {
envio_version: "latest".to_string(),
relative_path_from_root_to_generated: "./generated".to_string(),
envio_api_token: None,
extra_dependencies: vec![],
};

assert_eq!(expected, init_temp);
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/template_dirs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ impl Template for evm::Template {
match self {
evm::Template::Greeter => "greeter",
evm::Template::Erc20 => "erc20",
evm::Template::FeatureExternalCalls => "external_calls",
evm::Template::FeatureFactory => "factory",
}
.to_string()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
"vitest": "4.0.18"
},
"dependencies": {
"envio": "{{envio_version}}"
"envio": "{{envio_version}}"{{#if extra_dependencies}},
{{#each extra_dependencies}}"{{this.[0]}}": "{{this.[1]}}"{{#unless @last}},
{{/unless}}{{/each}}{{/if}}
},
"optionalDependencies": {
"generated": "{{relative_path_from_root_to_generated}}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# To create or update a token visit https://envio.dev/app/api-tokens
ENVIO_API_TOKEN=<YOUR-API-TOKEN>
ENVIO_ARBITRUM_RPC=<YOUR-ARBITRUM-RPC-URL>
ENVIO_ETHEREUM_MAINNET_RPC=<YOUR-ETHEREUM-MAINNET-RPC-URL>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
*.exe
*.obj
*.out
*.compile
*.native
*.byte
*.cmo
*.annot
*.cmi
*.cmx
*.cmt
*.cmti
*.cma
*.a
*.cmxa
*.obj
*~
*.annot
*.cmj
*.bak
lib/*
*.mlast
*.mliast
.vscode
.merlin
.bsb.lock
node_modules/
build/
benchmarks/
artifacts
cache
*.res.js
*.res.mjs
generated
.env
.pnpm-store
107 changes: 107 additions & 0 deletions packages/cli/templates/static/external_calls_template/shared/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# External Calls

_Please refer to the [documentation website](https://docs.envio.dev) for a thorough guide on all [Envio](https://envio.dev) indexer features_

With HyperIndex, the Effect API allows you to perform external calls from your handlers.
These calls are executed within the handler context and support optional caching and rate limiting.

You can learn more about the Effect API in the [documentation](https://docs.envio.dev/docs/HyperIndex/effect-api).

The following example extends the **factory pattern** example to fetch the **decimal of a token** via an RPC call, indexing Uniswap V3 `PoolCreated` events across Ethereum Mainnet and Arbitrum.

## Create Effect

To create an effect in your handler, use the `createEffect` function from the `envio` package.
This function takes **two arguments**: effect options and a handler function.

Effect options:

- `name`: used for debugging and logging
- `input`: the input type of the effect
- `output`: the output type of the effect
- `cache`: whether to cache the effect result in the database
- `rateLimit`: limits the execution frequency of this effect, read more in the [documentation](https://docs.envio.dev/docs/HyperIndex/effect-api#rate-limit)

## Using Effect

To use the effect, use `context.effect` from your handlers, loaders, or other effects:

```ts
CONTRACT.EVENT.handler(async ({ event, context }) => {
const effectOutput = await context.effect(YOUR_EFFECT, EFFECT_INPUTS);
});
```

## `fetchTokenDetails` Effect

The following effect fetches the **decimal of a token** using an RPC call:

```ts
const fetchTokenDetails = createEffect(
{
name: "fetchTokenDetails", // Name used internally for the effect
input: {
token: S.string, // Input: token address as string
chainId: S.number, // Input: chain ID to select the right RPC client
},
output: {
decimal: S.number, // Output: decimal value for the token
},
rateLimit: false, // Disable rate limiting for this effect
},
async ({ input, context }) => {
try {
const client = CHAIN_CLIENTS[input.chainId];
if (!client) {
context.log.warn(
`No RPC client configured for chainId ${input.chainId}`,
);
return { decimal: 18 };
}

// Call token.decimals() via RPC
const decimals = await client.readContract({
address: input.token as `0x${string}`,
abi: ERC20_ABI,
functionName: "decimals",
});

return { decimal: Number(decimals) };
} catch (err) {
// Log a warning instead of failing the entire event
context.log.warn(
`⚠️ Failed to fetch token decimals for ${input.token}: ${err}`,
);

// Fallback: most tokens use 18 decimals
return { decimal: 18 };
}
},
);
```

## Prerequisites

Before running the indexer locally, make sure you have the following installed:

- **[Node.js 22+](https://nodejs.org/en/download/)**
- **[pnpm](https://pnpm.io/installation)**
- **[Docker Desktop](https://www.docker.com/products/docker-desktop/)**

## Running the Indexer

Add your Envio API key and RPC URLs to the `.env` file, then start the indexer:

```bash
pnpm dev
```

If you make changes to `config.yaml` or `schema.graphql`, regenerate the type files:

```bash
pnpm codegen
```

## GraphQL Playground

While indexer is running, visit the [Envio Console](https://envio.dev/console) to open the GraphQL Playground and query your indexed data.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
type UniswapV3Factory_PoolCreated {
id: ID! # Unique ID for the event (tx hash + log index)
token0: String! # Address of token0 in the pool
token0Decimals: Int! # Decimals of token0 (from ERC20)
token1: String! # Address of token1 in the pool
token1Decimals: Int! # Decimals of token1 (from ERC20)
fee: BigInt! # Pool fee tier (e.g., 500, 3000, 10000)
tickSpacing: BigInt! # Tick spacing for the pool ticks
pool: String! # Address of the created Uniswap V3 pool
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# yaml-language-server: $schema=./node_modules/envio/evm.schema.json
name: external-call-example
description: Example showing how to use HyperIndex's Effect API for external calls like RPC or API.
contracts:
- name: UniswapV3Factory
events:
- event: PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool)

chains:
- id: 1 # Ethereum Mainnet
start_block: 12369621
contracts:
- name: UniswapV3Factory
address:
- 0x1F98431c8aD98523631AE4a59f267346ea31F984
- id: 42161 # Arbitrum
start_block: 165
contracts:
- name: UniswapV3Factory
address:
- 0x1F98431c8aD98523631AE4a59f267346ea31F984
Loading