diff --git a/docs/pages/guides/design-your-schema.mdx b/docs/pages/guides/design-your-schema.mdx
index ef225f7eb..c2d63d169 100644
--- a/docs/pages/guides/design-your-schema.mdx
+++ b/docs/pages/guides/design-your-schema.mdx
@@ -30,7 +30,7 @@ Every entity type _must_ have an `id` field that is a `String!`, `Int!`, `Bytes!
### Scalars
-In GraphQL, **scalars** are the most primitive types – they represent concrete data like strings and numbers. Each GraphQL scalar maps to a TypeScript type (used in event handler code) and a JSON data type (returned by the GraphQL API).
+In GraphQL, **scalars** are the most primitive types – they represent concrete data like strings and numbers. Each GraphQL scalar maps to a TypeScript type (used in indexing function code) and a JSON data type (returned by the GraphQL API).
| name | description | TypeScript type | JSON data type |
| :-------- | :------------------------------------------ | :-------------- | :------------- |
@@ -290,7 +290,7 @@ const bob = await Person.get("Bob");
### Entity types should generally be nouns
-Blockchain events describe an action that has taken place (a verb). Event handler funtions are most effective when they convert events into the nouns that represent the current state of your application. For example, prefer modeling an ERC721 collection using `Account` and `Token` entities rather than `TransferEvent` entities. (Unless you need the full transfer history of every account).
+Blockchain events describe an action that has taken place (a verb). Indexing funtions are most effective when they convert events into the nouns that represent the current state of your application. For example, prefer modeling an ERC721 collection using `Account` and `Token` entities rather than `TransferEvent` entities. (Unless you need the full transfer history of every account).
### Use relationship types generously
diff --git a/docs/pages/guides/installation.mdx b/docs/pages/guides/installation.mdx
index 298d842ef..4b90b267e 100644
--- a/docs/pages/guides/installation.mdx
+++ b/docs/pages/guides/installation.mdx
@@ -6,6 +6,12 @@ import { Callout } from "nextra-theme-docs";
# Installation
+
+ Ponder usually requires `node-gyp` to be installed globally. If you encounter
+ an installation error, try `npm install -g node-gyp` and then run
+ `create-ponder` again.
+
+
## System requirements
- MacOS, Linux, or Windows (via [WSL](https://learn.microsoft.com/en-us/windows/wsl/install))
diff --git a/docs/pages/guides/read-contract-data.mdx b/docs/pages/guides/read-contract-data.mdx
index 3fb2fb560..a22279980 100644
--- a/docs/pages/guides/read-contract-data.mdx
+++ b/docs/pages/guides/read-contract-data.mdx
@@ -92,7 +92,7 @@ ponder.on("AaveToken:Mint", async ({ event, context }) => {
## Caching
-To avoid unnecessary RPC requests and speed up indexing, Ponder caches all contract read results. When an event handler that reads a contract runs for the first time, it will make an RPC request. But on subsequent hot reloads or redeployments, this data will be served from the cache.
+To avoid unnecessary RPC requests and speed up indexing, Ponder caches all contract read results. When an indexing function that reads a contract runs for the first time, it will make an RPC request. But on subsequent hot reloads or redeployments, this data will be served from the cache.
To take advantage of caching, you _must_ use `context.contracts`. _Do not manually set up a viem Client._
@@ -134,7 +134,7 @@ ponder.on("Blitmap:Mint", async ({ event, context }) => {
## Specify a block number
-By default, contract reads use the `eth_call` RPC method with `blockNumber` set to the block number of the event being handled (`event.block.number`). You can read the contract at a different block number (e.g. the contract deployment block number or `"latest"`) by passing the `blockNumber` or `blockTag` option, but this will disable caching.
+By default, contract reads use the `eth_call` RPC method with `blockNumber` set to the block number of the event being processed (`event.block.number`). You can read the contract at a different block number (e.g. the contract deployment block number or `"latest"`) by passing the `blockNumber` or `blockTag` option, but this will disable caching.
```ts filename="src/index.ts"
ponder.on("Blitmap:Mint", async ({ event, context }) => {
diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx
index fbbbc3ad9..c8a5d77b7 100644
--- a/docs/pages/index.mdx
+++ b/docs/pages/index.mdx
@@ -3,7 +3,7 @@ description: "An introduction to Ponder"
title: "Introduction"
---
-import { Steps } from "nextra/components";
+import { FileTree, Steps } from "nextra/components";
import { Callout } from "nextra-theme-docs";
import Architecture from "../public/architecture.svg";
@@ -20,31 +20,28 @@ Ponder is an open-source framework for blockchain application backends. With Pon
✅ Quickly deploy to [Railway](/guides/production), or anywhere using Node.js/Docker
✅ Native support for cross-chain apps
✅ Gracefully handles chain reorganizations
-🏗️ Handle transactions calls (in addition to logs)
-🏗️ Run effects (e.g. send an API request) in event handler code
+🏗️ Process transactions calls (in addition to logs)
+🏗️ Run effects (e.g. send an API request) in indexing code
## Architecture
- Data flows from smart contracts → event handler functions → application data
- store → GraphQL API → client applications.
+ Data flows from smart contracts → indexing functions → application data store
+ → GraphQL API → client applications.
-## Example: ENS
+## Example: Ethereum Name Service
Ponder is similar to Graph Protocol subgraphs. If you've built a subgraph
before, skip to [getting started](/getting-started/new-project).
-Let's use Ponder to build a GraphQL API that tracks ownership of Ethereum Name Service registrations.
-
-The ENS `BaseRegistrar` contract emits a `NameRegistered` event every time a user registers a new ENS name. Here's the relevant snippet from that contract:
+Let's use Ponder to build a GraphQL API that tracks ownership of ENS registrations. The ENS `BaseRegistrar` contract emits a `NameRegistered` event every time a user registers a new ENS name.
```solidity
contract BaseRegistrar {
-
event NameRegistered(string name, address owner);
function registerName(string calldata name, address owner) external {
@@ -56,9 +53,9 @@ contract BaseRegistrar {
-### Ponder config
+### Add a contract to `ponder.config.ts`
-First we'll add `BaseRegistrar` as a contract in our config file. Ponder's sync engine will now fetch all the events logs that have been emitted by the `BaseRegistrar` contract.
+First, we add `BaseRegistrar` as a contract in our config file. Ponder's sync engine fetches all the events logs that have been emitted by the `BaseRegistrar` contract.
```ts filename="ponder.config.ts"
import { http } from "viem";
@@ -83,9 +80,9 @@ export const config = {
};
```
-### GraphQL schema
+### Create your schema
-Next we'll add an entity to our schema. The `schema.graphql` file defines the shape of the data that our GraphQL API will serve. Let's add an `EnsName` entity that stores the name, the owner's address, and a timestamp.
+Next, we add an entity to our schema. The `schema.graphql` file defines the shape of the data that our GraphQL API will serve. Let's add an `EnsName` entity that stores the name, the owner's address, and a timestamp.
```graphql filename="schema.graphql"
type EnsName @entity {
@@ -96,11 +93,9 @@ type EnsName @entity {
}
```
-### Event handler functions
-
-Event handlers are TypeScript functions that process a blockchain event and insert data into the Entity store matching the GraphQL schema.
+### Write indexing code
-Here's an event handler to process the `NameRegistered` event. It inserts an `EnsName` entity into the store using the event parameters and the block timestamp.
+Indexing functions are TypeScript functions that process a blockchain event and insert data into the entity store. Here's an indexing function to process the `NameRegistered` event. It inserts an `EnsName` entity into the store using the event parameters and the block timestamp.
{/* prettier-ignore */}
```ts filename="src/EthereumNameService.ts"
@@ -121,9 +116,9 @@ ponder.on("BaseRegistrar:NameRegistered", async ({ event, context }) => {
});
```
-### GraphQL API
+### Query the API
-Now that we've inserted some data into the Entity store, we can query that data from the autogenerated GraphQL API:
+Now that we've inserted some data into the Entity store, we can query that data from the autogenerated GraphQL API.
{/* prettier-ignore */}
```graphql filename="http://localhost:42069/graphql"
@@ -154,7 +149,7 @@ query GetEnsNames {
}
```
-The GraphQL API includes useful features like pagination, sorting, and complex filters for each entity defined in `schema.graphql`.
+The GraphQL API automatically includes features like pagination, sorting, and complex filters for each entity defined in `schema.graphql`.
diff --git a/docs/public/architecture.svg b/docs/public/architecture.svg
index 681692b9f..2b3147011 100644
--- a/docs/public/architecture.svg
+++ b/docs/public/architecture.svg
@@ -1,4 +1,4 @@
-
+
@@ -12,5 +12,7 @@
src: url("https://excalidraw.com/Cascadia.woff2");
}
+
- Ponder Event handlers Blockchain Raw blockchain data Database Application Application data Website GraphQL API Mobile app Smart contract Smart contract Application data
\ No newline at end of file
+
+ Ponder Indexing functions Blockchain Raw blockchain data Database Application Application data Website GraphQL API Mobile app Smart contract Smart contract Application data
\ No newline at end of file
diff --git a/docs/theme.config.tsx b/docs/theme.config.tsx
index 2cd61396b..1924839ac 100644
--- a/docs/theme.config.tsx
+++ b/docs/theme.config.tsx
@@ -18,7 +18,8 @@ const config: DocsThemeConfig = {
sidebar: {
defaultMenuCollapseLevel: 2,
},
- primaryHue: 188,
+ primaryHue: 186,
+ primarySaturation: 86,
editLink: {
text: "Edit this page on GitHub →",
},
diff --git a/packages/core/README.md b/packages/core/README.md
index 2fd265e25..b50089dff 100644
--- a/packages/core/README.md
+++ b/packages/core/README.md
@@ -26,14 +26,15 @@ Join [Ponder's telegram chat](https://t.me/ponder_sh) for support, feedback, and
✅ Supports all Ethereum-based blockchains, including test nodes like [Anvil](https://book.getfoundry.sh/anvil)
✅ Index events from multiple chains in the same app
✅ Reconciles chain reorganization
-🏗️ Transaction call event handlers
-🏗️ Support for factory contracts like Uniswap V2/V3
+✅ Factory contracts
+🏗️ Process transactions calls (in addition to logs)
+🏗️ Run effects (e.g. send an API request) in indexing code
## Quickstart
### 1. Run `create-ponder`
-You will be asked for a project name, and if you are using an Etherscan or Graph Protocol [template](https://ponder.sh/api-reference/create-ponder) (recommended).
+You will be asked for a project name, and if you are using a [template](https://ponder.sh/api-reference/create-ponder#templates) (recommended). Then, the CLI will create a project directory, install dependencies, and initialize a git repository.
```bash
npm init ponder@latest
@@ -45,7 +46,7 @@ yarn create ponder
### 2. Start the development server
-The development server automatically reloads your app when you save changes in any project file, and prints `console.log` statements and errors in your code.
+Just like Next.js and Vite, Ponder has a development server that automatically reloads when you save changes in any project file. It also prints `console.log` statements and errors encountered while running your code. First, `cd` into your project directory, then start the server.
```bash
npm run dev
@@ -57,7 +58,7 @@ yarn dev
### 3. Add contracts & networks
-Ponder fetches event logs for the contracts added to `ponder.config.ts`, and passes those events to the handler functions you write.
+Ponder fetches event logs for the contracts added to `ponder.config.ts`, and passes those events to the indexing functions you write.
```ts
// ponder.config.ts
@@ -85,7 +86,7 @@ export const config = {
### 4. Define your schema
-The `schema.graphql` file specifies the shape of your application's data.
+The `schema.graphql` file contains a model of your application data. The entity types defined here correspond to database tables.
```ts
// schema.graphql
@@ -98,9 +99,9 @@ type EnsName @entity {
}
```
-### 5. Write event handlers
+### 5. Write indexing functions
-Use event handler functions to convert raw blockchain events into application data.
+Files in the `src/` directory contain **indexing functions**, which are TypeScript functions that process a contract event. The purpose of these functions is to insert data into the entity store.
```ts
// src/BaseRegistrar.ts
@@ -122,9 +123,11 @@ ponder.on("BaseRegistrar:NameRegistered", async ({ event, context }) => {
});
```
+See the [create & update entities](https://ponder.sh/guides/create-update-entities) docs for a detailed guide on writing indexing functions.
+
### 6. Query the GraphQL API
-Ponder automatically generates a frontend-ready GraphQL API based on your project's `schema.graphql`. The API will serve the data that you inserted in your event handler functions.
+Ponder automatically generates a frontend-ready GraphQL API based on your project's `schema.graphql`. The API serves the data that you inserted in your indexing functions.
```ts
{
diff --git a/packages/core/src/Ponder.ts b/packages/core/src/Ponder.ts
index e1f1835be..7e5b452c5 100644
--- a/packages/core/src/Ponder.ts
+++ b/packages/core/src/Ponder.ts
@@ -16,13 +16,13 @@ import { PostgresEventStore } from "@/event-store/postgres/store";
import { SqliteEventStore } from "@/event-store/sqlite/store";
import { type EventStore } from "@/event-store/store";
import { HistoricalSyncService } from "@/historical-sync/service";
+import { IndexingService } from "@/indexing/service";
import { LoggerService } from "@/logs/service";
import { MetricsService } from "@/metrics/service";
import { RealtimeSyncService } from "@/realtime-sync/service";
import { ServerService } from "@/server/service";
import { TelemetryService } from "@/telemetry/service";
import { UiService } from "@/ui/service";
-import { EventHandlerService } from "@/user-handlers/service";
import { PostgresUserStore } from "@/user-store/postgres/store";
import { SqliteUserStore } from "@/user-store/sqlite/store";
import { type UserStore } from "@/user-store/store";
@@ -52,7 +52,7 @@ export class Ponder {
}[] = [];
eventAggregatorService: EventAggregatorService;
- eventHandlerService: EventHandlerService;
+ indexingService: IndexingService;
serverService: ServerService;
buildService: BuildService;
@@ -162,7 +162,7 @@ export class Ponder {
factories,
});
- this.eventHandlerService = new EventHandlerService({
+ this.indexingService = new IndexingService({
common,
eventStore: this.eventStore,
userStore: this.userStore,
@@ -197,16 +197,16 @@ export class Ponder {
await this.serverService.start();
// These files depend only on ponder.config.ts, so can generate once on setup.
- // Note that loadHandlers depends on the index.ts file being present.
+ // Note that buildIndexingFunctions depends on the index.ts file being present.
this.codegenService.generateAppFile();
- // Note that this must occur before loadSchema and loadHandlers.
+ // Note that this must occur before buildSchema and buildIndexingFunctions.
await this.eventStore.migrateUp();
- // Manually trigger loading schema and handlers. Subsequent loads
+ // Manually trigger loading schema and indexing functions. Subsequent loads
// are triggered by changes to project files (handled in BuildService).
this.buildService.buildSchema();
- await this.buildService.buildHandlers();
+ await this.buildService.buildIndexingFunctions();
}
async dev() {
@@ -295,7 +295,7 @@ export class Ponder {
await this.buildService.kill?.();
this.uiService.kill();
- this.eventHandlerService.kill();
+ this.indexingService.kill();
await this.serverService.kill();
await this.userStore.teardown();
await this.common.telemetry.kill();
@@ -323,14 +323,17 @@ export class Ponder {
this.serverService.reload({ graphqlSchema });
- await this.eventHandlerService.reset({ schema });
- await this.eventHandlerService.processEvents();
+ await this.indexingService.reset({ schema });
+ await this.indexingService.processEvents();
});
- this.buildService.on("newHandlers", async ({ handlers }) => {
- await this.eventHandlerService.reset({ handlers });
- await this.eventHandlerService.processEvents();
- });
+ this.buildService.on(
+ "newIndexingFunctions",
+ async ({ indexingFunctions }) => {
+ await this.indexingService.reset({ indexingFunctions });
+ await this.indexingService.processEvents();
+ }
+ );
this.networkSyncServices.forEach((networkSyncService) => {
const { chainId } = networkSyncService.network;
@@ -374,18 +377,18 @@ export class Ponder {
});
this.eventAggregatorService.on("newCheckpoint", async () => {
- await this.eventHandlerService.processEvents();
+ await this.indexingService.processEvents();
});
this.eventAggregatorService.on(
"reorg",
async ({ commonAncestorTimestamp }) => {
- await this.eventHandlerService.handleReorg({ commonAncestorTimestamp });
- await this.eventHandlerService.processEvents();
+ await this.indexingService.handleReorg({ commonAncestorTimestamp });
+ await this.indexingService.processEvents();
}
);
- this.eventHandlerService.on("eventsProcessed", ({ toTimestamp }) => {
+ this.indexingService.on("eventsProcessed", ({ toTimestamp }) => {
if (this.serverService.isHistoricalIndexingComplete) return;
// If a batch of events are processed AND the historical sync is complete AND
diff --git a/packages/core/src/_test/art-gobblers/app.test.ts b/packages/core/src/_test/art-gobblers/app.test.ts
index 9f8d572ee..434a65975 100644
--- a/packages/core/src/_test/art-gobblers/app.test.ts
+++ b/packages/core/src/_test/art-gobblers/app.test.ts
@@ -43,7 +43,7 @@ const setup = async ({ context }: { context: TestContext }) => {
// Wait for historical sync event processing to complete.
await new Promise
((resolve) => {
- ponder.eventHandlerService.on("eventsProcessed", ({ toTimestamp }) => {
+ ponder.indexingService.on("eventsProcessed", ({ toTimestamp }) => {
// Block 15870405
if (toTimestamp >= 1667247815) {
resolve();
diff --git a/packages/core/src/_test/ens/app.test.ts b/packages/core/src/_test/ens/app.test.ts
index 0dc6855c9..86de428de 100644
--- a/packages/core/src/_test/ens/app.test.ts
+++ b/packages/core/src/_test/ens/app.test.ts
@@ -43,7 +43,7 @@ const setup = async ({ context }: { context: TestContext }) => {
// Wait for historical sync event processing to complete.
await new Promise((resolve) => {
- ponder.eventHandlerService.on("eventsProcessed", ({ toTimestamp }) => {
+ ponder.indexingService.on("eventsProcessed", ({ toTimestamp }) => {
// Block 16370020
if (toTimestamp >= 1673276663) {
resolve();
diff --git a/packages/core/src/build/handlers.ts b/packages/core/src/build/functions.ts
similarity index 73%
rename from packages/core/src/build/handlers.ts
rename to packages/core/src/build/functions.ts
index ef658e8e3..0e4ae5d70 100644
--- a/packages/core/src/build/handlers.ts
+++ b/packages/core/src/build/functions.ts
@@ -24,7 +24,7 @@ export interface LogEvent {
type EventSourceName = string;
type EventName = string;
-type LogEventHandlerFunction = ({
+type LogEventIndexingFunction = ({
event,
context,
}: {
@@ -32,37 +32,38 @@ type LogEventHandlerFunction = ({
context: unknown;
}) => Promise | void;
-type SetupEventHandlerFunction = ({
+type SetupEventIndexingFunction = ({
context,
}: {
context: unknown;
}) => Promise | void;
-type RawHandlerFunctions = {
+type RawIndexingFunctions = {
_meta_?: {
- setup?: SetupEventHandlerFunction;
+ setup?: SetupEventIndexingFunction;
};
eventSources: {
[key: EventSourceName]: {
- [key: EventName]: LogEventHandlerFunction;
+ [key: EventName]: LogEventIndexingFunction;
};
};
};
// @ponder/core creates an instance of this class called `ponder`
export class PonderApp<
- EventHandlers = Record
+ IndexingFunctions = Record
> {
- private handlerFunctions: RawHandlerFunctions = { eventSources: {} };
+ private indexingFunctions: RawIndexingFunctions = { eventSources: {} };
private errors: Error[] = [];
- on>(
+ on>(
name: EventName,
- handler: EventHandlers[EventName]
+ indexingFunction: IndexingFunctions[EventName]
) {
if (name === "setup") {
- this.handlerFunctions._meta_ ||= {};
- this.handlerFunctions._meta_.setup = handler as SetupEventHandlerFunction;
+ this.indexingFunctions._meta_ ||= {};
+ this.indexingFunctions._meta_.setup =
+ indexingFunction as SetupEventIndexingFunction;
return;
}
@@ -72,19 +73,19 @@ export class PonderApp<
return;
}
- this.handlerFunctions.eventSources[eventSourceName] ||= {};
- if (this.handlerFunctions.eventSources[eventSourceName][eventName]) {
+ this.indexingFunctions.eventSources[eventSourceName] ||= {};
+ if (this.indexingFunctions.eventSources[eventSourceName][eventName]) {
this.errors.push(
- new Error(`Cannot add multiple handler functions for event: ${name}`)
+ new Error(`Cannot add multiple indexing functions for event: ${name}`)
);
return;
}
- this.handlerFunctions.eventSources[eventSourceName][eventName] =
- handler as LogEventHandlerFunction;
+ this.indexingFunctions.eventSources[eventSourceName][eventName] =
+ indexingFunction as LogEventIndexingFunction;
}
}
-export const buildRawHandlerFunctions = async ({
+export const buildRawIndexingFunctions = async ({
options,
}: {
options: Options;
@@ -182,49 +183,47 @@ export const buildRawHandlerFunctions = async ({
throw error;
}
- const handlers = app["handlerFunctions"] as RawHandlerFunctions;
-
- return handlers;
+ return app["indexingFunctions"] as RawIndexingFunctions;
};
-export type HandlerFunctions = {
+export type IndexingFunctions = {
_meta_: {
setup?: {
- fn: SetupEventHandlerFunction;
+ fn: SetupEventIndexingFunction;
};
};
eventSources: {
[key: EventSourceName]: {
- // This mapping is passed from the EventHandlerService to the EventAggregatorService, which uses
- // it to fetch from the store _only_ the events that the user has handled.
+ // This mapping is passed from the IndexingService to the EventAggregatorService, which uses
+ // it to fetch from the store _only_ the events that the user has indexed.
bySelector: { [key: Hex]: LogEventMetadata };
- // This mapping is used by the EventHandlerService to fetch the user-provided `fn` before running it.
+ // This mapping is used by the IndexingService to fetch the user-provided `fn` before running it.
bySafeName: {
- [key: EventName]: LogEventMetadata & { fn: LogEventHandlerFunction };
+ [key: EventName]: LogEventMetadata & { fn: LogEventIndexingFunction };
};
};
};
};
-export const hydrateHandlerFunctions = ({
- rawHandlerFunctions,
+export const hydrateIndexingFunctions = ({
+ rawIndexingFunctions,
logFilters,
factories,
}: {
- rawHandlerFunctions: RawHandlerFunctions;
+ rawIndexingFunctions: RawIndexingFunctions;
logFilters: LogFilter[];
factories: Factory[];
}) => {
- const handlerFunctions: HandlerFunctions = {
+ const indexingFunctions: IndexingFunctions = {
_meta_: {},
eventSources: {},
};
- if (rawHandlerFunctions._meta_?.setup) {
- handlerFunctions._meta_.setup = { fn: rawHandlerFunctions._meta_.setup };
+ if (rawIndexingFunctions._meta_?.setup) {
+ indexingFunctions._meta_.setup = { fn: rawIndexingFunctions._meta_.setup };
}
- Object.entries(rawHandlerFunctions.eventSources).forEach(
+ Object.entries(rawIndexingFunctions.eventSources).forEach(
([eventSourceName, eventSourceFunctions]) => {
const logFilter = logFilters.find((l) => l.name === eventSourceName);
const factory = factories.find((f) => f.name === eventSourceName);
@@ -242,19 +241,19 @@ export const hydrateHandlerFunctions = ({
throw new Error(`Log event not found in ABI: ${eventName}`);
}
- handlerFunctions.eventSources[eventSourceName] ||= {
+ indexingFunctions.eventSources[eventSourceName] ||= {
bySafeName: {},
bySelector: {},
};
- handlerFunctions.eventSources[eventSourceName].bySelector[
+ indexingFunctions.eventSources[eventSourceName].bySelector[
eventData.selector
] = eventData;
- handlerFunctions.eventSources[eventSourceName].bySafeName[
+ indexingFunctions.eventSources[eventSourceName].bySafeName[
eventData.safeName
] = { ...eventData, fn: fn };
});
}
);
- return handlerFunctions;
+ return indexingFunctions;
};
diff --git a/packages/core/src/build/service.ts b/packages/core/src/build/service.ts
index 81f4a3777..2376b589b 100644
--- a/packages/core/src/build/service.ts
+++ b/packages/core/src/build/service.ts
@@ -14,15 +14,15 @@ import type { Schema } from "@/schema/types";
import { buildGqlSchema } from "@/server/graphql/schema";
import {
- type HandlerFunctions,
- buildRawHandlerFunctions,
- hydrateHandlerFunctions,
-} from "./handlers";
+ type IndexingFunctions,
+ buildRawIndexingFunctions,
+ hydrateIndexingFunctions,
+} from "./functions";
import { readGraphqlSchema } from "./schema";
type BuildServiceEvents = {
newConfig: undefined;
- newHandlers: { handlers: HandlerFunctions };
+ newIndexingFunctions: { indexingFunctions: IndexingFunctions };
newSchema: { schema: Schema; graphqlSchema: GraphQLSchema };
};
@@ -88,38 +88,38 @@ export class BuildService extends Emittery {
if (filePath === this.common.options.schemaFile) {
this.buildSchema();
} else {
- await this.buildHandlers();
+ await this.buildIndexingFunctions();
}
}
});
}
- async buildHandlers() {
+ async buildIndexingFunctions() {
try {
- const rawHandlerFunctions = await buildRawHandlerFunctions({
+ const rawIndexingFunctions = await buildRawIndexingFunctions({
options: this.common.options,
});
- const handlers = hydrateHandlerFunctions({
- rawHandlerFunctions,
+ const indexingFunctions = hydrateIndexingFunctions({
+ rawIndexingFunctions,
logFilters: this.logFilters,
factories: this.factories,
});
- if (Object.values(handlers.eventSources).length === 0) {
+ if (Object.values(indexingFunctions.eventSources).length === 0) {
this.common.logger.warn({
service: "build",
- msg: "No event handler functions found",
+ msg: "No indexing functions found",
});
}
- this.emit("newHandlers", { handlers });
+ this.emit("newIndexingFunctions", { indexingFunctions });
} catch (error_) {
const error = error_ as Error;
- // TODO: Build the UserError object within readHandlers, check instanceof,
+ // TODO: Build the UserError object within readIndexingFunctions, check instanceof,
// then log/submit as-is if it's already a UserError.
- const message = `Error while building handlers: ${error.message}`;
+ const message = `Error during build: ${error.message}`;
const userError = new UserError(message, {
stack: error.stack,
});
@@ -147,7 +147,7 @@ export class BuildService extends Emittery {
// TODO: Parse GraphQLError instances better here.
// We can use the `.locations` property to build a pretty codeframe.
- // TODO: Build the UserError object within readHandlers, check instanceof,
+ // TODO: Build the UserError object within readIndexingFunctions, check instanceof,
// then log/submit as-is if it's already a UserError.
const message = `Error while building schema.graphql: ${error.message}`;
const userError = new UserError(message, {
diff --git a/packages/core/src/codegen/event.ts b/packages/core/src/codegen/event.ts
index 73f8b676e..715992e77 100644
--- a/packages/core/src/codegen/event.ts
+++ b/packages/core/src/codegen/event.ts
@@ -9,7 +9,7 @@ export const buildEventTypes = ({
logFilters: LogFilter[];
factories: Factory[];
}) => {
- const allHandlers = [
+ const allIndexingFunctions = [
...logFilters.map((logFilter) => {
return Object.values(logFilter.events)
.filter((val): val is LogEventMetadata => !!val)
@@ -66,13 +66,13 @@ export const buildEventTypes = ({
}),
];
- allHandlers.unshift(
+ allIndexingFunctions.unshift(
`["setup"]: ({ context }: { context: Context; }) => Promise | any;`
);
const final = `
export type AppType = {
- ${allHandlers.join("")}
+ ${allIndexingFunctions.join("")}
}
`;
diff --git a/packages/core/src/codegen/service.ts b/packages/core/src/codegen/service.ts
index 137289b04..7d231b4aa 100644
--- a/packages/core/src/codegen/service.ts
+++ b/packages/core/src/codegen/service.ts
@@ -74,7 +74,7 @@ export class CodegenService extends Emittery {
}
- /* HANDLER TYPES */
+ /* INDEXING FUNCTION TYPES */
${buildEventTypes({
logFilters: this.logFilters,
diff --git a/packages/core/src/config/abi.ts b/packages/core/src/config/abi.ts
index ffbb0b275..80766415b 100644
--- a/packages/core/src/config/abi.ts
+++ b/packages/core/src/config/abi.ts
@@ -85,7 +85,7 @@ type SafeEventName = string;
export type LogEventMetadata = {
// Event name (if no overloads) or full event signature (if name is overloaded).
- // This is the event name used when registering event handlers using `ponder.on("ContractName:EventName", ...)`
+ // This is the event name used when registering indexing functions using `ponder.on("ContractName:EventName", ...)`
safeName: string;
// Full event signature, e.g. `event Deposit(address indexed from,bytes32 indexed id,uint value);`
signature: string;
diff --git a/packages/core/src/config/types.ts b/packages/core/src/config/types.ts
index 5ce5f76c2..23f02e201 100644
--- a/packages/core/src/config/types.ts
+++ b/packages/core/src/config/types.ts
@@ -40,7 +40,7 @@ export type ResolvedConfig = {
/** Maximum concurrency of RPC requests during the historical sync. Default: `10`. */
maxRpcRequestConcurrency?: number;
}[];
- /** List of contracts to fetch & handle events from. Contracts defined here will be present in `context.contracts`. */
+ /** List of contracts to sync & index events from. Contracts defined here will be present in `context.contracts`. */
contracts?: ({
/** Contract name. Must be unique across `contracts` and `filters`. */
name: string;
@@ -76,7 +76,7 @@ export type ResolvedConfig = {
/** Whether to fetch & process event logs for this contract. If `false`, this contract will still be present in `context.contracts`. Default: `true`. */
isLogEventSource?: boolean;
})[];
- /** List of log filters from which to fetch & handle event logs. */
+ /** List of log filters from which to sync & index event logs. */
filters?: {
/** Filter name. Must be unique across `contracts` and `filters`. */
name: string;
diff --git a/packages/core/src/event-aggregator/service.ts b/packages/core/src/event-aggregator/service.ts
index a3a2cc9be..97f0a9117 100644
--- a/packages/core/src/event-aggregator/service.ts
+++ b/packages/core/src/event-aggregator/service.ts
@@ -114,11 +114,11 @@ export class EventAggregatorService extends Emittery {
async *getEvents({
fromTimestamp,
toTimestamp,
- handledEventMetadata,
+ indexingMetadata,
}: {
fromTimestamp: number;
toTimestamp: number;
- handledEventMetadata: {
+ indexingMetadata: {
[eventSourceName: string]:
| {
bySelector: { [selector: Hex]: LogEventMetadata };
@@ -136,7 +136,7 @@ export class EventAggregatorService extends Emittery {
fromBlock: logFilter.startBlock,
toBlock: logFilter.endBlock,
includeEventSelectors: Object.keys(
- handledEventMetadata[logFilter.name]?.bySelector ?? {}
+ indexingMetadata[logFilter.name]?.bySelector ?? {}
) as Hex[],
})),
factories: this.factories.map((factory) => ({
@@ -146,7 +146,7 @@ export class EventAggregatorService extends Emittery {
fromBlock: factory.startBlock,
toBlock: factory.endBlock,
includeEventSelectors: Object.keys(
- handledEventMetadata[factory.name]?.bySelector ?? {}
+ indexingMetadata[factory.name]?.bySelector ?? {}
) as Hex[],
})),
});
@@ -163,7 +163,7 @@ export class EventAggregatorService extends Emittery {
}
const logEventMetadata =
- handledEventMetadata[event.eventSourceName]?.bySelector[selector];
+ indexingMetadata[event.eventSourceName]?.bySelector[selector];
if (!logEventMetadata) {
throw new Error(
`Metadata for event ${event.eventSourceName}:${selector} not found in includeEvents`
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index dad8b386b..d26b8e021 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -1,4 +1,4 @@
-export { PonderApp } from "@/build/handlers";
+export { PonderApp } from "@/build/functions";
export type { Config, ResolvedConfig } from "@/config/types";
export type { Block } from "@/types/block";
export type { ReadOnlyContract } from "@/types/contract";
diff --git a/packages/core/src/user-handlers/contract.test.ts b/packages/core/src/indexing/contract.test.ts
similarity index 100%
rename from packages/core/src/user-handlers/contract.test.ts
rename to packages/core/src/indexing/contract.test.ts
diff --git a/packages/core/src/user-handlers/contract.ts b/packages/core/src/indexing/contract.ts
similarity index 98%
rename from packages/core/src/user-handlers/contract.ts
rename to packages/core/src/indexing/contract.ts
index 095ec06a9..f1c7120dd 100644
--- a/packages/core/src/user-handlers/contract.ts
+++ b/packages/core/src/indexing/contract.ts
@@ -58,7 +58,7 @@ export function buildReadOnlyContracts({
}
// If the user specified a block number, use it, otherwise use the
- // block number of the current event being handled.
+ // block number of the current event being processed.
const blockNumber = options?.blockNumber ?? getCurrentBlockNumber();
const calldata = encodeFunctionData({ abi, args, functionName });
diff --git a/packages/core/src/user-handlers/model.ts b/packages/core/src/indexing/model.ts
similarity index 100%
rename from packages/core/src/user-handlers/model.ts
rename to packages/core/src/indexing/model.ts
diff --git a/packages/core/src/user-handlers/service.test.ts b/packages/core/src/indexing/service.test.ts
similarity index 82%
rename from packages/core/src/user-handlers/service.test.ts
rename to packages/core/src/indexing/service.test.ts
index ecc7b5069..201c8aad3 100644
--- a/packages/core/src/user-handlers/service.test.ts
+++ b/packages/core/src/indexing/service.test.ts
@@ -4,13 +4,13 @@ import { beforeEach, expect, test, vi } from "vitest";
import { usdcContractConfig } from "@/_test/constants";
import { setupEventStore, setupUserStore } from "@/_test/setup";
import { publicClient } from "@/_test/utils";
-import type { HandlerFunctions } from "@/build/handlers";
+import type { IndexingFunctions } from "@/build/functions";
import { schemaHeader } from "@/build/schema";
import { LogEventMetadata } from "@/config/abi";
import { EventAggregatorService } from "@/event-aggregator/service";
import { buildSchema } from "@/schema/schema";
-import { EventHandlerService } from "./service";
+import { IndexingService } from "./service";
beforeEach((context) => setupEventStore(context));
beforeEach((context) => setupUserStore(context));
@@ -46,7 +46,7 @@ const schema = buildSchema(
`)
);
-const transferHandler = vi.fn(async ({ event, context }) => {
+const transferIndexingFunction = vi.fn(async ({ event, context }) => {
await context.entities.TransferEvent.create({
id: event.log.id,
data: {
@@ -59,7 +59,7 @@ const transferEventMetadata = usdcContractConfig.events[
"Transfer"
] as LogEventMetadata;
-const handlers: HandlerFunctions = {
+const indexingFunctions: IndexingFunctions = {
_meta_: {},
eventSources: {
USDC: {
@@ -69,7 +69,7 @@ const handlers: HandlerFunctions = {
bySafeName: {
["Transfer"]: {
...transferEventMetadata,
- fn: transferHandler,
+ fn: transferIndexingFunction,
},
},
},
@@ -118,7 +118,7 @@ beforeEach(() => {
test("processEvents() calls getEvents with sequential timestamp ranges", async (context) => {
const { common, eventStore, userStore } = context;
- const service = new EventHandlerService({
+ const service = new IndexingService({
common,
eventStore,
userStore,
@@ -127,7 +127,7 @@ test("processEvents() calls getEvents with sequential timestamp ranges", async (
logFilters,
});
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
expect(getEvents).not.toHaveBeenCalled();
@@ -154,10 +154,10 @@ test("processEvents() calls getEvents with sequential timestamp ranges", async (
service.kill();
});
-test("processEvents() calls event handler functions with correct arguments", async (context) => {
+test("processEvents() calls indexing functions with correct arguments", async (context) => {
const { common, eventStore, userStore } = context;
- const service = new EventHandlerService({
+ const service = new IndexingService({
common,
eventStore,
userStore,
@@ -166,12 +166,12 @@ test("processEvents() calls event handler functions with correct arguments", asy
logFilters,
});
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
eventAggregatorService.checkpoint = 10;
await service.processEvents();
- expect(transferHandler).toHaveBeenCalledWith(
+ expect(transferIndexingFunction).toHaveBeenCalledWith(
expect.objectContaining({
event: {
eventSourceName: "USDC",
@@ -195,7 +195,7 @@ test("processEvents() calls event handler functions with correct arguments", asy
test("processEvents() model methods insert data into the user store", async (context) => {
const { common, eventStore, userStore } = context;
- const service = new EventHandlerService({
+ const service = new IndexingService({
common,
eventStore,
userStore,
@@ -204,7 +204,7 @@ test("processEvents() model methods insert data into the user store", async (con
logFilters,
});
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
eventAggregatorService.checkpoint = 10;
await service.processEvents();
@@ -220,7 +220,7 @@ test("processEvents() model methods insert data into the user store", async (con
test("processEvents() updates event count metrics", async (context) => {
const { common, eventStore, userStore } = context;
- const service = new EventHandlerService({
+ const service = new IndexingService({
common,
eventStore,
userStore,
@@ -229,13 +229,13 @@ test("processEvents() updates event count metrics", async (context) => {
logFilters,
});
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
eventAggregatorService.checkpoint = 10;
await service.processEvents();
const matchedEventsMetric = (
- await common.metrics.ponder_handlers_matched_events.get()
+ await common.metrics.ponder_indexing_matched_events.get()
).values;
expect(matchedEventsMetric).toMatchObject([
{ labels: { eventName: "setup" }, value: 1 },
@@ -243,14 +243,14 @@ test("processEvents() updates event count metrics", async (context) => {
]);
const handledEventsMetric = (
- await common.metrics.ponder_handlers_handled_events.get()
+ await common.metrics.ponder_indexing_handled_events.get()
).values;
expect(handledEventsMetric).toMatchObject([
{ labels: { eventName: "USDC:Transfer" }, value: 5 },
]);
const processedEventsMetric = (
- await common.metrics.ponder_handlers_processed_events.get()
+ await common.metrics.ponder_indexing_processed_events.get()
).values;
expect(processedEventsMetric).toMatchObject([
{ labels: { eventName: "USDC:Transfer" }, value: 1 },
@@ -262,7 +262,7 @@ test("processEvents() updates event count metrics", async (context) => {
test("reset() reloads the user store", async (context) => {
const { common, eventStore, userStore } = context;
- const service = new EventHandlerService({
+ const service = new IndexingService({
common,
eventStore,
userStore,
@@ -271,7 +271,7 @@ test("reset() reloads the user store", async (context) => {
logFilters,
});
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
eventAggregatorService.checkpoint = 10;
await service.processEvents();
@@ -283,7 +283,7 @@ test("reset() reloads the user store", async (context) => {
const versionIdBeforeReset = userStore.versionId;
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
expect(userStore.versionId).not.toBe(versionIdBeforeReset);
@@ -298,7 +298,7 @@ test("reset() reloads the user store", async (context) => {
test("handleReorg() updates ponder_handlers_latest_processed_timestamp metric", async (context) => {
const { common, eventStore, userStore } = context;
- const service = new EventHandlerService({
+ const service = new IndexingService({
common,
eventStore,
userStore,
@@ -307,20 +307,20 @@ test("handleReorg() updates ponder_handlers_latest_processed_timestamp metric",
logFilters,
});
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
eventAggregatorService.checkpoint = 10;
await service.processEvents();
const latestProcessedTimestampMetric = (
- await common.metrics.ponder_handlers_latest_processed_timestamp.get()
+ await common.metrics.ponder_indexing_latest_processed_timestamp.get()
).values[0].value;
expect(latestProcessedTimestampMetric).toBe(10);
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
const latestProcessedTimestampMetricAfterReset = (
- await common.metrics.ponder_handlers_latest_processed_timestamp.get()
+ await common.metrics.ponder_indexing_latest_processed_timestamp.get()
).values[0].value;
expect(latestProcessedTimestampMetricAfterReset).toBe(0);
@@ -330,7 +330,7 @@ test("handleReorg() updates ponder_handlers_latest_processed_timestamp metric",
test("handleReorg() reverts the user store", async (context) => {
const { common, eventStore, userStore } = context;
- const service = new EventHandlerService({
+ const service = new IndexingService({
common,
eventStore,
userStore,
@@ -341,7 +341,7 @@ test("handleReorg() reverts the user store", async (context) => {
const userStoreRevertSpy = vi.spyOn(userStore, "revert");
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
eventAggregatorService.checkpoint = 10;
await service.processEvents();
@@ -356,7 +356,7 @@ test("handleReorg() reverts the user store", async (context) => {
test("handleReorg() does nothing if there is a user error", async (context) => {
const { common, eventStore, userStore } = context;
- const service = new EventHandlerService({
+ const service = new IndexingService({
common,
eventStore,
userStore,
@@ -367,9 +367,9 @@ test("handleReorg() does nothing if there is a user error", async (context) => {
const userStoreRevertSpy = vi.spyOn(userStore, "revert");
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
- transferHandler.mockImplementationOnce(() => {
+ transferIndexingFunction.mockImplementationOnce(() => {
throw new Error("User error!");
});
@@ -386,7 +386,7 @@ test("handleReorg() does nothing if there is a user error", async (context) => {
test("handleReorg() processes the correct range of events after a reorg", async (context) => {
const { common, eventStore, userStore } = context;
- const service = new EventHandlerService({
+ const service = new IndexingService({
common,
eventStore,
userStore,
@@ -395,7 +395,7 @@ test("handleReorg() processes the correct range of events after a reorg", async
logFilters,
});
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
eventAggregatorService.checkpoint = 10;
await service.processEvents();
@@ -426,7 +426,7 @@ test("handleReorg() processes the correct range of events after a reorg", async
test("handleReorg() updates ponder_handlers_latest_processed_timestamp metric", async (context) => {
const { common, eventStore, userStore } = context;
- const service = new EventHandlerService({
+ const service = new IndexingService({
common,
eventStore,
userStore,
@@ -435,13 +435,13 @@ test("handleReorg() updates ponder_handlers_latest_processed_timestamp metric",
logFilters,
});
- await service.reset({ schema, handlers });
+ await service.reset({ schema, indexingFunctions });
eventAggregatorService.checkpoint = 10;
await service.processEvents();
const latestProcessedTimestampMetric = (
- await common.metrics.ponder_handlers_latest_processed_timestamp.get()
+ await common.metrics.ponder_indexing_latest_processed_timestamp.get()
).values[0].value;
expect(latestProcessedTimestampMetric).toBe(10);
@@ -451,7 +451,7 @@ test("handleReorg() updates ponder_handlers_latest_processed_timestamp metric",
await service.handleReorg({ commonAncestorTimestamp: 6 });
const latestProcessedTimestampMetricAfterReorg = (
- await common.metrics.ponder_handlers_latest_processed_timestamp.get()
+ await common.metrics.ponder_indexing_latest_processed_timestamp.get()
).values[0].value;
expect(latestProcessedTimestampMetricAfterReorg).toBe(6);
diff --git a/packages/core/src/user-handlers/service.ts b/packages/core/src/indexing/service.ts
similarity index 75%
rename from packages/core/src/user-handlers/service.ts
rename to packages/core/src/indexing/service.ts
index fb215b6f2..f76c3d0c5 100644
--- a/packages/core/src/user-handlers/service.ts
+++ b/packages/core/src/indexing/service.ts
@@ -1,7 +1,7 @@
import { E_CANCELED, Mutex } from "async-mutex";
import Emittery from "emittery";
-import type { HandlerFunctions } from "@/build/handlers";
+import type { IndexingFunctions } from "@/build/functions";
import { LogEventMetadata } from "@/config/abi";
import type { Contract } from "@/config/contracts";
import { Factory } from "@/config/factories";
@@ -26,16 +26,16 @@ import { buildReadOnlyContracts } from "./contract";
import { buildModels } from "./model";
import { getStackTrace } from "./trace";
-type EventHandlerEvents = {
+type IndexingEvents = {
eventsProcessed: { toTimestamp: number };
};
type SetupTask = { kind: "SETUP" };
type LogEventTask = { kind: "LOG"; event: LogEvent };
-type EventHandlerTask = SetupTask | LogEventTask;
-type EventHandlerQueue = Queue;
+type IndexingFunctionTask = SetupTask | LogEventTask;
+type IndexingFunctionQueue = Queue;
-export class EventHandlerService extends Emittery {
+export class IndexingService extends Emittery {
private common: Common;
private userStore: UserStore;
private eventAggregatorService: EventAggregatorService;
@@ -47,11 +47,11 @@ export class EventHandlerService extends Emittery {
private schema?: Schema;
private models: Record> = {};
- private handlers?: HandlerFunctions;
- private handledEventMetadata: HandlerFunctions["eventSources"] = {};
+ private indexingFunctions?: IndexingFunctions;
+ private indexingMetadata: IndexingFunctions["eventSources"] = {};
private eventProcessingMutex: Mutex;
- private queue?: EventHandlerQueue;
+ private queue?: IndexingFunctionQueue;
private eventsProcessedToTimestamp = 0;
private hasError = false;
@@ -99,23 +99,23 @@ export class EventHandlerService extends Emittery {
this.eventProcessingMutex.cancel();
this.common.logger.debug({
- service: "handlers",
- msg: `Killed user handler service`,
+ service: "indexing",
+ msg: `Killed indexing service`,
});
};
/**
- * Registers a new set of handler functions and/or a new schema, cancels
+ * Registers a new set of indexing functions and/or a new schema, cancels
* the current event processing mutex & event queue, drops and re-creates
* all tables from the user store, and resets eventsProcessedToTimestamp to zero.
*
* Note: Caller should (probably) immediately call processEvents after this method.
*/
reset = async ({
- handlers: newHandlers,
+ indexingFunctions: newIndexingFunctions,
schema: newSchema,
}: {
- handlers?: HandlerFunctions;
+ indexingFunctions?: IndexingFunctions;
schema?: Schema;
} = {}) => {
if (newSchema) {
@@ -128,47 +128,49 @@ export class EventHandlerService extends Emittery {
});
}
- if (newHandlers) {
- this.handlers = newHandlers;
- this.handledEventMetadata = this.handlers.eventSources;
+ if (newIndexingFunctions) {
+ this.indexingFunctions = newIndexingFunctions;
+ this.indexingMetadata = this.indexingFunctions.eventSources;
}
- // If either the schema or handlers have not been provided yet,
+ // If either the schema or indexing functions have not been provided yet,
// we're not ready to process events. Just return early.
- if (!this.schema || !this.handlers) return;
+ if (!this.schema || !this.indexingFunctions) return;
// Cancel all pending calls to processEvents and reset the mutex.
this.eventProcessingMutex.cancel();
this.eventProcessingMutex = new Mutex();
- // Pause the old queue, (maybe) wait for the current event handler to finish,
- // then create a new queue using the new handlers.
+ // Pause the old queue, (maybe) wait for the current indexing function to finish,
+ // then create a new queue using the new indexing functions.
this.queue?.clear();
this.queue?.pause();
await this.queue?.onIdle();
- this.queue = this.createEventQueue({ handlers: this.handlers });
+ this.queue = this.createEventQueue({
+ indexingFunctions: this.indexingFunctions,
+ });
this.common.logger.debug({
- service: "handlers",
+ service: "indexing",
msg: `Paused event queue (versionId=${this.userStore.versionId})`,
});
this.hasError = false;
- this.common.metrics.ponder_handlers_has_error.set(0);
+ this.common.metrics.ponder_indexing_has_error.set(0);
- this.common.metrics.ponder_handlers_matched_events.reset();
- this.common.metrics.ponder_handlers_handled_events.reset();
- this.common.metrics.ponder_handlers_processed_events.reset();
+ this.common.metrics.ponder_indexing_matched_events.reset();
+ this.common.metrics.ponder_indexing_handled_events.reset();
+ this.common.metrics.ponder_indexing_processed_events.reset();
await this.userStore.reload({ schema: this.schema });
this.common.logger.debug({
- service: "handlers",
+ service: "indexing",
msg: `Reset user store (versionId=${this.userStore.versionId})`,
});
// When we call userStore.reload() above, the user store is dropped.
// Set the latest processed timestamp to zero accordingly.
this.eventsProcessedToTimestamp = 0;
- this.common.metrics.ponder_handlers_latest_processed_timestamp.set(0);
+ this.common.metrics.ponder_indexing_latest_processed_timestamp.set(0);
};
/**
@@ -201,7 +203,7 @@ export class EventHandlerService extends Emittery {
if (this.eventsProcessedToTimestamp <= commonAncestorTimestamp) {
// No unsafe events have been processed, so no need to revert (case 1 & case 2).
this.common.logger.debug({
- service: "handlers",
+ service: "indexing",
msg: `No unsafe events were detected while reconciling a reorg, no-op`,
});
} else {
@@ -212,16 +214,16 @@ export class EventHandlerService extends Emittery {
});
this.eventsProcessedToTimestamp = commonAncestorTimestamp;
- this.common.metrics.ponder_handlers_latest_processed_timestamp.set(
+ this.common.metrics.ponder_indexing_latest_processed_timestamp.set(
commonAncestorTimestamp
);
// Note: There's currently no way to know how many events are "thrown out"
// during the reorg reconciliation, so the event count metrics
- // (e.g. ponder_handlers_processed_events) will be slightly inflated.
+ // (e.g. ponder_indexing_processed_events) will be slightly inflated.
this.common.logger.debug({
- service: "handlers",
+ service: "indexing",
msg: `Reverted user store to safe timestamp ${commonAncestorTimestamp}`,
});
}
@@ -266,12 +268,12 @@ export class EventHandlerService extends Emittery {
// If no events have been added yet, add the setup event & associated metrics.
if (this.eventsProcessedToTimestamp === 0) {
- this.common.metrics.ponder_handlers_matched_events.inc({
+ this.common.metrics.ponder_indexing_matched_events.inc({
eventName: "setup",
});
- if (this.handlers?._meta_.setup) {
+ if (this.indexingFunctions?._meta_.setup) {
this.queue.addTask({ kind: "SETUP" });
- this.common.metrics.ponder_handlers_handled_events.inc({
+ this.common.metrics.ponder_indexing_handled_events.inc({
eventName: "setup",
});
}
@@ -280,7 +282,7 @@ export class EventHandlerService extends Emittery {
const iterator = this.eventAggregatorService.getEvents({
fromTimestamp,
toTimestamp,
- handledEventMetadata: this.handledEventMetadata,
+ indexingMetadata: this.indexingMetadata,
});
let pageIndex = 0;
@@ -288,7 +290,7 @@ export class EventHandlerService extends Emittery {
for await (const page of iterator) {
const { events, metadata } = page;
- // Increment the metrics for the total number of matching & handled events in this timestamp range.
+ // Increment the metrics for the total number of matching & indexed events in this timestamp range.
if (pageIndex === 0) {
metadata.counts.forEach(({ eventSourceName, selector, count }) => {
const safeName = Object.values({
@@ -303,16 +305,15 @@ export class EventHandlerService extends Emittery {
if (!safeName) return;
const isHandled =
- !!this.handlers?.eventSources[eventSourceName]?.bySelector?.[
- selector
- ];
+ !!this.indexingFunctions?.eventSources[eventSourceName]
+ ?.bySelector?.[selector];
- this.common.metrics.ponder_handlers_matched_events.inc(
+ this.common.metrics.ponder_indexing_matched_events.inc(
{ eventName: `${eventSourceName}:${safeName}` },
count
);
if (isHandled) {
- this.common.metrics.ponder_handlers_handled_events.inc(
+ this.common.metrics.ponder_indexing_handled_events.inc(
{ eventName: `${eventSourceName}:${safeName}` },
count
);
@@ -340,7 +341,7 @@ export class EventHandlerService extends Emittery {
if (events.length > 0) {
this.common.logger.info({
- service: "handlers",
+ service: "indexing",
msg: `Processed ${
events.length === 1 ? "1 event" : `${events.length} events`
} (up to ${formatShortDate(metadata.pageEndsAtTimestamp)})`,
@@ -353,9 +354,9 @@ export class EventHandlerService extends Emittery {
this.emit("eventsProcessed", { toTimestamp });
this.eventsProcessedToTimestamp = toTimestamp;
- // Note that this happens both here and in the log event handler function.
+ // Note that this happens both here and in the log event indexing function.
// They must also happen here to handle the case where no events were processed.
- this.common.metrics.ponder_handlers_latest_processed_timestamp.set(
+ this.common.metrics.ponder_indexing_latest_processed_timestamp.set(
toTimestamp
);
});
@@ -366,41 +367,45 @@ export class EventHandlerService extends Emittery {
}
};
- private createEventQueue = ({ handlers }: { handlers: HandlerFunctions }) => {
+ private createEventQueue = ({
+ indexingFunctions,
+ }: {
+ indexingFunctions: IndexingFunctions;
+ }) => {
const context = {
contracts: this.readOnlyContracts,
entities: this.models,
};
- const eventHandlerWorker: Worker = async ({
+ const indexingFunctionWorker: Worker = async ({
task,
queue,
}) => {
- // This is a hack to ensure that the eventsProcessed handler is called and updates
+ // This is a hack to ensure that the eventsProcessed method is called and updates
// the UI when using SQLite. It also allows the process to GC and handle SIGINT events.
// It does, however, slow down event processing a bit.
await wait(0);
switch (task.kind) {
case "SETUP": {
- const setupHandler = handlers._meta_.setup?.fn;
- if (!setupHandler) return;
+ const setupFunction = indexingFunctions._meta_.setup?.fn;
+ if (!setupFunction) return;
try {
this.common.logger.trace({
- service: "handlers",
- msg: `Started handler (event="setup")`,
+ service: "indexing",
+ msg: `Started indexing function (event="setup")`,
});
// Running user code here!
- await setupHandler({ context });
+ await setupFunction({ context });
this.common.logger.trace({
- service: "handlers",
- msg: `Completed handler (event="setup")`,
+ service: "indexing",
+ msg: `Completed indexing function (event="setup")`,
});
- this.common.metrics.ponder_handlers_processed_events.inc({
+ this.common.metrics.ponder_indexing_processed_events.inc({
eventName: "setup",
});
} catch (error_) {
@@ -408,17 +413,17 @@ export class EventHandlerService extends Emittery {
queue.clear();
this.hasError = true;
- this.common.metrics.ponder_handlers_has_error.set(1);
+ this.common.metrics.ponder_indexing_has_error.set(1);
this.common.logger.trace({
- service: "handlers",
- msg: `Failed while running handler (event="setup")`,
+ service: "indexing",
+ msg: `Failed while running indexing function (event="setup")`,
});
const error = error_ as Error;
const trace = getStackTrace(error, this.common.options);
- const message = `Error while handling "setup" event: ${error.message}`;
+ const message = `Error while processing "setup" event: ${error.message}`;
const userError = new UserError(message, {
stack: trace,
@@ -426,7 +431,7 @@ export class EventHandlerService extends Emittery {
});
this.common.logger.error({
- service: "handlers",
+ service: "indexing",
error: userError,
});
this.common.errors.submitUserError({ error: userError });
@@ -437,28 +442,27 @@ export class EventHandlerService extends Emittery {
case "LOG": {
const event = task.event;
- const handlerData =
- this.handlers?.eventSources[event.eventSourceName].bySafeName[
- event.eventName
- ];
- if (!handlerData)
+ const indexingMetadata =
+ this.indexingFunctions?.eventSources[event.eventSourceName]
+ .bySafeName[event.eventName];
+ if (!indexingMetadata)
throw new Error(
- `Internal: Handler not found for event source ${event.eventSourceName}`
+ `Internal: Indexing function not found for event source ${event.eventSourceName}`
);
// This enables contract calls occurring within the
- // handler code to use the event block number by default.
+ // user code to use the event block number by default.
this.currentEventBlockNumber = event.block.number;
this.currentEventTimestamp = Number(event.block.timestamp);
try {
this.common.logger.trace({
- service: "handlers",
- msg: `Started handler (event="${event.eventSourceName}:${event.eventName}", block=${event.block.number})`,
+ service: "indexing",
+ msg: `Started indexing function (event="${event.eventSourceName}:${event.eventName}", block=${event.block.number})`,
});
// Running user code here!
- await handlerData.fn({
+ await indexingMetadata.fn({
event: {
...event,
name: event.eventName,
@@ -467,25 +471,25 @@ export class EventHandlerService extends Emittery {
});
this.common.logger.trace({
- service: "handlers",
- msg: `Completed handler (event="${event.eventSourceName}:${event.eventName}", block=${event.block.number})`,
+ service: "indexing",
+ msg: `Completed indexing function (event="${event.eventSourceName}:${event.eventName}", block=${event.block.number})`,
});
} catch (error_) {
// Remove all remaining tasks from the queue.
queue.clear();
this.hasError = true;
- this.common.metrics.ponder_handlers_has_error.set(1);
+ this.common.metrics.ponder_indexing_has_error.set(1);
this.common.logger.trace({
- service: "handlers",
- msg: `Failed while running handler (event="${event.eventSourceName}:${event.eventName}", block=${event.block.number})`,
+ service: "indexing",
+ msg: `Failed while running indexing function (event="${event.eventSourceName}:${event.eventName}", block=${event.block.number})`,
});
const error = error_ as Error;
const trace = getStackTrace(error, this.common.options);
- const message = `Error while handling "${event.eventSourceName}:${
+ const message = `Error while processing "${event.eventSourceName}:${
event.eventName
}" event at block ${Number(event.block.number)}: ${error.message}`;
@@ -498,16 +502,16 @@ export class EventHandlerService extends Emittery {
});
this.common.logger.error({
- service: "handlers",
+ service: "indexing",
error: userError,
});
this.common.errors.submitUserError({ error: userError });
}
- this.common.metrics.ponder_handlers_processed_events.inc({
+ this.common.metrics.ponder_indexing_processed_events.inc({
eventName: `${event.eventSourceName}:${event.eventName}`,
});
- this.common.metrics.ponder_handlers_latest_processed_timestamp.set(
+ this.common.metrics.ponder_indexing_latest_processed_timestamp.set(
this.currentEventTimestamp
);
@@ -517,7 +521,7 @@ export class EventHandlerService extends Emittery {
};
const queue = createQueue({
- worker: eventHandlerWorker,
+ worker: indexingFunctionWorker,
context: undefined,
options: {
concurrency: 1,
diff --git a/packages/core/src/user-handlers/trace.ts b/packages/core/src/indexing/trace.ts
similarity index 100%
rename from packages/core/src/user-handlers/trace.ts
rename to packages/core/src/indexing/trace.ts
diff --git a/packages/core/src/metrics/service.ts b/packages/core/src/metrics/service.ts
index 1b92a3a26..3edc173e8 100644
--- a/packages/core/src/metrics/service.ts
+++ b/packages/core/src/metrics/service.ts
@@ -33,11 +33,11 @@ export class MetricsService {
"network" | "method"
>;
- ponder_handlers_matched_events: prometheus.Gauge<"eventName">;
- ponder_handlers_handled_events: prometheus.Gauge<"eventName">;
- ponder_handlers_processed_events: prometheus.Gauge<"eventName">;
- ponder_handlers_has_error: prometheus.Gauge;
- ponder_handlers_latest_processed_timestamp: prometheus.Gauge;
+ ponder_indexing_matched_events: prometheus.Gauge<"eventName">;
+ ponder_indexing_handled_events: prometheus.Gauge<"eventName">;
+ ponder_indexing_processed_events: prometheus.Gauge<"eventName">;
+ ponder_indexing_has_error: prometheus.Gauge;
+ ponder_indexing_latest_processed_timestamp: prometheus.Gauge;
ponder_server_port: prometheus.Gauge;
ponder_server_request_size: prometheus.Histogram<
@@ -122,31 +122,31 @@ export class MetricsService {
registers: [this.registry],
});
- this.ponder_handlers_matched_events = new prometheus.Gauge({
- name: "ponder_handlers_matched_events",
+ this.ponder_indexing_matched_events = new prometheus.Gauge({
+ name: "ponder_indexing_matched_events",
help: "Number of available events for all log filters",
labelNames: ["eventName"] as const,
registers: [this.registry],
});
- this.ponder_handlers_handled_events = new prometheus.Gauge({
- name: "ponder_handlers_handled_events",
- help: "Number of available events for which there is a handler function registered",
+ this.ponder_indexing_handled_events = new prometheus.Gauge({
+ name: "ponder_indexing_handled_events",
+ help: "Number of available events for which there is an indexing function registered",
labelNames: ["eventName"] as const,
registers: [this.registry],
});
- this.ponder_handlers_processed_events = new prometheus.Gauge({
- name: "ponder_handlers_processed_events",
+ this.ponder_indexing_processed_events = new prometheus.Gauge({
+ name: "ponder_indexing_processed_events",
help: "Number of available events that have been processed",
labelNames: ["eventName"] as const,
registers: [this.registry],
});
- this.ponder_handlers_has_error = new prometheus.Gauge({
- name: "ponder_handlers_has_error",
- help: "Boolean (0 or 1) indicating if an error was encountered while running handlers",
+ this.ponder_indexing_has_error = new prometheus.Gauge({
+ name: "ponder_indexing_has_error",
+ help: "Boolean (0 or 1) indicating if an error was encountered while running user code",
registers: [this.registry],
});
- this.ponder_handlers_latest_processed_timestamp = new prometheus.Gauge({
- name: "ponder_handlers_latest_processed_timestamp",
+ this.ponder_indexing_latest_processed_timestamp = new prometheus.Gauge({
+ name: "ponder_indexing_latest_processed_timestamp",
help: "Block timestamp of the latest processed event",
registers: [this.registry],
});
diff --git a/packages/core/src/ui/HandlersBar.tsx b/packages/core/src/ui/IndexingBar.tsx
similarity index 65%
rename from packages/core/src/ui/HandlersBar.tsx
rename to packages/core/src/ui/IndexingBar.tsx
index 2a56bea8c..608e4f869 100644
--- a/packages/core/src/ui/HandlersBar.tsx
+++ b/packages/core/src/ui/IndexingBar.tsx
@@ -6,25 +6,25 @@ import { formatShortDate } from "@/utils/date";
import type { UiState } from "./app";
import { ProgressBar } from "./ProgressBar";
-export const HandlersBar = ({ ui }: { ui: UiState }) => {
+export const IndexingBar = ({ ui }: { ui: UiState }) => {
const completionRate =
- ui.handlersCurrent / Math.max(ui.handlersHandledTotal, 1);
+ ui.processedEventCount / Math.max(ui.totalMatchedEventCount, 1);
const completionDecimal = Math.round(completionRate * 1000) / 10;
const completionText =
Number.isInteger(completionDecimal) && completionDecimal < 100
? `${completionDecimal}.0%`
: `${completionDecimal}%`;
- const isStarted = ui.handlersTotal > 0;
+ const isStarted = ui.totalEventCount > 0;
const isHistoricalSyncComplete = ui.isHistoricalSyncComplete;
- const isUpToDate = ui.handlersCurrent === ui.handlersHandledTotal;
+ const isUpToDate = ui.processedEventCount === ui.totalMatchedEventCount;
const titleText = () => {
if (!isStarted) return (not started) ;
if (!isHistoricalSyncComplete || !isUpToDate) {
return (
- (up to {formatShortDate(ui.handlersToTimestamp)})
+ (up to {formatShortDate(ui.eventsProcessedToTimestamp)})
);
}
@@ -37,17 +37,17 @@ export const HandlersBar = ({ ui }: { ui: UiState }) => {
return (
{" "}
- | {ui.handlersCurrent}/
- {"?".repeat(ui.handlersCurrent.toString().length)} events (
- {ui.handlersTotal} total)
+ | {ui.processedEventCount}/
+ {"?".repeat(ui.processedEventCount.toString().length)} events (
+ {ui.totalEventCount} total)
);
}
return (
{" "}
- | {ui.handlersCurrent}/{ui.handlersHandledTotal} events (
- {ui.handlersTotal} total)
+ | {ui.processedEventCount}/{ui.totalMatchedEventCount} events (
+ {ui.totalEventCount} total)
);
};
@@ -55,13 +55,13 @@ export const HandlersBar = ({ ui }: { ui: UiState }) => {
return (
- Event handlers
+ Indexing
{titleText()}
{" "}
diff --git a/packages/core/src/ui/app.tsx b/packages/core/src/ui/app.tsx
index c53d6cfce..f3b43ec34 100644
--- a/packages/core/src/ui/app.tsx
+++ b/packages/core/src/ui/app.tsx
@@ -4,8 +4,8 @@ import React from "react";
import { Factory } from "@/config/factories";
import type { LogFilter } from "@/config/logFilters";
-import { HandlersBar } from "./HandlersBar";
import { HistoricalBar } from "./HistoricalBar";
+import { IndexingBar } from "./IndexingBar";
export type UiState = {
port: number;
@@ -14,14 +14,13 @@ export type UiState = {
string,
{ rate: number; eta?: number }
>;
-
isHistoricalSyncComplete: boolean;
- handlerError: boolean;
- handlersCurrent: number;
- handlersTotal: number;
- handlersHandledTotal: number;
- handlersToTimestamp: number;
+ indexingError: boolean;
+ processedEventCount: number;
+ totalEventCount: number;
+ totalMatchedEventCount: number;
+ eventsProcessedToTimestamp: number;
networks: string[];
};
@@ -40,11 +39,11 @@ export const buildUiState = ({
isHistoricalSyncComplete: false,
- handlerError: false,
- handlersCurrent: 0,
- handlersTotal: 0,
- handlersHandledTotal: 0,
- handlersToTimestamp: 0,
+ indexingError: false,
+ processedEventCount: 0,
+ totalEventCount: 0,
+ totalMatchedEventCount: 0,
+ eventsProcessedToTimestamp: 0,
networks: [],
};
@@ -68,12 +67,12 @@ const App = (ui: UiState) => {
port,
historicalSyncEventSourceStats,
isHistoricalSyncComplete,
- handlersCurrent,
- handlerError,
+ processedEventCount,
+ indexingError,
networks,
} = ui;
- if (handlerError) {
+ if (indexingError) {
return (
@@ -114,7 +113,7 @@ const App = (ui: UiState) => {
)}
-
+
{networks.length > 0 && (
@@ -130,7 +129,7 @@ const App = (ui: UiState) => {
)}
- {handlersCurrent > 0 && (
+ {processedEventCount > 0 && (
GraphQL
diff --git a/packages/core/src/ui/service.ts b/packages/core/src/ui/service.ts
index fdf906284..301c64de0 100644
--- a/packages/core/src/ui/service.ts
+++ b/packages/core/src/ui/service.ts
@@ -86,27 +86,27 @@ export class UiService {
this.ui.networks = connectedNetworks;
- // Handlers
- const matchedEvents = (
- await this.common.metrics.ponder_handlers_matched_events.get()
+ // Indexing
+ const matchedEventCount = (
+ await this.common.metrics.ponder_indexing_matched_events.get()
).values.reduce((a, v) => a + v.value, 0);
- const handledEvents = (
- await this.common.metrics.ponder_handlers_handled_events.get()
+ const totalEventCount = (
+ await this.common.metrics.ponder_indexing_handled_events.get()
).values.reduce((a, v) => a + v.value, 0);
- const processedEvents = (
- await this.common.metrics.ponder_handlers_processed_events.get()
+ const processedEventCount = (
+ await this.common.metrics.ponder_indexing_processed_events.get()
).values.reduce((a, v) => a + v.value, 0);
const latestProcessedTimestamp =
(
- await this.common.metrics.ponder_handlers_latest_processed_timestamp.get()
+ await this.common.metrics.ponder_indexing_latest_processed_timestamp.get()
).values[0].value ?? 0;
- this.ui.handlersTotal = matchedEvents;
- this.ui.handlersHandledTotal = handledEvents;
- this.ui.handlersCurrent = processedEvents;
- this.ui.handlersToTimestamp = latestProcessedTimestamp;
+ this.ui.totalMatchedEventCount = matchedEventCount;
+ this.ui.totalEventCount = totalEventCount;
+ this.ui.processedEventCount = processedEventCount;
+ this.ui.eventsProcessedToTimestamp = latestProcessedTimestamp;
// Errors
- this.ui.handlerError = this.common.errors.hasUserError;
+ this.ui.indexingError = this.common.errors.hasUserError;
// Server
const port = (await this.common.metrics.ponder_server_port.get())
diff --git a/packages/core/src/user-store/postgres/store.ts b/packages/core/src/user-store/postgres/store.ts
index a3661a47e..859dc8c6c 100644
--- a/packages/core/src/user-store/postgres/store.ts
+++ b/packages/core/src/user-store/postgres/store.ts
@@ -447,8 +447,8 @@ export class PostgresUserStore implements UserStore {
const instance = await this.db.transaction().execute(async (tx) => {
// If the latest version is effective from the delete timestamp,
// then delete the instance in place. It "never existed".
- // This needs to be done first, because an update() earlier in the handler
- // call would have created a new version with the delete timestamp.
+ // This needs to be done first, because an update() earlier in the
+ // indexing function would have created a new version with the delete timestamp.
// Attempting to update first would result in a constraint violation.
let deletedInstance = await tx
.deleteFrom(tableName)
diff --git a/packages/core/src/user-store/sqlite/store.ts b/packages/core/src/user-store/sqlite/store.ts
index 00bff13d3..4092d5644 100644
--- a/packages/core/src/user-store/sqlite/store.ts
+++ b/packages/core/src/user-store/sqlite/store.ts
@@ -428,8 +428,8 @@ export class SqliteUserStore implements UserStore {
const instance = await this.db.transaction().execute(async (tx) => {
// If the latest version is effective from the delete timestamp,
// then delete the instance in place. It "never existed".
- // This needs to be done first, because an update() earlier in the handler
- // call would have created a new version with the delete timestamp.
+ // This needs to be done first, because an update() earlier in the
+ // indexing function would have created a new version with the delete timestamp.
// Attempting to update first would result in a constraint violation.
let deletedInstance = await tx
.deleteFrom(tableName)
diff --git a/packages/create-ponder/README.md b/packages/create-ponder/README.md
index 96ba24377..5044809fa 100644
--- a/packages/create-ponder/README.md
+++ b/packages/create-ponder/README.md
@@ -26,14 +26,15 @@ Join [Ponder's telegram chat](https://t.me/ponder_sh) for support, feedback, and
✅ Supports all Ethereum-based blockchains, including test nodes like [Anvil](https://book.getfoundry.sh/anvil)
✅ Index events from multiple chains in the same app
✅ Reconciles chain reorganization
-🏗️ Transaction call event handlers
-🏗️ Support for factory contracts like Uniswap V2/V3
+✅ Factory contracts
+🏗️ Process transactions calls (in addition to logs)
+🏗️ Run effects (e.g. send an API request) in indexing code
## Quickstart
### 1. Run `create-ponder`
-You will be asked for a project name, and if you are using an Etherscan or Graph Protocol [template](https://ponder.sh/api-reference/create-ponder) (recommended).
+You will be asked for a project name, and if you are using a [template](https://ponder.sh/api-reference/create-ponder#templates) (recommended). Then, the CLI will create a project directory, install dependencies, and initialize a git repository.
```bash
npm init ponder@latest
@@ -45,7 +46,7 @@ yarn create ponder
### 2. Start the development server
-The development server automatically reloads your app when you save changes in any project file, and prints `console.log` statements and errors in your code.
+Just like Next.js and Vite, Ponder has a development server that automatically reloads when you save changes in any project file. It also prints `console.log` statements and errors encountered while running your code. First, `cd` into your project directory, then start the server.
```bash
npm run dev
@@ -57,7 +58,7 @@ yarn dev
### 3. Add contracts & networks
-Ponder fetches event logs for the contracts added to `ponder.config.ts`, and passes those events to the handler functions you write.
+Ponder fetches event logs for the contracts added to `ponder.config.ts`, and passes those events to the indexing functions you write.
```ts
// ponder.config.ts
@@ -85,7 +86,7 @@ export const config = {
### 4. Define your schema
-The `schema.graphql` file specifies the shape of your application's data.
+The `schema.graphql` file contains a model of your application data. The entity types defined here correspond to database tables.
```ts
// schema.graphql
@@ -98,9 +99,9 @@ type EnsName @entity {
}
```
-### 5. Write event handlers
+### 5. Write indexing functions
-Use event handler functions to convert raw blockchain events into application data.
+Files in the `src/` directory contain **indexing functions**, which are TypeScript functions that process a contract event. The purpose of these functions is to insert data into the entity store.
```ts
// src/BaseRegistrar.ts
@@ -122,9 +123,11 @@ ponder.on("BaseRegistrar:NameRegistered", async ({ event, context }) => {
});
```
+See the [create & update entities](https://ponder.sh/guides/create-update-entities) docs for a detailed guide on writing indexing functions.
+
### 6. Query the GraphQL API
-Ponder automatically generates a frontend-ready GraphQL API based on your project's `schema.graphql`. The API will serve the data that you inserted in your event handler functions.
+Ponder automatically generates a frontend-ready GraphQL API based on your project's `schema.graphql`. The API serves the data that you inserted in your indexing functions.
```ts
{
diff --git a/packages/create-ponder/src/index.ts b/packages/create-ponder/src/index.ts
index 90bd2804b..38f6d8f45 100644
--- a/packages/create-ponder/src/index.ts
+++ b/packages/create-ponder/src/index.ts
@@ -88,7 +88,7 @@ export const run = async (
}
}
- // Write the handler ts files.
+ // Write the indexing function files.
config.contracts.forEach((contract) => {
let abi: Abi;
if (Array.isArray(contract.abi)) {
@@ -110,7 +110,7 @@ export const run = async (
const eventNamesToWrite = abiEvents.map((event) => event.name).slice(0, 2);
- const handlerFileContents = `
+ const indexingFunctionFileContents = `
import { ponder } from '@/generated'
${eventNamesToWrite
@@ -125,7 +125,7 @@ export const run = async (
writeFileSync(
path.join(rootDir, `./src/${contract.name}.ts`),
- prettier.format(handlerFileContents, { parser: "typescript" })
+ prettier.format(indexingFunctionFileContents, { parser: "typescript" })
);
});
diff --git a/packages/create-ponder/src/templates/basic.ts b/packages/create-ponder/src/templates/basic.ts
index 3d0c4ea47..59ed635ea 100644
--- a/packages/create-ponder/src/templates/basic.ts
+++ b/packages/create-ponder/src/templates/basic.ts
@@ -13,7 +13,7 @@ export const fromBasic = ({ rootDir }: { rootDir: string }) => {
const schemaGraphqlFileContents = `
# The entity types defined below map to database tables.
- # The functions you write as event handlers inside the \`src/\` directory are responsible for creating and updating records in those tables.
+ # The functions you write in the \`src/\` directory are responsible for creating and updating records in these tables.
# Your schema will be more flexible and powerful if it accurately models the logical relationships in your application's domain.
# Visit the [documentation](https://ponder.sh/guides/design-your-schema) or the [\`examples/\`](https://github.com/0xOlias/ponder/tree/main/examples) directory for further guidance on designing your schema.
diff --git a/packages/eslint-config-ponder/README.md b/packages/eslint-config-ponder/README.md
index 37ec8817a..18b46f253 100644
--- a/packages/eslint-config-ponder/README.md
+++ b/packages/eslint-config-ponder/README.md
@@ -26,14 +26,15 @@ Join [Ponder's telegram chat](https://t.me/ponder_sh) for support, feedback, and
✅ Supports all Ethereum-based blockchains, including test nodes like [Anvil](https://book.getfoundry.sh/anvil)
✅ Index events from multiple chains in the same app
✅ Reconciles chain reorganization
-🏗️ Transaction call event handlers
-🏗️ Support for factory contracts like Uniswap V2/V3
+✅ Factory contracts
+🏗️ Process transactions calls (in addition to logs)
+🏗️ Run effects (e.g. send an API request) in indexing code
## Quickstart
### 1. Run `create-ponder`
-You will be asked for a project name, and if you are using an Etherscan or Graph Protocol [template](https://ponder.sh/api-reference/create-ponder) (recommended).
+You will be asked for a project name, and if you are using a [template](https://ponder.sh/api-reference/create-ponder#templates) (recommended). Then, the CLI will create a project directory, install dependencies, and initialize a git repository.
```bash
npm init ponder@latest
@@ -45,7 +46,7 @@ yarn create ponder
### 2. Start the development server
-The development server automatically reloads your app when you save changes in any project file, and prints `console.log` statements and errors in your code.
+Just like Next.js and Vite, Ponder has a development server that automatically reloads when you save changes in any project file. It also prints `console.log` statements and errors encountered while running your code. First, `cd` into your project directory, then start the server.
```bash
npm run dev
@@ -57,7 +58,7 @@ yarn dev
### 3. Add contracts & networks
-Ponder fetches event logs for the contracts added to `ponder.config.ts`, and passes those events to the handler functions you write.
+Ponder fetches event logs for the contracts added to `ponder.config.ts`, and passes those events to the indexing functions you write.
```ts
// ponder.config.ts
@@ -85,7 +86,7 @@ export const config = {
### 4. Define your schema
-The `schema.graphql` file specifies the shape of your application's data.
+The `schema.graphql` file contains a model of your application data. The entity types defined here correspond to database tables.
```ts
// schema.graphql
@@ -98,9 +99,9 @@ type EnsName @entity {
}
```
-### 5. Write event handlers
+### 5. Write indexing functions
-Use event handler functions to convert raw blockchain events into application data.
+Files in the `src/` directory contain **indexing functions**, which are TypeScript functions that process a contract event. The purpose of these functions is to insert data into the entity store.
```ts
// src/BaseRegistrar.ts
@@ -122,9 +123,11 @@ ponder.on("BaseRegistrar:NameRegistered", async ({ event, context }) => {
});
```
+See the [create & update entities](https://ponder.sh/guides/create-update-entities) docs for a detailed guide on writing indexing functions.
+
### 6. Query the GraphQL API
-Ponder automatically generates a frontend-ready GraphQL API based on your project's `schema.graphql`. The API will serve the data that you inserted in your event handler functions.
+Ponder automatically generates a frontend-ready GraphQL API based on your project's `schema.graphql`. The API serves the data that you inserted in your indexing functions.
```ts
{
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index bd362b865..09e1d6cb9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -92,11 +92,11 @@ importers:
specifier: ^13.4.12
version: 13.4.12(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0)
nextra:
- specifier: ^2.10.0
- version: 2.10.0(next@13.4.12)(react-dom@18.2.0)(react@18.2.0)
+ specifier: ^2.13.2
+ version: 2.13.2(next@13.4.12)(react-dom@18.2.0)(react@18.2.0)
nextra-theme-docs:
- specifier: ^2.10.0
- version: 2.10.0(next@13.4.12)(nextra@2.10.0)(react-dom@18.2.0)(react@18.2.0)
+ specifier: ^2.13.2
+ version: 2.13.2(next@13.4.12)(nextra@2.13.2)(react-dom@18.2.0)(react@18.2.0)
react:
specifier: ^18.1.0
version: 18.2.0
@@ -2857,8 +2857,8 @@ packages:
'@types/react': 18.0.25
react: 18.2.0
- /@napi-rs/simple-git-android-arm-eabi@0.1.8:
- resolution: {integrity: sha512-JJCejHBB1G6O8nxjQLT4quWCcvLpC3oRdJJ9G3MFYSCoYS8i1bWCWeU+K7Br+xT+D6s1t9q8kNJAwJv9Ygpi0g==}
+ /@napi-rs/simple-git-android-arm-eabi@0.1.9:
+ resolution: {integrity: sha512-9D4JnfePMpgL4pg9aMUX7/TIWEUQ+Tgx8n3Pf8TNCMGjUbImJyYsDSLJzbcv9wH7srgn4GRjSizXFJHAPjzEug==}
engines: {node: '>= 10'}
cpu: [arm]
os: [android]
@@ -2866,8 +2866,8 @@ packages:
dev: false
optional: true
- /@napi-rs/simple-git-android-arm64@0.1.8:
- resolution: {integrity: sha512-mraHzwWBw3tdRetNOS5KnFSjvdAbNBnjFLA8I4PwTCPJj3Q4txrigcPp2d59cJ0TC51xpnPXnZjYdNwwSI9g6g==}
+ /@napi-rs/simple-git-android-arm64@0.1.9:
+ resolution: {integrity: sha512-Krilsw0gPrrASZzudNEl9pdLuNbhoTK0j7pUbfB8FRifpPdFB/zouwuEm0aSnsDXN4ftGrmGG82kuiR/2MeoPg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
@@ -2875,8 +2875,8 @@ packages:
dev: false
optional: true
- /@napi-rs/simple-git-darwin-arm64@0.1.8:
- resolution: {integrity: sha512-ufy/36eI/j4UskEuvqSH7uXtp3oXeLDmjQCfKJz3u5Vx98KmOMKrqAm2H81AB2WOtCo5mqS6PbBeUXR8BJX8lQ==}
+ /@napi-rs/simple-git-darwin-arm64@0.1.9:
+ resolution: {integrity: sha512-H/F09nDgYjv4gcFrZBgdTKkZEepqt0KLYcCJuUADuxkKupmjLdecMhypXLk13AzvLW4UQI7NlLTLDXUFLyr2BA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
@@ -2884,8 +2884,8 @@ packages:
dev: false
optional: true
- /@napi-rs/simple-git-darwin-x64@0.1.8:
- resolution: {integrity: sha512-Vb21U+v3tPJNl+8JtIHHT8HGe6WZ8o1Tq3f6p+Jx9Cz71zEbcIiB9FCEMY1knS/jwQEOuhhlI9Qk7d4HY+rprA==}
+ /@napi-rs/simple-git-darwin-x64@0.1.9:
+ resolution: {integrity: sha512-jBR2xS9nVPqmHv0TWz874W0m/d453MGrMeLjB+boK5IPPLhg3AWIZj0aN9jy2Je1BGVAa0w3INIQJtBBeB6kFA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
@@ -2893,8 +2893,8 @@ packages:
dev: false
optional: true
- /@napi-rs/simple-git-linux-arm-gnueabihf@0.1.8:
- resolution: {integrity: sha512-6BPTJ7CzpSm2t54mRLVaUr3S7ORJfVJoCk2rQ8v8oDg0XAMKvmQQxOsAgqKBo9gYNHJnqrOx3AEuEgvB586BuQ==}
+ /@napi-rs/simple-git-linux-arm-gnueabihf@0.1.9:
+ resolution: {integrity: sha512-3n0+VpO4YfZxndZ0sCvsHIvsazd+JmbSjrlTRBCnJeAU1/sfos3skNZtKGZksZhjvd+3o+/GFM8L7Xnv01yggA==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
@@ -2902,8 +2902,8 @@ packages:
dev: false
optional: true
- /@napi-rs/simple-git-linux-arm64-gnu@0.1.8:
- resolution: {integrity: sha512-qfESqUCAA/XoQpRXHptSQ8gIFnETCQt1zY9VOkplx6tgYk9PCeaX4B1Xuzrh3eZamSCMJFn+1YB9Ut8NwyGgAA==}
+ /@napi-rs/simple-git-linux-arm64-gnu@0.1.9:
+ resolution: {integrity: sha512-lIzf0KHU2SKC12vMrWwCtysG2Sdt31VHRPMUiz9lD9t3xwVn8qhFSTn5yDkTeG3rgX6o0p5EKalfQN5BXsJq2w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -2911,8 +2911,8 @@ packages:
dev: false
optional: true
- /@napi-rs/simple-git-linux-arm64-musl@0.1.8:
- resolution: {integrity: sha512-G80BQPpaRmQpn8dJGHp4I2/YVhWDUNJwcCrJAtAdbKFDCMyCHJBln2ERL/+IEUlIAT05zK/c1Z5WEprvXEdXow==}
+ /@napi-rs/simple-git-linux-arm64-musl@0.1.9:
+ resolution: {integrity: sha512-KQozUoNXrxrB8k741ncWXSiMbjl1AGBGfZV21PANzUM8wH4Yem2bg3kfglYS/QIx3udspsT35I9abu49n7D1/w==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -2920,8 +2920,8 @@ packages:
dev: false
optional: true
- /@napi-rs/simple-git-linux-x64-gnu@0.1.8:
- resolution: {integrity: sha512-NI6o1sZYEf6vPtNWJAm9w8BxJt+LlSFW0liSjYe3lc3e4dhMfV240f0ALeqlwdIldRPaDFwZSJX5/QbS7nMzhw==}
+ /@napi-rs/simple-git-linux-x64-gnu@0.1.9:
+ resolution: {integrity: sha512-O/Niui5mnHPcK3iYC3ui8wgERtJWsQ3Y74W/09t0bL/3dgzGMl4oQt0qTj9dWCsnoGsIEYHPzwCBp/2vqYp/pw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -2929,8 +2929,8 @@ packages:
dev: false
optional: true
- /@napi-rs/simple-git-linux-x64-musl@0.1.8:
- resolution: {integrity: sha512-wljGAEOW41er45VTiU8kXJmO480pQKzsgRCvPlJJSCaEVBbmo6XXbFIXnZy1a2J3Zyy2IOsRB4PVkUZaNuPkZQ==}
+ /@napi-rs/simple-git-linux-x64-musl@0.1.9:
+ resolution: {integrity: sha512-L9n+e8Wn3hKr3RsIdY8GaB+ry4xZ4BaGwyKExgoB8nDGQuRUY9oP6p0WA4hWfJvJnU1H6hvo36a5UFPReyBO7A==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -2938,8 +2938,8 @@ packages:
dev: false
optional: true
- /@napi-rs/simple-git-win32-arm64-msvc@0.1.8:
- resolution: {integrity: sha512-QuV4QILyKPfbWHoQKrhXqjiCClx0SxbCTVogkR89BwivekqJMd9UlMxZdoCmwLWutRx4z9KmzQqokvYI5QeepA==}
+ /@napi-rs/simple-git-win32-arm64-msvc@0.1.9:
+ resolution: {integrity: sha512-Z6Ja/SZK+lMvRWaxj7wjnvSbAsGrH006sqZo8P8nxKUdZfkVvoCaAWr1r0cfkk2Z3aijLLtD+vKeXGlUPH6gGQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
@@ -2947,8 +2947,8 @@ packages:
dev: false
optional: true
- /@napi-rs/simple-git-win32-x64-msvc@0.1.8:
- resolution: {integrity: sha512-UzNS4JtjhZhZ5hRLq7BIUq+4JOwt1ThIKv11CsF1ag2l99f0123XvfEpjczKTaa94nHtjXYc2Mv9TjccBqYOew==}
+ /@napi-rs/simple-git-win32-x64-msvc@0.1.9:
+ resolution: {integrity: sha512-VAZj1UvC+R2MjKOD3I/Y7dmQlHWAYy4omhReQJRpbCf+oGCBi9CWiIduGqeYEq723nLIKdxP7XjaO0wl1NnUww==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -2956,21 +2956,21 @@ packages:
dev: false
optional: true
- /@napi-rs/simple-git@0.1.8:
- resolution: {integrity: sha512-BvOMdkkofTz6lEE35itJ/laUokPhr/5ToMGlOH25YnhLD2yN1KpRAT4blW9tT8281/1aZjW3xyi73bs//IrDKA==}
+ /@napi-rs/simple-git@0.1.9:
+ resolution: {integrity: sha512-qKzDS0+VjMvVyU28px+C6zlD1HKy83NIdYzfMQWa/g/V1iG/Ic8uwrS2ihHfm7mp7X0PPrmINLiTTi6ieUIKfw==}
engines: {node: '>= 10'}
optionalDependencies:
- '@napi-rs/simple-git-android-arm-eabi': 0.1.8
- '@napi-rs/simple-git-android-arm64': 0.1.8
- '@napi-rs/simple-git-darwin-arm64': 0.1.8
- '@napi-rs/simple-git-darwin-x64': 0.1.8
- '@napi-rs/simple-git-linux-arm-gnueabihf': 0.1.8
- '@napi-rs/simple-git-linux-arm64-gnu': 0.1.8
- '@napi-rs/simple-git-linux-arm64-musl': 0.1.8
- '@napi-rs/simple-git-linux-x64-gnu': 0.1.8
- '@napi-rs/simple-git-linux-x64-musl': 0.1.8
- '@napi-rs/simple-git-win32-arm64-msvc': 0.1.8
- '@napi-rs/simple-git-win32-x64-msvc': 0.1.8
+ '@napi-rs/simple-git-android-arm-eabi': 0.1.9
+ '@napi-rs/simple-git-android-arm64': 0.1.9
+ '@napi-rs/simple-git-darwin-arm64': 0.1.9
+ '@napi-rs/simple-git-darwin-x64': 0.1.9
+ '@napi-rs/simple-git-linux-arm-gnueabihf': 0.1.9
+ '@napi-rs/simple-git-linux-arm64-gnu': 0.1.9
+ '@napi-rs/simple-git-linux-arm64-musl': 0.1.9
+ '@napi-rs/simple-git-linux-x64-gnu': 0.1.9
+ '@napi-rs/simple-git-linux-x64-musl': 0.1.9
+ '@napi-rs/simple-git-win32-arm64-msvc': 0.1.9
+ '@napi-rs/simple-git-win32-x64-msvc': 0.1.9
dev: false
/@next/env@13.4.12:
@@ -3425,8 +3425,8 @@ packages:
tslib: 2.5.3
dev: false
- /@theguild/remark-mermaid@0.0.4(react@18.2.0):
- resolution: {integrity: sha512-C1gssw07eURtCwzXqZZdvyV/eawQ/cXfARaXIgBU9orffox+/YQ+exxmNu9v16NSGzAVsGF4qEVHvCOcCR/FpQ==}
+ /@theguild/remark-mermaid@0.0.5(react@18.2.0):
+ resolution: {integrity: sha512-e+ZIyJkEv9jabI4m7q29wZtZv+2iwPGsXJ2d46Zi7e+QcFudiyuqhLhHG/3gX3ZEB+hxTch+fpItyMS8jwbIcw==}
peerDependencies:
react: ^18.2.0
dependencies:
@@ -3437,10 +3437,10 @@ packages:
- supports-color
dev: false
- /@theguild/remark-npm2yarn@0.1.1:
- resolution: {integrity: sha512-ZKwd/bjQ9V+pESLnu8+q8jqn15alXzJOuVckraebsXwqVBTw53Gmupiw9zCdLNHU829KTYNycJYea6m9HRLuOg==}
+ /@theguild/remark-npm2yarn@0.2.1:
+ resolution: {integrity: sha512-jUTFWwDxtLEFtGZh/TW/w30ySaDJ8atKWH8dq2/IiQF61dPrGfETpl0WxD0VdBfuLOeU14/kop466oBSRO/5CA==}
dependencies:
- npm-to-yarn: 2.0.0
+ npm-to-yarn: 2.1.0
unist-util-visit: 5.0.0
dev: false
@@ -3586,6 +3586,12 @@ packages:
'@types/unist': 2.0.7
dev: false
+ /@types/hast@3.0.2:
+ resolution: {integrity: sha512-B5hZHgHsXvfCoO3xgNJvBnX7N8p86TqQeGKXcokW4XXi+qY4vxxPSFYofytvVmpFxzPv7oxDQzjg5Un5m2/xiw==}
+ dependencies:
+ '@types/unist': 3.0.0
+ dev: false
+
/@types/is-ci@3.0.0:
resolution: {integrity: sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==}
dependencies:
@@ -3603,10 +3609,6 @@ packages:
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
dev: true
- /@types/katex@0.14.0:
- resolution: {integrity: sha512-+2FW2CcT0K3P+JMR8YG846bmDwplKUTsWgT2ENwdQ1UdVfRk3GQrh6Mi4sTopy30gI8Uau5CEqHTDZ6YvWIUPA==}
- dev: false
-
/@types/katex@0.16.1:
resolution: {integrity: sha512-cwglq2A63Yk082CQk0t8LIoDhZAVgJqkumLyk3grpg3K8sevaDW//Qsspmxj9Sf+97biqt79CfAlPrvizHlP0w==}
dev: false
@@ -3621,6 +3623,12 @@ packages:
'@types/unist': 2.0.7
dev: false
+ /@types/mdast@4.0.2:
+ resolution: {integrity: sha512-tYR83EignvhYO9iU3kDg8V28M0jqyh9zzp5GV+EO+AYnyUl3P5ltkTeJuTiFZQFz670FSb3EwT/6LQdX+UdKfw==}
+ dependencies:
+ '@types/unist': 3.0.0
+ dev: false
+
/@types/mdx@2.0.5:
resolution: {integrity: sha512-76CqzuD6Q7LC+AtbPqrvD9AqsN0k8bsYo2bM2J8pmNldP1aIPAbzUQ7QbobyXL4eLr1wK5x8FZFe8eF/ubRuBg==}
@@ -3965,6 +3973,10 @@ packages:
'@typescript-eslint/types': 6.3.0
eslint-visitor-keys: 3.4.1
+ /@ungap/structured-clone@1.2.0:
+ resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+ dev: false
+
/@viem/anvil@0.0.6:
resolution: {integrity: sha512-OjKR/+FVwzuygXYFqP8MBal1SXG8bT2gbZwqqB0XuLw81LNBBvmE/Repm6+5kkBh4IUj0PhYdrqOsnayS14Gtg==}
dependencies:
@@ -5040,8 +5052,8 @@ packages:
engines: {node: '>=0.8'}
dev: true
- /clsx@1.2.1:
- resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
+ /clsx@2.0.0:
+ resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==}
engines: {node: '>=6'}
dev: false
@@ -5834,6 +5846,12 @@ packages:
execa: 5.1.1
dev: false
+ /devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+ dependencies:
+ dequal: 2.0.3
+ dev: false
+
/dezalgo@1.0.4:
resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
dependencies:
@@ -7685,55 +7703,75 @@ packages:
minimalistic-assert: 1.0.1
dev: true
- /hast-util-from-dom@4.2.0:
- resolution: {integrity: sha512-t1RJW/OpJbCAJQeKi3Qrj1cAOLA0+av/iPFori112+0X7R3wng+jxLA+kXec8K4szqPRGI8vPxbbpEYvvpwaeQ==}
+ /hast-util-from-dom@5.0.0:
+ resolution: {integrity: sha512-d6235voAp/XR3Hh5uy7aGLbM3S4KamdW0WEgOaU1YoewnuYw4HXb5eRtv9g65m/RFGEfUY1Mw4UqCc5Y8L4Stg==}
dependencies:
- hastscript: 7.2.0
+ '@types/hast': 3.0.2
+ hastscript: 8.0.0
web-namespaces: 2.0.1
dev: false
- /hast-util-from-html-isomorphic@1.0.0:
- resolution: {integrity: sha512-Yu480AKeOEN/+l5LA674a+7BmIvtDj24GvOt7MtQWuhzUwlaaRWdEPXAh3Qm5vhuthpAipFb2vTetKXWOjmTvw==}
+ /hast-util-from-html-isomorphic@2.0.0:
+ resolution: {integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==}
dependencies:
- '@types/hast': 2.3.5
- hast-util-from-dom: 4.2.0
- hast-util-from-html: 1.0.2
- unist-util-remove-position: 4.0.2
+ '@types/hast': 3.0.2
+ hast-util-from-dom: 5.0.0
+ hast-util-from-html: 2.0.1
+ unist-util-remove-position: 5.0.0
dev: false
- /hast-util-from-html@1.0.2:
- resolution: {integrity: sha512-LhrTA2gfCbLOGJq2u/asp4kwuG0y6NhWTXiPKP+n0qNukKy7hc10whqqCFfyvIA1Q5U5d0sp9HhNim9gglEH4A==}
+ /hast-util-from-html@2.0.1:
+ resolution: {integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==}
dependencies:
- '@types/hast': 2.3.5
- hast-util-from-parse5: 7.1.2
+ '@types/hast': 3.0.2
+ devlop: 1.1.0
+ hast-util-from-parse5: 8.0.1
parse5: 7.1.2
- vfile: 5.3.7
- vfile-message: 3.1.4
+ vfile: 6.0.1
+ vfile-message: 4.0.2
dev: false
- /hast-util-from-parse5@7.1.2:
- resolution: {integrity: sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==}
+ /hast-util-from-parse5@8.0.1:
+ resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==}
dependencies:
- '@types/hast': 2.3.5
- '@types/unist': 2.0.7
- hastscript: 7.2.0
+ '@types/hast': 3.0.2
+ '@types/unist': 3.0.0
+ devlop: 1.1.0
+ hastscript: 8.0.0
property-information: 6.2.0
- vfile: 5.3.7
- vfile-location: 4.1.0
+ vfile: 6.0.1
+ vfile-location: 5.0.2
web-namespaces: 2.0.1
dev: false
- /hast-util-is-element@2.1.3:
- resolution: {integrity: sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==}
+ /hast-util-is-element@3.0.0:
+ resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
dependencies:
- '@types/hast': 2.3.5
- '@types/unist': 2.0.7
+ '@types/hast': 3.0.2
dev: false
- /hast-util-parse-selector@3.1.1:
- resolution: {integrity: sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==}
+ /hast-util-parse-selector@4.0.0:
+ resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
dependencies:
- '@types/hast': 2.3.5
+ '@types/hast': 3.0.2
+ dev: false
+
+ /hast-util-raw@9.0.1:
+ resolution: {integrity: sha512-5m1gmba658Q+lO5uqL5YNGQWeh1MYWZbZmWrM5lncdcuiXuo5E2HT/CIOp0rLF8ksfSwiCVJ3twlgVRyTGThGA==}
+ dependencies:
+ '@types/hast': 3.0.2
+ '@types/unist': 3.0.0
+ '@ungap/structured-clone': 1.2.0
+ hast-util-from-parse5: 8.0.1
+ hast-util-to-parse5: 8.0.0
+ html-void-elements: 3.0.0
+ mdast-util-to-hast: 13.0.2
+ parse5: 7.1.2
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.0.0
+ vfile: 6.0.1
+ web-namespaces: 2.0.1
+ zwitch: 2.0.4
dev: false
/hast-util-to-estree@2.3.3:
@@ -7758,25 +7796,37 @@ packages:
- supports-color
dev: false
- /hast-util-to-text@3.1.2:
- resolution: {integrity: sha512-tcllLfp23dJJ+ju5wCCZHVpzsQQ43+moJbqVX3jNWPB7z/KFC4FyZD6R7y94cHL6MQ33YtMZL8Z0aIXXI4XFTw==}
+ /hast-util-to-parse5@8.0.0:
+ resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==}
dependencies:
- '@types/hast': 2.3.5
- '@types/unist': 2.0.7
- hast-util-is-element: 2.1.3
- unist-util-find-after: 4.0.1
+ '@types/hast': 3.0.2
+ comma-separated-tokens: 2.0.3
+ devlop: 1.1.0
+ property-information: 6.2.0
+ space-separated-tokens: 2.0.2
+ web-namespaces: 2.0.1
+ zwitch: 2.0.4
+ dev: false
+
+ /hast-util-to-text@4.0.0:
+ resolution: {integrity: sha512-EWiE1FSArNBPUo1cKWtzqgnuRQwEeQbQtnFJRYV1hb1BWDgrAlBU0ExptvZMM/KSA82cDpm2sFGf3Dmc5Mza3w==}
+ dependencies:
+ '@types/hast': 3.0.2
+ '@types/unist': 3.0.0
+ hast-util-is-element: 3.0.0
+ unist-util-find-after: 5.0.0
dev: false
/hast-util-whitespace@2.0.1:
resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==}
dev: false
- /hastscript@7.2.0:
- resolution: {integrity: sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==}
+ /hastscript@8.0.0:
+ resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==}
dependencies:
- '@types/hast': 2.3.5
+ '@types/hast': 3.0.2
comma-separated-tokens: 2.0.3
- hast-util-parse-selector: 3.1.1
+ hast-util-parse-selector: 4.0.0
property-information: 6.2.0
space-separated-tokens: 2.0.2
dev: false
@@ -7802,6 +7852,10 @@ packages:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
dev: true
+ /html-void-elements@3.0.0:
+ resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
+ dev: false
+
/http-basic@8.1.3:
resolution: {integrity: sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==}
engines: {node: '>=6.0.0'}
@@ -8628,8 +8682,8 @@ packages:
object.assign: 4.1.4
dev: true
- /katex@0.16.8:
- resolution: {integrity: sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==}
+ /katex@0.16.9:
+ resolution: {integrity: sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==}
hasBin: true
dependencies:
commander: 8.3.0
@@ -9139,6 +9193,19 @@ packages:
unist-util-visit: 4.1.2
dev: false
+ /mdast-util-to-hast@13.0.2:
+ resolution: {integrity: sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og==}
+ dependencies:
+ '@types/hast': 3.0.2
+ '@types/mdast': 4.0.2
+ '@ungap/structured-clone': 1.2.0
+ devlop: 1.1.0
+ micromark-util-sanitize-uri: 2.0.0
+ trim-lines: 3.0.1
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.0.0
+ dev: false
+
/mdast-util-to-markdown@1.5.0:
resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==}
dependencies:
@@ -9328,7 +9395,7 @@ packages:
resolution: {integrity: sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==}
dependencies:
'@types/katex': 0.16.1
- katex: 0.16.8
+ katex: 0.16.9
micromark-factory-space: 1.1.0
micromark-util-character: 1.2.0
micromark-util-symbol: 1.1.0
@@ -9459,6 +9526,13 @@ packages:
micromark-util-types: 1.1.0
dev: false
+ /micromark-util-character@2.0.1:
+ resolution: {integrity: sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==}
+ dependencies:
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
/micromark-util-chunked@1.1.0:
resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==}
dependencies:
@@ -9499,6 +9573,10 @@ packages:
resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==}
dev: false
+ /micromark-util-encode@2.0.0:
+ resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
+ dev: false
+
/micromark-util-events-to-acorn@1.2.3:
resolution: {integrity: sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==}
dependencies:
@@ -9536,6 +9614,14 @@ packages:
micromark-util-symbol: 1.1.0
dev: false
+ /micromark-util-sanitize-uri@2.0.0:
+ resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
+ dependencies:
+ micromark-util-character: 2.0.1
+ micromark-util-encode: 2.0.0
+ micromark-util-symbol: 2.0.0
+ dev: false
+
/micromark-util-subtokenize@1.1.0:
resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==}
dependencies:
@@ -9549,10 +9635,18 @@ packages:
resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==}
dev: false
+ /micromark-util-symbol@2.0.0:
+ resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
+ dev: false
+
/micromark-util-types@1.1.0:
resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==}
dev: false
+ /micromark-util-types@2.0.0:
+ resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
+ dev: false
+
/micromark@3.2.0:
resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==}
dependencies:
@@ -9918,17 +10012,18 @@ packages:
- babel-plugin-macros
dev: false
- /nextra-theme-docs@2.10.0(next@13.4.12)(nextra@2.10.0)(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-uXoqRoewbu0JoqQ1m67aIztWe9/nEhcSeHMimhLxZghKZxkYN0kTR5y5jmrwOHRPuJUTLL2YFwy1rvWJIZS2lw==}
+ /nextra-theme-docs@2.13.2(next@13.4.12)(nextra@2.13.2)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-yE4umXaImp1/kf/sFciPj2+EFrNSwd9Db26hi98sIIiujzGf3+9eUgAz45vF9CwBw50FSXxm1QGRcY+slQ4xQQ==}
peerDependencies:
next: '>=9.5.3'
- nextra: 2.10.0
+ nextra: 2.13.2
react: '>=16.13.1'
react-dom: '>=16.13.1'
dependencies:
'@headlessui/react': 1.7.15(react-dom@18.2.0)(react@18.2.0)
'@popperjs/core': 2.11.6
- clsx: 1.2.1
+ clsx: 2.0.0
+ escape-string-regexp: 5.0.0
flexsearch: 0.7.31
focus-visible: 5.2.0
git-url-parse: 13.1.0
@@ -9937,15 +10032,15 @@ packages:
next: 13.4.12(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0)
next-seo: 6.1.0(next@13.4.12)(react-dom@18.2.0)(react@18.2.0)
next-themes: 0.2.1(next@13.4.12)(react-dom@18.2.0)(react@18.2.0)
- nextra: 2.10.0(next@13.4.12)(react-dom@18.2.0)(react@18.2.0)
+ nextra: 2.13.2(next@13.4.12)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
scroll-into-view-if-needed: 3.0.10
- zod: 3.21.4
+ zod: 3.22.4
dev: false
- /nextra@2.10.0(next@13.4.12)(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-euv93UnWpdth8slMRJLqMrWvCCzR/VTVH6DPrn1JW7hZS03c2lzG2q+fsiYULGiy/kFyysmlxd4Nx5KGB1Txwg==}
+ /nextra@2.13.2(next@13.4.12)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-pIgOSXNUqTz1laxV4ChFZOU7lzJAoDHHaBPj8L09PuxrLKqU1BU/iZtXAG6bQeKCx8EPdBsoXxEuENnL9QGnGA==}
engines: {node: '>=16'}
peerDependencies:
next: '>=9.5.3'
@@ -9955,22 +10050,23 @@ packages:
'@headlessui/react': 1.7.15(react-dom@18.2.0)(react@18.2.0)
'@mdx-js/mdx': 2.3.0
'@mdx-js/react': 2.3.0(react@18.2.0)
- '@napi-rs/simple-git': 0.1.8
- '@theguild/remark-mermaid': 0.0.4(react@18.2.0)
- '@theguild/remark-npm2yarn': 0.1.1
- clsx: 1.2.1
+ '@napi-rs/simple-git': 0.1.9
+ '@theguild/remark-mermaid': 0.0.5(react@18.2.0)
+ '@theguild/remark-npm2yarn': 0.2.1
+ clsx: 2.0.0
github-slugger: 2.0.0
graceful-fs: 4.2.11
gray-matter: 4.0.3
- katex: 0.16.8
+ katex: 0.16.9
lodash.get: 4.4.2
next: 13.4.12(@babel/core@7.21.0)(react-dom@18.2.0)(react@18.2.0)
next-mdx-remote: 4.4.1(react-dom@18.2.0)(react@18.2.0)
p-limit: 3.1.0
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
- rehype-katex: 6.0.3
+ rehype-katex: 7.0.0
rehype-pretty-code: 0.9.11(shiki@0.14.3)
+ rehype-raw: 7.0.0
remark-gfm: 3.0.1
remark-math: 5.1.1
remark-reading-time: 2.0.1
@@ -9979,7 +10075,7 @@ packages:
title: 3.5.3
unist-util-remove: 4.0.0
unist-util-visit: 5.0.0
- zod: 3.21.4
+ zod: 3.22.4
transitivePeerDependencies:
- supports-color
dev: false
@@ -10055,9 +10151,9 @@ packages:
path-key: 4.0.0
dev: true
- /npm-to-yarn@2.0.0:
- resolution: {integrity: sha512-/IbjiJ7vqbxfxJxAZ+QI9CCRjnIbvGxn5KQcSY9xHh0lMKc/Sgqmm7yp7KPmd6TiTZX5/KiSBKlkGHo59ucZbg==}
- engines: {node: '>=6.0.0'}
+ /npm-to-yarn@2.1.0:
+ resolution: {integrity: sha512-2C1IgJLdJngq1bSER7K7CGFszRr9s2rijEwvENPEgI0eK9xlD3tNwDc0UJnRj7FIT2aydWm72jB88uVswAhXHA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: false
/nth-check@2.1.1:
@@ -11114,15 +11210,16 @@ packages:
jsesc: 0.5.0
dev: true
- /rehype-katex@6.0.3:
- resolution: {integrity: sha512-ByZlRwRUcWegNbF70CVRm2h/7xy7jQ3R9LaY4VVSvjnoVWwWVhNL60DiZsBpC5tSzYQOCvDbzncIpIjPZWodZA==}
+ /rehype-katex@7.0.0:
+ resolution: {integrity: sha512-h8FPkGE00r2XKU+/acgqwWUlyzve1IiOKwsEkg4pDL3k48PiE0Pt+/uLtVHDVkN1yA4iurZN6UES8ivHVEQV6Q==}
dependencies:
- '@types/hast': 2.3.5
- '@types/katex': 0.14.0
- hast-util-from-html-isomorphic: 1.0.0
- hast-util-to-text: 3.1.2
- katex: 0.16.8
- unist-util-visit: 4.1.2
+ '@types/hast': 3.0.2
+ '@types/katex': 0.16.1
+ hast-util-from-html-isomorphic: 2.0.0
+ hast-util-to-text: 4.0.0
+ katex: 0.16.9
+ unist-util-visit-parents: 6.0.1
+ vfile: 6.0.1
dev: false
/rehype-pretty-code@0.9.11(shiki@0.14.3):
@@ -11137,6 +11234,14 @@ packages:
shiki: 0.14.3
dev: false
+ /rehype-raw@7.0.0:
+ resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
+ dependencies:
+ '@types/hast': 3.0.2
+ hast-util-raw: 9.0.1
+ vfile: 6.0.1
+ dev: false
+
/remark-gfm@3.0.1:
resolution: {integrity: sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==}
dependencies:
@@ -12623,11 +12728,11 @@ packages:
vfile: 5.3.7
dev: false
- /unist-util-find-after@4.0.1:
- resolution: {integrity: sha512-QO/PuPMm2ERxC6vFXEPtmAutOopy5PknD+Oq64gGwxKtk4xwo9Z97t9Av1obPmGU0IyTa6EKYUfTrK2QJS3Ozw==}
+ /unist-util-find-after@5.0.0:
+ resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
dependencies:
- '@types/unist': 2.0.7
- unist-util-is: 5.2.1
+ '@types/unist': 3.0.0
+ unist-util-is: 6.0.0
dev: false
/unist-util-generated@2.0.1:
@@ -12658,6 +12763,12 @@ packages:
'@types/unist': 2.0.7
dev: false
+ /unist-util-position@5.0.0:
+ resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
+ dependencies:
+ '@types/unist': 3.0.0
+ dev: false
+
/unist-util-remove-position@4.0.2:
resolution: {integrity: sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==}
dependencies:
@@ -12665,6 +12776,13 @@ packages:
unist-util-visit: 4.1.2
dev: false
+ /unist-util-remove-position@5.0.0:
+ resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==}
+ dependencies:
+ '@types/unist': 3.0.0
+ unist-util-visit: 5.0.0
+ dev: false
+
/unist-util-remove@4.0.0:
resolution: {integrity: sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==}
dependencies:
@@ -12679,6 +12797,12 @@ packages:
'@types/unist': 2.0.7
dev: false
+ /unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+ dependencies:
+ '@types/unist': 3.0.0
+ dev: false
+
/unist-util-visit-parents@4.1.1:
resolution: {integrity: sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw==}
dependencies:
@@ -12826,11 +12950,11 @@ packages:
extsprintf: 1.3.0
dev: true
- /vfile-location@4.1.0:
- resolution: {integrity: sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==}
+ /vfile-location@5.0.2:
+ resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==}
dependencies:
- '@types/unist': 2.0.7
- vfile: 5.3.7
+ '@types/unist': 3.0.0
+ vfile: 6.0.1
dev: false
/vfile-matter@3.0.1:
@@ -12848,6 +12972,13 @@ packages:
unist-util-stringify-position: 3.0.3
dev: false
+ /vfile-message@4.0.2:
+ resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
+ dependencies:
+ '@types/unist': 3.0.0
+ unist-util-stringify-position: 4.0.0
+ dev: false
+
/vfile@5.3.7:
resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==}
dependencies:
@@ -12857,6 +12988,14 @@ packages:
vfile-message: 3.1.4
dev: false
+ /vfile@6.0.1:
+ resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
+ dependencies:
+ '@types/unist': 3.0.0
+ unist-util-stringify-position: 4.0.0
+ vfile-message: 4.0.2
+ dev: false
+
/viem@0.3.50(typescript@5.1.3):
resolution: {integrity: sha512-s+LxCYZTR9F/qPk1/n1YDVAX9vSeVz7GraqBZWGrDuenCJxo9ArCoIceJ6ksI0WwSeNzcZ0VVbD/kWRzTxkipw==}
dependencies:
@@ -13326,6 +13465,10 @@ packages:
resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==}
dev: false
+ /zod@3.22.4:
+ resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
+ dev: false
+
/zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
dev: false
From 980c40e3dbb4a86c11c050f69dfd0f75e38a6c96 Mon Sep 17 00:00:00 2001
From: typedarray <90073088+0xOlias@users.noreply.github.com>
Date: Thu, 26 Oct 2023 19:16:54 -0400
Subject: [PATCH 03/44] add all examples to ignored
---
.changeset/config.json | 6 +++++-
examples/with-docker/package.json | 2 +-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/.changeset/config.json b/.changeset/config.json
index a2eb8a038..ae001d666 100644
--- a/.changeset/config.json
+++ b/.changeset/config.json
@@ -7,7 +7,11 @@
"ponder-examples-ethfs",
"ponder-examples-art-gobblers",
"ponder-examples-factory-llama",
- "ponder-examples-friendtech"
+ "ponder-examples-friendtech",
+ "ponder-examples-token-erc20",
+ "ponder-examples-token-erc721",
+ "ponder-examples-token-reth",
+ "ponder-examples-with-docker"
],
"linked": [],
"access": "public",
diff --git a/examples/with-docker/package.json b/examples/with-docker/package.json
index fb9725068..5bb368119 100644
--- a/examples/with-docker/package.json
+++ b/examples/with-docker/package.json
@@ -1,5 +1,5 @@
{
- "name": "ponder-examples-token-erc20-with-docker",
+ "name": "ponder-examples-with-docker",
"private": true,
"scripts": {
"dev:up": "docker-compose -f docker-compose-local.yml up",
From 0381cb806612fe3bd5e77185b041d59ad6cef80d Mon Sep 17 00:00:00 2001
From: typedarray <90073088+0xOlias@users.noreply.github.com>
Date: Thu, 26 Oct 2023 23:51:02 -0400
Subject: [PATCH 04/44] test: add sort test for encoding
---
packages/core/src/utils/encoding.test.ts | 26 ++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/packages/core/src/utils/encoding.test.ts b/packages/core/src/utils/encoding.test.ts
index e9bbcfeda..bfad7a8fd 100644
--- a/packages/core/src/utils/encoding.test.ts
+++ b/packages/core/src/utils/encoding.test.ts
@@ -72,3 +72,29 @@ test("encodeAsText handles zero", () => {
);
expect(decodeToBigInt(encoded)).toBe(value);
});
+
+test("lexicographic sort works as expected", () => {
+ const values = [
+ EVM_MAX_UINT,
+ 0n,
+ EVM_MIN_INT,
+ EVM_MAX_UINT - 1n,
+ 1_000n,
+ -500n,
+ EVM_MIN_INT + 1n,
+ ];
+
+ const encoded = values.map(encodeAsText);
+ const sorted = encoded.slice().sort();
+ const decoded = sorted.map(decodeToBigInt);
+
+ expect(decoded).toMatchObject([
+ EVM_MIN_INT,
+ EVM_MIN_INT + 1n,
+ -500n,
+ 0n,
+ 1_000n,
+ EVM_MAX_UINT - 1n,
+ EVM_MAX_UINT,
+ ]);
+});
From 2f69117138ec622aa0bfc9f57758eb087846d60a Mon Sep 17 00:00:00 2001
From: kyscott18 <43524469+kyscott18@users.noreply.github.com>
Date: Thu, 2 Nov 2023 18:55:51 -0400
Subject: [PATCH 05/44] Handle custom topics for factory contracts internally
(#404)
* handle events for factory contracts internally
* update postgres store to support factory topics
* :)
* fix join because factory log filters are non-null
* test inclusion rules for factory log filter
* log filter fragments use id instead of index
* update realtime sync for factory log filters and fix bug with filter utility
* fix: remove unnecessary flat
---------
Co-authored-by: typedarray <90073088+0xOlias@users.noreply.github.com>
---
packages/core/src/config/factories.test.ts | 25 ---
packages/core/src/config/factories.ts | 7 +-
.../core/src/event-store/postgres/format.ts | 4 +
.../src/event-store/postgres/migrations.ts | 4 +
.../core/src/event-store/postgres/store.ts | 190 ++++++++++++------
.../core/src/event-store/sqlite/format.ts | 4 +
.../core/src/event-store/sqlite/migrations.ts | 4 +
packages/core/src/event-store/sqlite/store.ts | 190 ++++++++++++------
packages/core/src/event-store/store.test.ts | 39 ++++
packages/core/src/historical-sync/service.ts | 1 +
.../core/src/realtime-sync/filter.test.ts | 9 +
packages/core/src/realtime-sync/filter.ts | 2 +-
packages/core/src/realtime-sync/service.ts | 37 ++--
.../{logFilter.test.ts => fragments.test.ts} | 33 ++-
packages/core/src/utils/fragments.ts | 125 ++++++++++++
packages/core/src/utils/logFilter.ts | 65 ------
16 files changed, 502 insertions(+), 237 deletions(-)
rename packages/core/src/utils/{logFilter.test.ts => fragments.test.ts} (63%)
create mode 100644 packages/core/src/utils/fragments.ts
delete mode 100644 packages/core/src/utils/logFilter.ts
diff --git a/packages/core/src/config/factories.test.ts b/packages/core/src/config/factories.test.ts
index 5b04a7a5c..e61f8bc26 100644
--- a/packages/core/src/config/factories.test.ts
+++ b/packages/core/src/config/factories.test.ts
@@ -3,7 +3,6 @@ import { expect, test } from "vitest";
import {
buildFactoryCriteria,
- buildFactoryId,
getAddressFromFactoryEventLog,
} from "./factories";
@@ -171,27 +170,3 @@ test("getAddressFromFactoryEventLog gets address from nonindexed parameter 3", (
expect(address).toBe("0x5a6480483462533564634b8c81aea01cf87c6ddb");
});
-
-test("buildFactoryId builds id containing topic", () => {
- const criteria = buildFactoryCriteria({
- address: "0xa",
- event: llamaFactoryEventAbiItem,
- parameter: "deployer",
- });
-
- expect(buildFactoryId({ chainId: 1, ...criteria })).toBe(
- "1_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_topic1"
- );
-});
-
-test("buildFactoryId builds id containing offset", () => {
- const criteria = buildFactoryCriteria({
- address: "0xa",
- event: llamaFactoryEventAbiItem,
- parameter: "llamaPolicy",
- });
-
- expect(buildFactoryId({ chainId: 115511, ...criteria })).toBe(
- "115511_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_offset64"
- );
-});
diff --git a/packages/core/src/config/factories.ts b/packages/core/src/config/factories.ts
index fd0b9a481..3a373ee7e 100644
--- a/packages/core/src/config/factories.ts
+++ b/packages/core/src/config/factories.ts
@@ -12,6 +12,7 @@ export type FactoryCriteria = {
address: Address;
eventSelector: Hex;
childAddressLocation: "topic1" | "topic2" | "topic3" | `offset${number}`;
+ topics?: (Hex | Hex[] | null)[];
};
export type Factory = {
@@ -171,9 +172,3 @@ export function getAddressFromFactoryEventLog({
`Invalid child address location identifier: ${childAddressLocation}`
);
}
-
-export function buildFactoryId(
- criteria: FactoryCriteria & { chainId: number }
-) {
- return `${criteria.chainId}_${criteria.address}_${criteria.eventSelector}_${criteria.childAddressLocation}` as const;
-}
diff --git a/packages/core/src/event-store/postgres/format.ts b/packages/core/src/event-store/postgres/format.ts
index 719af7280..69d29caff 100644
--- a/packages/core/src/event-store/postgres/format.ts
+++ b/packages/core/src/event-store/postgres/format.ts
@@ -186,6 +186,10 @@ type FactoriesTable = {
address: Hex;
eventSelector: Hex;
childAddressLocation: `topic${1 | 2 | 3}` | `offset${number}`;
+ topic0: Hex | null;
+ topic1: Hex | null;
+ topic2: Hex | null;
+ topic3: Hex | null;
};
type FactoryLogFilterIntervalsTable = {
diff --git a/packages/core/src/event-store/postgres/migrations.ts b/packages/core/src/event-store/postgres/migrations.ts
index 039b14f90..2b556ac3d 100644
--- a/packages/core/src/event-store/postgres/migrations.ts
+++ b/packages/core/src/event-store/postgres/migrations.ts
@@ -328,6 +328,10 @@ const migrations: Record = {
.addColumn("address", "varchar(42)", (col) => col.notNull())
.addColumn("eventSelector", "varchar(66)", (col) => col.notNull())
.addColumn("childAddressLocation", "text", (col) => col.notNull()) // `topic${number}` or `offset${number}`
+ .addColumn("topic0", "varchar(66)")
+ .addColumn("topic1", "varchar(66)")
+ .addColumn("topic2", "varchar(66)")
+ .addColumn("topic3", "varchar(66)")
.execute();
await db.schema
.createTable("factoryLogFilterIntervals")
diff --git a/packages/core/src/event-store/postgres/store.ts b/packages/core/src/event-store/postgres/store.ts
index bbf9e4651..3075812ac 100644
--- a/packages/core/src/event-store/postgres/store.ts
+++ b/packages/core/src/event-store/postgres/store.ts
@@ -10,14 +10,17 @@ import {
import type { Pool } from "pg";
import type { Address, Hex, RpcBlock, RpcLog, RpcTransaction } from "viem";
-import { type FactoryCriteria, buildFactoryId } from "@/config/factories";
+import { type FactoryCriteria } from "@/config/factories";
import type { LogFilterCriteria } from "@/config/logFilters";
import type { Block } from "@/types/block";
import type { Log } from "@/types/log";
import type { Transaction } from "@/types/transaction";
import type { NonNull } from "@/types/utils";
+import {
+ buildFactoryFragments,
+ buildLogFilterFragments,
+} from "@/utils/fragments";
import { intervalIntersectionMany, intervalUnion } from "@/utils/interval";
-import { buildLogFilterFragments } from "@/utils/logFilter";
import { range } from "@/utils/range";
import type { EventStore } from "../store";
@@ -185,19 +188,14 @@ export class PostgresEventStore implements EventStore {
})
);
- const fragmentsWithIdx = fragments.map((f, idx) => ({
- idx,
- ...f,
- }));
-
const intervals = await this.db
.with(
- "logFilterFragments(fragmentIndex, fragmentAddress, fragmentTopic0, fragmentTopic1, fragmentTopic2, fragmentTopic3)",
+ "logFilterFragments(fragmentId, fragmentAddress, fragmentTopic0, fragmentTopic1, fragmentTopic2, fragmentTopic3)",
() =>
sql`( values ${sql.join(
- fragmentsWithIdx.map(
+ fragments.map(
(f) =>
- sql`( ${sql.val(f.idx)}, ${sql.val(f.address)}, ${sql.val(
+ sql`( ${sql.val(f.id)}, ${sql.val(f.address)}, ${sql.val(
f.topic0
)}, ${sql.val(f.topic1)}, ${sql.val(f.topic2)}, ${sql.val(
f.topic3
@@ -226,19 +224,19 @@ export class PostgresEventStore implements EventStore {
return baseJoin;
})
- .select(["fragmentIndex", "startBlock", "endBlock"])
+ .select(["fragmentId", "startBlock", "endBlock"])
.where("chainId", "=", chainId)
.execute();
const intervalsByFragment = intervals.reduce((acc, cur) => {
- const { fragmentIndex, ...rest } = cur;
- acc[fragmentIndex] ||= [];
- acc[fragmentIndex].push(rest);
+ const { fragmentId, ...rest } = cur;
+ acc[fragmentId] ||= [];
+ acc[fragmentId].push(rest);
return acc;
- }, {} as Record);
+ }, {} as Record);
- const fragmentIntervals = fragmentsWithIdx.map((f) => {
- return (intervalsByFragment[f.idx] ?? []).map(
+ const fragmentIntervals = fragments.map((f) => {
+ return (intervalsByFragment[f.id] ?? []).map(
(r) =>
[Number(r.startBlock), Number(r.endBlock)] satisfies [number, number]
);
@@ -373,46 +371,119 @@ export class PostgresEventStore implements EventStore {
chainId: number;
factory: FactoryCriteria;
}) => {
- return await this.db.transaction().execute(async (tx) => {
- const factory_ = {
- ...factory,
- id: buildFactoryId({ ...factory, chainId }),
- chainId,
- };
- const { id: factoryId } = await tx
- .insertInto("factories")
- .values(factory_)
- .onConflict((oc) => oc.column("id").doUpdateSet(factory_))
- .returningAll()
- .executeTakeFirstOrThrow();
-
- const existingIntervals = await tx
- .deleteFrom("factoryLogFilterIntervals")
- .where("factoryId", "=", factoryId)
- .returningAll()
- .execute();
+ const fragments = buildFactoryFragments({
+ ...factory,
+ chainId,
+ });
- const mergedIntervals = intervalUnion(
- existingIntervals.map((i) => [Number(i.startBlock), Number(i.endBlock)])
- );
+ await Promise.all(
+ fragments.map(async (fragment) => {
+ await this.db.transaction().execute(async (tx) => {
+ const { id: factoryId } = await tx
+ .insertInto("factories")
+ .values(fragment)
+ .onConflict((oc) => oc.column("id").doUpdateSet(fragment))
+ .returningAll()
+ .executeTakeFirstOrThrow();
- const mergedIntervalRows = mergedIntervals.map(
- ([startBlock, endBlock]) => ({
- factoryId,
- startBlock: BigInt(startBlock),
- endBlock: BigInt(endBlock),
- })
- );
+ const existingIntervals = await tx
+ .deleteFrom("factoryLogFilterIntervals")
+ .where("factoryId", "=", factoryId)
+ .returningAll()
+ .execute();
- if (mergedIntervalRows.length > 0) {
- await tx
- .insertInto("factoryLogFilterIntervals")
- .values(mergedIntervalRows)
- .execute();
- }
+ const mergedIntervals = intervalUnion(
+ existingIntervals.map((i) => [
+ Number(i.startBlock),
+ Number(i.endBlock),
+ ])
+ );
+
+ const mergedIntervalRows = mergedIntervals.map(
+ ([startBlock, endBlock]) => ({
+ factoryId,
+ startBlock: BigInt(startBlock),
+ endBlock: BigInt(endBlock),
+ })
+ );
- return mergedIntervals;
+ if (mergedIntervalRows.length > 0) {
+ await tx
+ .insertInto("factoryLogFilterIntervals")
+ .values(mergedIntervalRows)
+ .execute();
+ }
+
+ return mergedIntervals;
+ });
+ })
+ );
+
+ const intervals = await this.db
+ .with(
+ "factoryFilterFragments(fragmentId, fragmentAddress, fragmentEventSelector, fragmentChildAddressLocation, fragmentTopic0, fragmentTopic1, fragmentTopic2, fragmentTopic3)",
+ () =>
+ sql`( values ${sql.join(
+ fragments.map(
+ (f) =>
+ sql`( ${sql.val(f.id)}, ${sql.val(f.address)}, ${sql.val(
+ f.eventSelector
+ )}, ${sql.val(f.childAddressLocation)}, ${sql.val(
+ f.topic0
+ )}, ${sql.val(f.topic1)}, ${sql.val(f.topic2)}, ${sql.val(
+ f.topic3
+ )} )`
+ )
+ )} )`
+ )
+ .selectFrom("factoryLogFilterIntervals")
+ .leftJoin("factories", "factoryId", "factories.id")
+ .innerJoin("factoryFilterFragments", (join) => {
+ let baseJoin = join.on(({ and, cmpr }) =>
+ and([
+ cmpr("fragmentAddress", "=", sql.ref("address")),
+ cmpr("fragmentEventSelector", "=", sql.ref("eventSelector")),
+ cmpr(
+ "fragmentChildAddressLocation",
+ "=",
+ sql.ref("childAddressLocation")
+ ),
+ ])
+ );
+ for (const idx_ of range(0, 4)) {
+ baseJoin = baseJoin.on(({ or, cmpr }) => {
+ const idx = idx_ as 0 | 1 | 2 | 3;
+ return or([
+ cmpr(`topic${idx}`, "is", null),
+ cmpr(`fragmentTopic${idx}`, "=", sql.ref(`topic${idx}`)),
+ ]);
+ });
+ }
+
+ return baseJoin;
+ })
+ .select(["fragmentId", "startBlock", "endBlock"])
+ .where("chainId", "=", chainId)
+ .execute();
+
+ const intervalsByFragment = intervals.reduce((acc, cur) => {
+ const { fragmentId, ...rest } = cur;
+ acc[fragmentId] ||= [];
+ acc[fragmentId].push({
+ startBlock: rest.startBlock,
+ endBlock: rest.endBlock,
+ });
+ return acc;
+ }, {} as Record);
+
+ const fragmentIntervals = fragments.map((f) => {
+ return (intervalsByFragment[f.id] ?? []).map(
+ (r) =>
+ [Number(r.startBlock), Number(r.endBlock)] satisfies [number, number]
+ );
});
+
+ return intervalIntersectionMany(fragmentIntervals);
};
insertRealtimeBlock = async ({
@@ -632,17 +703,16 @@ export class PostgresEventStore implements EventStore {
factories: FactoryCriteria[];
interval: { startBlock: bigint; endBlock: bigint };
}) => {
+ const factoryFragments = factories
+ .map((factory) => buildFactoryFragments({ ...factory, chainId }))
+ .flat();
+
await Promise.all(
- factories.map(async (factory) => {
- const factory_ = {
- id: buildFactoryId({ chainId, ...factory }),
- chainId,
- ...factory,
- };
+ factoryFragments.map(async (fragment) => {
const { id: factoryId } = await tx
.insertInto("factories")
- .values(factory_)
- .onConflict((oc) => oc.column("id").doUpdateSet(factory_))
+ .values(fragment)
+ .onConflict((oc) => oc.column("id").doUpdateSet(fragment))
.returningAll()
.executeTakeFirstOrThrow();
diff --git a/packages/core/src/event-store/sqlite/format.ts b/packages/core/src/event-store/sqlite/format.ts
index 387b16c81..6de1f0dc4 100644
--- a/packages/core/src/event-store/sqlite/format.ts
+++ b/packages/core/src/event-store/sqlite/format.ts
@@ -191,6 +191,10 @@ type FactoriesTable = {
address: Hex;
eventSelector: Hex;
childAddressLocation: `topic${1 | 2 | 3}` | `offset${number}`;
+ topic0: Hex | null;
+ topic1: Hex | null;
+ topic2: Hex | null;
+ topic3: Hex | null;
};
type FactoryLogFilterIntervalsTable = {
diff --git a/packages/core/src/event-store/sqlite/migrations.ts b/packages/core/src/event-store/sqlite/migrations.ts
index a19c038e3..3ef4fc61b 100644
--- a/packages/core/src/event-store/sqlite/migrations.ts
+++ b/packages/core/src/event-store/sqlite/migrations.ts
@@ -328,6 +328,10 @@ const migrations: Record = {
.addColumn("address", "varchar(42)", (col) => col.notNull())
.addColumn("eventSelector", "varchar(66)", (col) => col.notNull())
.addColumn("childAddressLocation", "text", (col) => col.notNull()) // `topic${number}` or `offset${number}`
+ .addColumn("topic0", "varchar(66)")
+ .addColumn("topic1", "varchar(66)")
+ .addColumn("topic2", "varchar(66)")
+ .addColumn("topic3", "varchar(66)")
.execute();
await db.schema
.createTable("factoryLogFilterIntervals")
diff --git a/packages/core/src/event-store/sqlite/store.ts b/packages/core/src/event-store/sqlite/store.ts
index 25001ac52..ab04ae122 100644
--- a/packages/core/src/event-store/sqlite/store.ts
+++ b/packages/core/src/event-store/sqlite/store.ts
@@ -9,15 +9,18 @@ import {
} from "kysely";
import type { Address, Hex, RpcBlock, RpcLog, RpcTransaction } from "viem";
-import { type FactoryCriteria, buildFactoryId } from "@/config/factories";
+import { type FactoryCriteria } from "@/config/factories";
import type { LogFilterCriteria } from "@/config/logFilters";
import type { Block } from "@/types/block";
import type { Log } from "@/types/log";
import type { Transaction } from "@/types/transaction";
import type { NonNull } from "@/types/utils";
import { decodeToBigInt, encodeAsText } from "@/utils/encoding";
+import {
+ buildFactoryFragments,
+ buildLogFilterFragments,
+} from "@/utils/fragments";
import { intervalIntersectionMany, intervalUnion } from "@/utils/interval";
-import { buildLogFilterFragments } from "@/utils/logFilter";
import { range } from "@/utils/range";
import type { EventStore } from "../store";
@@ -155,16 +158,14 @@ export class SqliteEventStore implements EventStore {
})
);
- const fragmentsWithIdx = fragments.map((f, idx) => ({ idx, ...f }));
-
const intervals = await this.db
.with(
- "logFilterFragments(fragmentIndex, fragmentAddress, fragmentTopic0, fragmentTopic1, fragmentTopic2, fragmentTopic3)",
+ "logFilterFragments(fragmentId, fragmentAddress, fragmentTopic0, fragmentTopic1, fragmentTopic2, fragmentTopic3)",
() =>
sql`( values ${sql.join(
- fragmentsWithIdx.map(
+ fragments.map(
(f) =>
- sql`( ${sql.val(f.idx)}, ${sql.val(f.address)}, ${sql.val(
+ sql`( ${sql.val(f.id)}, ${sql.val(f.address)}, ${sql.val(
f.topic0
)}, ${sql.val(f.topic1)}, ${sql.val(f.topic2)}, ${sql.val(
f.topic3
@@ -193,22 +194,22 @@ export class SqliteEventStore implements EventStore {
return baseJoin;
})
- .select(["fragmentIndex", "startBlock", "endBlock"])
+ .select(["fragmentId", "startBlock", "endBlock"])
.where("chainId", "=", chainId)
.execute();
const intervalsByFragment = intervals.reduce((acc, cur) => {
- const { fragmentIndex, ...rest } = cur;
- acc[fragmentIndex] ||= [];
- acc[fragmentIndex].push({
+ const { fragmentId, ...rest } = cur;
+ acc[fragmentId] ||= [];
+ acc[fragmentId].push({
startBlock: decodeToBigInt(rest.startBlock),
endBlock: decodeToBigInt(rest.endBlock),
});
return acc;
- }, {} as Record);
+ }, {} as Record);
- const fragmentIntervals = fragmentsWithIdx.map((f) => {
- return (intervalsByFragment[f.idx] ?? []).map(
+ const fragmentIntervals = fragments.map((f) => {
+ return (intervalsByFragment[f.id] ?? []).map(
(r) =>
[Number(r.startBlock), Number(r.endBlock)] satisfies [number, number]
);
@@ -338,49 +339,119 @@ export class SqliteEventStore implements EventStore {
chainId: number;
factory: FactoryCriteria;
}) => {
- return await this.db.transaction().execute(async (tx) => {
- const factory_ = {
- ...factory,
- id: buildFactoryId({ ...factory, chainId }),
- chainId,
- };
- const { id: factoryId } = await tx
- .insertInto("factories")
- .values(factory_)
- .onConflict((oc) => oc.doUpdateSet(factory_))
- .returningAll()
- .executeTakeFirstOrThrow();
-
- const existingIntervals = await tx
- .deleteFrom("factoryLogFilterIntervals")
- .where("factoryId", "=", factoryId)
- .returningAll()
- .execute();
+ const fragments = buildFactoryFragments({
+ ...factory,
+ chainId,
+ });
- const mergedIntervals = intervalUnion(
- existingIntervals.map((i) => [
- Number(decodeToBigInt(i.startBlock)),
- Number(decodeToBigInt(i.endBlock)),
- ])
- );
+ await Promise.all(
+ fragments.map(async (fragment) => {
+ return await this.db.transaction().execute(async (tx) => {
+ const { id: factoryId } = await tx
+ .insertInto("factories")
+ .values(fragment)
+ .onConflict((oc) => oc.doUpdateSet(fragment))
+ .returningAll()
+ .executeTakeFirstOrThrow();
- const mergedIntervalRows = mergedIntervals.map(
- ([startBlock, endBlock]) => ({
- factoryId,
- startBlock: encodeAsText(startBlock),
- endBlock: encodeAsText(endBlock),
- })
- );
+ const existingIntervals = await tx
+ .deleteFrom("factoryLogFilterIntervals")
+ .where("factoryId", "=", factoryId)
+ .returningAll()
+ .execute();
- if (mergedIntervalRows.length > 0) {
- await tx
- .insertInto("factoryLogFilterIntervals")
- .values(mergedIntervalRows)
- .execute();
- }
+ const mergedIntervals = intervalUnion(
+ existingIntervals.map((i) => [
+ Number(decodeToBigInt(i.startBlock)),
+ Number(decodeToBigInt(i.endBlock)),
+ ])
+ );
+
+ const mergedIntervalRows = mergedIntervals.map(
+ ([startBlock, endBlock]) => ({
+ factoryId,
+ startBlock: encodeAsText(startBlock),
+ endBlock: encodeAsText(endBlock),
+ })
+ );
+
+ if (mergedIntervalRows.length > 0) {
+ await tx
+ .insertInto("factoryLogFilterIntervals")
+ .values(mergedIntervalRows)
+ .execute();
+ }
+
+ return mergedIntervals;
+ });
+ })
+ );
- return mergedIntervals;
+ const intervals = await this.db
+ .with(
+ "factoryFilterFragments(fragmentId, fragmentAddress, fragmentEventSelector, fragmentChildAddressLocation, fragmentTopic0, fragmentTopic1, fragmentTopic2, fragmentTopic3)",
+ () =>
+ sql`( values ${sql.join(
+ fragments.map(
+ (f) =>
+ sql`( ${sql.val(f.id)}, ${sql.val(f.address)}, ${sql.val(
+ f.eventSelector
+ )}, ${sql.val(f.childAddressLocation)}, ${sql.val(
+ f.topic0
+ )}, ${sql.val(f.topic1)}, ${sql.val(f.topic2)}, ${sql.val(
+ f.topic3
+ )} )`
+ )
+ )} )`
+ )
+ .selectFrom("factoryLogFilterIntervals")
+ .leftJoin("factories", "factoryId", "factories.id")
+ .innerJoin("factoryFilterFragments", (join) => {
+ let baseJoin = join.on(({ and, cmpr }) =>
+ and([
+ cmpr("fragmentAddress", "=", sql.ref("address")),
+ cmpr("fragmentEventSelector", "=", sql.ref("eventSelector")),
+ cmpr(
+ "fragmentChildAddressLocation",
+ "=",
+ sql.ref("childAddressLocation")
+ ),
+ ])
+ );
+ for (const idx_ of range(0, 4)) {
+ baseJoin = baseJoin.on(({ or, cmpr }) => {
+ const idx = idx_ as 0 | 1 | 2 | 3;
+ return or([
+ cmpr(`topic${idx}`, "is", null),
+ cmpr(`fragmentTopic${idx}`, "=", sql.ref(`topic${idx}`)),
+ ]);
+ });
+ }
+
+ return baseJoin;
+ })
+ .select(["fragmentId", "startBlock", "endBlock"])
+ .where("chainId", "=", chainId)
+ .execute();
+
+ const intervalsByFragment = intervals.reduce((acc, cur) => {
+ const { fragmentId, ...rest } = cur;
+ acc[fragmentId] ||= [];
+ acc[fragmentId].push({
+ startBlock: decodeToBigInt(rest.startBlock),
+ endBlock: decodeToBigInt(rest.endBlock),
+ });
+ return acc;
+ }, {} as Record);
+
+ const fragmentIntervals = fragments.map((f) => {
+ return (intervalsByFragment[f.id] ?? []).map(
+ (r) =>
+ [Number(r.startBlock), Number(r.endBlock)] satisfies [number, number]
+ );
});
+
+ return intervalIntersectionMany(fragmentIntervals);
};
insertRealtimeBlock = async ({
@@ -606,17 +677,16 @@ export class SqliteEventStore implements EventStore {
factories: FactoryCriteria[];
interval: { startBlock: bigint; endBlock: bigint };
}) => {
+ const factoryFragments = factories
+ .map((factory) => buildFactoryFragments({ ...factory, chainId }))
+ .flat();
+
await Promise.all(
- factories.map(async (factory) => {
- const factory_ = {
- id: buildFactoryId({ chainId, ...factory }),
- chainId,
- ...factory,
- };
+ factoryFragments.map(async (fragment) => {
const { id: factoryId } = await tx
.insertInto("factories")
- .values(factory_)
- .onConflict((oc) => oc.doUpdateSet(factory_))
+ .values(fragment)
+ .onConflict((oc) => oc.doUpdateSet(fragment))
.returningAll()
.executeTakeFirstOrThrow();
diff --git a/packages/core/src/event-store/store.test.ts b/packages/core/src/event-store/store.test.ts
index 169ca031b..b1273fc15 100644
--- a/packages/core/src/event-store/store.test.ts
+++ b/packages/core/src/event-store/store.test.ts
@@ -639,6 +639,45 @@ test("insertFactoryLogFilterInterval inserts and merges child contract intervals
expect(intervals).toMatchObject([[0, 1000]]);
});
+test("getFactoryLogFilterIntervals handles topic filtering rules", async (context) => {
+ const { eventStore } = context;
+
+ const factoryCriteria = {
+ address: "0xfactory",
+ eventSelector:
+ "0x0000000000000000000000000000000000000000000factoryeventsignature",
+ childAddressLocation: "topic1",
+ } satisfies FactoryCriteria;
+
+ await eventStore.insertFactoryLogFilterInterval({
+ chainId: 1,
+ factory: factoryCriteria,
+ block: blockOne,
+ transactions: blockOneTransactions,
+ logs: blockOneLogs,
+ interval: { startBlock: 0n, endBlock: 500n },
+ });
+
+ let intervals = await eventStore.getFactoryLogFilterIntervals({
+ chainId: 1,
+ factory: factoryCriteria,
+ });
+
+ expect(intervals).toMatchObject([[0, 500]]);
+
+ intervals = await eventStore.getFactoryLogFilterIntervals({
+ chainId: 1,
+ factory: {
+ ...factoryCriteria,
+ topics: [
+ "0x0000000000000000000000000000000000000000000factoryeventsignature",
+ ],
+ } as FactoryCriteria,
+ });
+
+ expect(intervals).toMatchObject([[0, 500]]);
+});
+
test("insertRealtimeBlock inserts data", async (context) => {
const { eventStore } = context;
diff --git a/packages/core/src/historical-sync/service.ts b/packages/core/src/historical-sync/service.ts
index 041408a14..caa350cd8 100644
--- a/packages/core/src/historical-sync/service.ts
+++ b/packages/core/src/historical-sync/service.ts
@@ -697,6 +697,7 @@ export class HistoricalSyncService extends Emittery {
for await (const childContractAddressBatch of iterator) {
const batchLogs = await this._eth_getLogs({
address: childContractAddressBatch,
+ topics: factory.criteria.topics,
fromBlock: toHex(fromBlock),
toBlock: toHex(toBlock),
});
diff --git a/packages/core/src/realtime-sync/filter.test.ts b/packages/core/src/realtime-sync/filter.test.ts
index 4d6d80877..a60e46cdc 100644
--- a/packages/core/src/realtime-sync/filter.test.ts
+++ b/packages/core/src/realtime-sync/filter.test.ts
@@ -86,6 +86,15 @@ test("filterLogs handles one logFilter, two addresses", () => {
);
});
+test("filterLogs handles empty array of addresses", () => {
+ const filteredLogs = filterLogs({
+ logs,
+ logFilters: [{ address: [] }],
+ });
+
+ expect(filteredLogs).toStrictEqual(logs);
+});
+
test("filterLogs handles two logFilters, one address each", () => {
const filteredLogs = filterLogs({
logs,
diff --git a/packages/core/src/realtime-sync/filter.ts b/packages/core/src/realtime-sync/filter.ts
index 217a78ba2..d80722a58 100644
--- a/packages/core/src/realtime-sync/filter.ts
+++ b/packages/core/src/realtime-sync/filter.ts
@@ -30,7 +30,7 @@ export function isLogMatchedByFilter({
address?: Address | Address[];
topics?: (Hex | Hex[] | null)[];
}) {
- if (address) {
+ if (address !== undefined && address.length > 0) {
if (Array.isArray(address)) {
if (!address.includes(log.address)) return false;
} else {
diff --git a/packages/core/src/realtime-sync/service.ts b/packages/core/src/realtime-sync/service.ts
index 8e3a1f49b..90d7b4593 100644
--- a/packages/core/src/realtime-sync/service.ts
+++ b/packages/core/src/realtime-sync/service.ts
@@ -343,30 +343,29 @@ export class RealtimeSyncService extends Emittery {
// NOTE: It might make sense to just insert all logs rather than introduce
// a potentially slow DB operation here. It's a tradeoff between sync
// latency and database growth.
- const allChildContractAddresses = (
- await Promise.all(
- this.factories.map(async (factory) => {
- const iterator = this.eventStore.getFactoryChildAddresses({
- chainId: this.network.chainId,
- factory: factory.criteria,
- upToBlockNumber: hexToBigInt(block.number!),
- });
- const childContractAddresses: Hex[] = [];
- for await (const batch of iterator) {
- childContractAddresses.push(...batch);
- }
- return childContractAddresses;
- })
- )
- ).flat();
+ const factoryLogFilters = await Promise.all(
+ this.factories.map(async (factory) => {
+ const iterator = this.eventStore.getFactoryChildAddresses({
+ chainId: this.network.chainId,
+ factory: factory.criteria,
+ upToBlockNumber: hexToBigInt(block.number!),
+ });
+ const childContractAddresses: Hex[] = [];
+ for await (const batch of iterator) {
+ childContractAddresses.push(...batch);
+ }
+ return {
+ address: childContractAddresses,
+ topics: factory.criteria.topics,
+ };
+ })
+ );
matchedLogs = filterLogs({
logs,
logFilters: [
...this.logFilters.map((l) => l.criteria),
- ...(allChildContractAddresses.length > 0
- ? [{ address: allChildContractAddresses }]
- : []),
+ ...factoryLogFilters,
],
});
}
diff --git a/packages/core/src/utils/logFilter.test.ts b/packages/core/src/utils/fragments.test.ts
similarity index 63%
rename from packages/core/src/utils/logFilter.test.ts
rename to packages/core/src/utils/fragments.test.ts
index 8e9b1b369..11e7a8acf 100644
--- a/packages/core/src/utils/logFilter.test.ts
+++ b/packages/core/src/utils/fragments.test.ts
@@ -1,6 +1,13 @@
+import { parseAbiItem } from "viem";
import { expect, test } from "vitest";
-import { buildLogFilterFragments } from "./logFilter";
+import { buildFactoryCriteria } from "@/config/factories";
+
+import { buildFactoryFragments, buildLogFilterFragments } from "./fragments";
+
+const llamaFactoryEventAbiItem = parseAbiItem(
+ "event LlamaInstanceCreated(address indexed deployer, string indexed name, address llamaCore, address llamaExecutor, address llamaPolicy, uint256 chainId)"
+);
test("buildLogFilterFragments generates 1 log filter fragment for null filter", () => {
const logFilterFragments = buildLogFilterFragments({ chainId: 1 });
@@ -87,3 +94,27 @@ test("buildLogFilterFragments generates 12 log filter fragment for 2x2x3 filter"
expect(logFilterFragments.length).toBe(12);
});
+
+test("buildFactoryFragments builds id containing topic", () => {
+ const criteria = buildFactoryCriteria({
+ address: "0xa",
+ event: llamaFactoryEventAbiItem,
+ parameter: "deployer",
+ });
+
+ expect(buildFactoryFragments({ chainId: 1, ...criteria })[0].id).toBe(
+ "1_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_topic1_null_null_null_null"
+ );
+});
+
+test("buildFactoryFragments builds id containing offset", () => {
+ const criteria = buildFactoryCriteria({
+ address: "0xa",
+ event: llamaFactoryEventAbiItem,
+ parameter: "llamaPolicy",
+ });
+
+ expect(buildFactoryFragments({ chainId: 115511, ...criteria })[0].id).toBe(
+ "115511_0xa_0x00fef2d461a2fabbb523f9f42752c61336f03b17a602af52cc6c83cb8b110599_offset64_null_null_null_null"
+ );
+});
diff --git a/packages/core/src/utils/fragments.ts b/packages/core/src/utils/fragments.ts
new file mode 100644
index 000000000..775b90c34
--- /dev/null
+++ b/packages/core/src/utils/fragments.ts
@@ -0,0 +1,125 @@
+import type { Address, Hex } from "viem";
+
+import { FactoryCriteria } from "@/config/factories";
+import type { LogFilterCriteria } from "@/config/logFilters";
+
+/**
+ * Generates log filter fragments from a log filter.
+ *
+ * @param logFilter Log filter to be decompose into fragments.
+ * @returns A list of log filter fragments.
+ */
+export function buildLogFilterFragments({
+ address,
+ topics,
+ chainId,
+}: LogFilterCriteria & {
+ chainId: number;
+}) {
+ return buildFragments({
+ address,
+ topics,
+ chainId,
+ idCallback: (address_, topic0_, topic1_, topic2_, topic3_) =>
+ `${chainId}_${address_}_${topic0_}_${topic1_}_${topic2_}_${topic3_}`,
+ });
+}
+
+/**
+ * Generates factory fragments from a factory.
+ *
+ * @param factory Factory to be decomposed into fragments.
+ * @returns A list of factory fragments.
+ */
+export function buildFactoryFragments({
+ address,
+ topics,
+ childAddressLocation,
+ eventSelector,
+ chainId,
+}: FactoryCriteria & {
+ chainId: number;
+}) {
+ const fragments = buildFragments({
+ address,
+ topics,
+ chainId,
+ childAddressLocation,
+ eventSelector,
+ idCallback: (address_, topic0_, topic1_, topic2_, topic3_) =>
+ `${chainId}_${address_}_${eventSelector}_${childAddressLocation}_${topic0_}_${topic1_}_${topic2_}_${topic3_}`,
+ });
+
+ return fragments as ((typeof fragments)[number] &
+ Pick<
+ FactoryCriteria,
+ "eventSelector" | "childAddressLocation" | "address"
+ >)[];
+}
+
+function buildFragments({
+ address,
+ topics,
+ chainId,
+ idCallback,
+ ...rest
+}:
+ | (LogFilterCriteria | FactoryCriteria) & {
+ idCallback: (
+ address: Address | null,
+ topic0: ReturnType["topic0"],
+ topic1: ReturnType["topic1"],
+ topic2: ReturnType["topic2"],
+ topic3: ReturnType["topic3"]
+ ) => string;
+ chainId: number;
+ }) {
+ const fragments: {
+ id: string;
+ chainId: number;
+ address: Hex | null;
+ topic0: Hex | null;
+ topic1: Hex | null;
+ topic2: Hex | null;
+ topic3: Hex | null;
+ }[] = [];
+
+ const { topic0, topic1, topic2, topic3 } = parseTopics(topics);
+
+ for (const address_ of Array.isArray(address) ? address : [address ?? null]) {
+ for (const topic0_ of Array.isArray(topic0) ? topic0 : [topic0]) {
+ for (const topic1_ of Array.isArray(topic1) ? topic1 : [topic1]) {
+ for (const topic2_ of Array.isArray(topic2) ? topic2 : [topic2]) {
+ for (const topic3_ of Array.isArray(topic3) ? topic3 : [topic3]) {
+ fragments.push({
+ id: idCallback(address_, topic0_, topic1_, topic2_, topic3_),
+ ...rest,
+ chainId,
+ address: address_,
+ topic0: topic0_,
+ topic1: topic1_,
+ topic2: topic2_,
+ topic3: topic3_,
+ });
+ }
+ }
+ }
+ }
+ }
+
+ return fragments;
+}
+
+function parseTopics(topics: (Hex | Hex[] | null)[] | undefined) {
+ return {
+ topic0: topics?.[0] ?? null,
+ topic1: topics?.[1] ?? null,
+ topic2: topics?.[2] ?? null,
+ topic3: topics?.[3] ?? null,
+ } as {
+ topic0: Hex | Hex[] | null;
+ topic1: Hex | Hex[] | null;
+ topic2: Hex | Hex[] | null;
+ topic3: Hex | Hex[] | null;
+ };
+}
diff --git a/packages/core/src/utils/logFilter.ts b/packages/core/src/utils/logFilter.ts
deleted file mode 100644
index 7151a55f5..000000000
--- a/packages/core/src/utils/logFilter.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import type { Hex } from "viem";
-
-import type { LogFilterCriteria } from "@/config/logFilters";
-
-/**
- * Generates log filter fragments from a log filter.
- *
- * @param logFilter Log filter to be decompose into fragments.
- * @returns A list of log filter fragments.
- */
-export function buildLogFilterFragments({
- address,
- topics,
- chainId,
-}: LogFilterCriteria & {
- chainId: number;
-}) {
- const fragments: {
- id: string;
- chainId: number;
- address: Hex | null;
- topic0: Hex | null;
- topic1: Hex | null;
- topic2: Hex | null;
- topic3: Hex | null;
- }[] = [];
-
- const { topic0, topic1, topic2, topic3 } = parseTopics(topics);
-
- for (const address_ of Array.isArray(address) ? address : [address ?? null]) {
- for (const topic0_ of Array.isArray(topic0) ? topic0 : [topic0]) {
- for (const topic1_ of Array.isArray(topic1) ? topic1 : [topic1]) {
- for (const topic2_ of Array.isArray(topic2) ? topic2 : [topic2]) {
- for (const topic3_ of Array.isArray(topic3) ? topic3 : [topic3]) {
- fragments.push({
- id: `${chainId}_${address_}_${topic0_}_${topic1_}_${topic2_}_${topic3_}`,
- chainId,
- address: address_,
- topic0: topic0_,
- topic1: topic1_,
- topic2: topic2_,
- topic3: topic3_,
- });
- }
- }
- }
- }
- }
-
- return fragments;
-}
-
-function parseTopics(topics: (Hex | Hex[] | null)[] | undefined) {
- return {
- topic0: topics?.[0] ?? null,
- topic1: topics?.[1] ?? null,
- topic2: topics?.[2] ?? null,
- topic3: topics?.[3] ?? null,
- } as {
- topic0: Hex | Hex[] | null;
- topic1: Hex | Hex[] | null;
- topic2: Hex | Hex[] | null;
- topic3: Hex | Hex[] | null;
- };
-}
From 4c09f909cce49f2b28e484edd6f340348ee226e9 Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Thu, 2 Nov 2023 23:30:13 -0400
Subject: [PATCH 06/44] config types refactor wip
---
packages/core/src/Ponder.ts | 57 +--
packages/core/src/build/functions.ts | 9 +-
packages/core/src/build/service.ts | 22 +-
packages/core/src/codegen/contract.ts | 33 --
packages/core/src/codegen/event.ts | 11 +-
packages/core/src/codegen/service.ts | 38 +-
packages/core/src/config/abi.test.ts | 506 +++++++++----------
packages/core/src/config/abi.ts | 158 +++---
packages/core/src/config/contracts.ts | 106 ++--
packages/core/src/config/factories.ts | 77 +--
packages/core/src/config/logFilters.ts | 120 -----
packages/core/src/config/sources.ts | 164 ++++++
packages/core/src/config/types.ts | 112 ++--
packages/core/src/historical-sync/service.ts | 18 +-
packages/core/src/indexing/contract.test.ts | 272 +++++-----
packages/core/src/indexing/contract.ts | 302 +++++------
packages/core/src/indexing/service.test.ts | 22 +-
packages/core/src/indexing/service.ts | 21 +-
packages/core/src/realtime-sync/service.ts | 19 +-
packages/core/src/ui/app.tsx | 16 +-
packages/core/src/ui/service.ts | 22 +-
packages/core/src/utils/fragments.ts | 3 +-
22 files changed, 963 insertions(+), 1145 deletions(-)
delete mode 100644 packages/core/src/codegen/contract.ts
delete mode 100644 packages/core/src/config/logFilters.ts
create mode 100644 packages/core/src/config/sources.ts
diff --git a/packages/core/src/Ponder.ts b/packages/core/src/Ponder.ts
index 7e5b452c5..7da2da157 100644
--- a/packages/core/src/Ponder.ts
+++ b/packages/core/src/Ponder.ts
@@ -3,10 +3,7 @@ import process from "node:process";
import { BuildService } from "@/build/service";
import { CodegenService } from "@/codegen/service";
-import { buildContracts } from "@/config/contracts";
import { buildDatabase } from "@/config/database";
-import { type Factory, buildFactories } from "@/config/factories";
-import { type LogFilter, buildLogFilters } from "@/config/logFilters";
import { type Network, buildNetwork } from "@/config/networks";
import { type Options } from "@/config/options";
import { type ResolvedConfig } from "@/config/types";
@@ -27,6 +24,8 @@ import { PostgresUserStore } from "@/user-store/postgres/store";
import { SqliteUserStore } from "@/user-store/sqlite/store";
import { type UserStore } from "@/user-store/store";
+import { buildSources, Source } from "./config/sources";
+
export type Common = {
options: Options;
logger: LoggerService;
@@ -37,7 +36,7 @@ export type Common = {
export class Ponder {
common: Common;
- logFilters: LogFilter[];
+ sources: Source[];
eventStore: EventStore;
userStore: UserStore;
@@ -45,8 +44,7 @@ export class Ponder {
// List of indexing-related services. One per configured network.
networkSyncServices: {
network: Network;
- logFilters: LogFilter[];
- factories: Factory[];
+ sources: Source[];
historicalSyncService: HistoricalSyncService;
realtimeSyncService: RealtimeSyncService;
}[] = [];
@@ -106,15 +104,14 @@ export class Ponder {
const networks = config.networks.map((network) =>
buildNetwork({ network, common })
);
- const logFilters = buildLogFilters({ options, config });
- this.logFilters = logFilters;
- const contracts = buildContracts({ options, config, networks });
- const factories = buildFactories({ options, config });
+
+ const sources = buildSources({ options, config });
+ this.sources = sources;
const networksToSync = config.networks
.map((network) => buildNetwork({ network, common }))
.filter((network) => {
- const hasEventSources = [...logFilters, ...factories].some(
+ const hasEventSources = this.sources.some(
(eventSource) => eventSource.network === network.name
);
if (!hasEventSources) {
@@ -127,29 +124,25 @@ export class Ponder {
});
networksToSync.forEach((network) => {
- const logFiltersForNetwork = logFilters.filter(
- (logFilter) => logFilter.network === network.name
- );
- const factoriesForNetwork = factories.filter(
- (logFilter) => logFilter.network === network.name
+ const sourcesForNetwork = sources.filter(
+ (logSource) => logSource.network === network.name
);
+
this.networkSyncServices.push({
network,
- logFilters: logFiltersForNetwork,
- factories: factoriesForNetwork,
+
+ sources: sourcesForNetwork,
historicalSyncService: new HistoricalSyncService({
common,
eventStore: this.eventStore,
network,
- logFilters: logFiltersForNetwork,
- factories: factoriesForNetwork,
+ sources: sourcesForNetwork,
}),
realtimeSyncService: new RealtimeSyncService({
common,
eventStore: this.eventStore,
network,
- logFilters: logFiltersForNetwork,
- factories,
+ sources: sourcesForNetwork,
}),
});
});
@@ -158,8 +151,7 @@ export class Ponder {
common,
eventStore: this.eventStore,
networks,
- logFilters,
- factories,
+ sources,
});
this.indexingService = new IndexingService({
@@ -167,9 +159,7 @@ export class Ponder {
eventStore: this.eventStore,
userStore: this.userStore,
eventAggregatorService: this.eventAggregatorService,
- contracts,
- logFilters,
- factories,
+ sources,
});
this.serverService = new ServerService({
@@ -178,16 +168,13 @@ export class Ponder {
});
this.buildService = new BuildService({
common,
- logFilters,
- factories,
+ sources,
});
this.codegenService = new CodegenService({
common,
- contracts,
- logFilters,
- factories,
+ sources,
});
- this.uiService = new UiService({ common, logFilters, factories });
+ this.uiService = new UiService({ common, sources });
}
async setup() {
@@ -216,7 +203,7 @@ export class Ponder {
event: "App Started",
properties: {
command: "ponder dev",
- logFilterCount: this.logFilters.length,
+ logFilterCount: this.sources.length,
databaseKind: this.eventStore.kind,
},
});
@@ -243,7 +230,7 @@ export class Ponder {
event: "App Started",
properties: {
command: "ponder start",
- logFilterCount: this.logFilters.length,
+ logFilterCount: this.sources.length,
databaseKind: this.eventStore.kind,
},
});
diff --git a/packages/core/src/build/functions.ts b/packages/core/src/build/functions.ts
index 0e4ae5d70..0503f0690 100644
--- a/packages/core/src/build/functions.ts
+++ b/packages/core/src/build/functions.ts
@@ -6,9 +6,8 @@ import { replaceTscAliasPaths } from "tsc-alias";
import type { Hex } from "viem";
import { LogEventMetadata } from "@/config/abi";
-import { Factory } from "@/config/factories";
-import type { LogFilter } from "@/config/logFilters";
import type { Options } from "@/config/options";
+import { Source } from "@/config/sources";
import type { Block } from "@/types/block";
import type { Log } from "@/types/log";
import type { Transaction } from "@/types/transaction";
@@ -207,12 +206,10 @@ export type IndexingFunctions = {
export const hydrateIndexingFunctions = ({
rawIndexingFunctions,
- logFilters,
- factories,
+ sources,
}: {
rawIndexingFunctions: RawIndexingFunctions;
- logFilters: LogFilter[];
- factories: Factory[];
+ sources: Source[];
}) => {
const indexingFunctions: IndexingFunctions = {
_meta_: {},
diff --git a/packages/core/src/build/service.ts b/packages/core/src/build/service.ts
index 2376b589b..2c8ea3d25 100644
--- a/packages/core/src/build/service.ts
+++ b/packages/core/src/build/service.ts
@@ -5,8 +5,7 @@ import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import path from "node:path";
-import type { Factory } from "@/config/factories";
-import type { LogFilter } from "@/config/logFilters";
+import { Source } from "@/config/sources";
import { UserError } from "@/errors/user";
import type { Common } from "@/Ponder";
import { buildSchema } from "@/schema/schema";
@@ -28,25 +27,15 @@ type BuildServiceEvents = {
export class BuildService extends Emittery {
private common: Common;
- private logFilters: LogFilter[];
- private factories: Factory[];
+ private sources: Source[];
private closeWatcher?: () => Promise;
private latestFileHashes: Record = {};
- constructor({
- common,
- logFilters,
- factories,
- }: {
- common: Common;
- logFilters: LogFilter[];
- factories: Factory[];
- }) {
+ constructor({ common, sources }: { common: Common; sources: Source[] }) {
super();
this.common = common;
- this.logFilters = logFilters;
- this.factories = factories;
+ this.sources = sources;
}
async kill() {
@@ -102,8 +91,7 @@ export class BuildService extends Emittery {
const indexingFunctions = hydrateIndexingFunctions({
rawIndexingFunctions,
- logFilters: this.logFilters,
- factories: this.factories,
+ sources: this.sources,
});
if (Object.values(indexingFunctions.eventSources).length === 0) {
diff --git a/packages/core/src/codegen/contract.ts b/packages/core/src/codegen/contract.ts
deleted file mode 100644
index 199e32cfd..000000000
--- a/packages/core/src/codegen/contract.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import type { AbiParameter } from "abitype";
-
-import type { Contract } from "@/config/contracts";
-
-type AbiReadOrViewFunction = {
- type: "function";
- stateMutability: "pure" | "view";
- inputs: readonly AbiParameter[];
- name: string;
- outputs: readonly AbiParameter[];
-};
-
-export const buildContractTypes = (contracts: Contract[]) => {
- return contracts
- .map((contract) => {
- const abiReadOrViewFunctions = contract.abi.filter(
- (item): item is AbiReadOrViewFunction =>
- item.type === "function" &&
- (item.stateMutability === "pure" || item.stateMutability === "view")
- );
-
- return `
- const ${contract.name}Abi = ${JSON.stringify(
- abiReadOrViewFunctions
- )} as const;
-
- export type ${contract.name} = ReadOnlyContract;
- `;
- })
- .join("\n");
-};
diff --git a/packages/core/src/codegen/event.ts b/packages/core/src/codegen/event.ts
index 715992e77..b2232e7d1 100644
--- a/packages/core/src/codegen/event.ts
+++ b/packages/core/src/codegen/event.ts
@@ -1,14 +1,7 @@
import type { LogEventMetadata } from "@/config/abi";
-import type { Factory } from "@/config/factories";
-import type { LogFilter } from "@/config/logFilters";
+import { Source } from "@/config/sources";
-export const buildEventTypes = ({
- logFilters,
- factories,
-}: {
- logFilters: LogFilter[];
- factories: Factory[];
-}) => {
+export const buildEventTypes = ({ sources }: { sources: Source[] }) => {
const allIndexingFunctions = [
...logFilters.map((logFilter) => {
return Object.values(logFilter.events)
diff --git a/packages/core/src/codegen/service.ts b/packages/core/src/codegen/service.ts
index 7d231b4aa..b255b808a 100644
--- a/packages/core/src/codegen/service.ts
+++ b/packages/core/src/codegen/service.ts
@@ -3,40 +3,23 @@ import { GraphQLSchema, printSchema } from "graphql";
import { writeFileSync } from "node:fs";
import path from "node:path";
-import type { Contract } from "@/config/contracts";
-import { Factory } from "@/config/factories";
-import type { LogFilter } from "@/config/logFilters";
+import { Source } from "@/config/sources";
import type { Common } from "@/Ponder";
import type { Schema } from "@/schema/types";
import { ensureDirExists } from "@/utils/exists";
-import { buildContractTypes } from "./contract";
import { buildEntityTypes } from "./entity";
import { buildEventTypes } from "./event";
import { formatPrettier } from "./prettier";
export class CodegenService extends Emittery {
private common: Common;
- private contracts: Contract[];
- private logFilters: LogFilter[];
- private factories: Factory[];
-
- constructor({
- common,
- contracts,
- logFilters,
- factories,
- }: {
- common: Common;
- contracts: Contract[];
- logFilters: LogFilter[];
- factories: Factory[];
- }) {
+ private sources: Source[];
+
+ constructor({ common, sources }: { common: Common; sources: Source[] }) {
super();
this.common = common;
- this.contracts = contracts;
- this.logFilters = logFilters;
- this.factories = factories;
+ this.sources = sources;
}
generateAppFile({ schema }: { schema?: Schema } = {}) {
@@ -56,16 +39,10 @@ export class CodegenService extends Emittery {
/* CONTRACT TYPES */
- ${buildContractTypes(this.contracts)}
-
/* CONTEXT TYPES */
export type Context = {
- contracts: {
- ${this.contracts
- .map((contract) => `${contract.name}: ${contract.name};`)
- .join("")}
- },
+
entities: {
${entities
.map((entity) => `${entity.name}: Model<${entity.name}>;`)
@@ -77,8 +54,7 @@ export class CodegenService extends Emittery {
/* INDEXING FUNCTION TYPES */
${buildEventTypes({
- logFilters: this.logFilters,
- factories: this.factories,
+ sources: this.sources,
})}
export const ponder = new PonderApp();
diff --git a/packages/core/src/config/abi.test.ts b/packages/core/src/config/abi.test.ts
index 487c223f4..b4c4ad645 100644
--- a/packages/core/src/config/abi.test.ts
+++ b/packages/core/src/config/abi.test.ts
@@ -1,280 +1,280 @@
-import { randomUUID } from "node:crypto";
-import { mkdirSync, rmSync, writeFileSync } from "node:fs";
-import { tmpdir } from "node:os";
-import path from "node:path";
-import { beforeEach, expect, test } from "vitest";
+// import { randomUUID } from "node:crypto";
+// import { mkdirSync, rmSync, writeFileSync } from "node:fs";
+// import { tmpdir } from "node:os";
+// import path from "node:path";
+// import { beforeEach, expect, test } from "vitest";
-import { buildAbi, getEvents } from "./abi";
+// import { buildAbi, getEvents } from "./abi";
-const abiSimple = [
- {
- inputs: [],
- stateMutability: "nonpayable",
- type: "constructor",
- },
- {
- inputs: [
- {
- indexed: true,
- type: "address",
- },
- {
- indexed: true,
- type: "address",
- },
- {
- indexed: false,
- type: "uint256",
- },
- ],
- name: "Transfer",
- type: "event",
- },
- {
- inputs: [
- {
- indexed: true,
- type: "address",
- },
- {
- indexed: true,
- type: "address",
- },
- {
- indexed: false,
- type: "uint256",
- },
- ],
- name: "Approve",
- type: "event",
- },
-] as const;
+// const abiSimple = [
+// {
+// inputs: [],
+// stateMutability: "nonpayable",
+// type: "constructor",
+// },
+// {
+// inputs: [
+// {
+// indexed: true,
+// type: "address",
+// },
+// {
+// indexed: true,
+// type: "address",
+// },
+// {
+// indexed: false,
+// type: "uint256",
+// },
+// ],
+// name: "Transfer",
+// type: "event",
+// },
+// {
+// inputs: [
+// {
+// indexed: true,
+// type: "address",
+// },
+// {
+// indexed: true,
+// type: "address",
+// },
+// {
+// indexed: false,
+// type: "uint256",
+// },
+// ],
+// name: "Approve",
+// type: "event",
+// },
+// ] as const;
-const abiWithSameEvent = [
- {
- inputs: [],
- stateMutability: "nonpayable",
- type: "constructor",
- },
- {
- inputs: [
- {
- indexed: true,
- type: "address",
- },
- {
- indexed: true,
- type: "address",
- },
- {
- indexed: false,
- type: "uint256",
- },
- ],
- name: "Approve",
- type: "event",
- },
-] as const;
+// const abiWithSameEvent = [
+// {
+// inputs: [],
+// stateMutability: "nonpayable",
+// type: "constructor",
+// },
+// {
+// inputs: [
+// {
+// indexed: true,
+// type: "address",
+// },
+// {
+// indexed: true,
+// type: "address",
+// },
+// {
+// indexed: false,
+// type: "uint256",
+// },
+// ],
+// name: "Approve",
+// type: "event",
+// },
+// ] as const;
-let tmpDir: string;
-let configFilePath: string;
+// let tmpDir: string;
+// let configFilePath: string;
-beforeEach(() => {
- tmpDir = path.join(tmpdir(), randomUUID());
- configFilePath = path.join(tmpDir, "ponder.config.ts");
+// beforeEach(() => {
+// tmpDir = path.join(tmpdir(), randomUUID());
+// configFilePath = path.join(tmpDir, "ponder.config.ts");
- mkdirSync(tmpDir, { recursive: true });
+// mkdirSync(tmpDir, { recursive: true });
- return () => rmSync(tmpDir, { recursive: true, force: true });
-});
+// return () => rmSync(tmpDir, { recursive: true, force: true });
+// });
-test("buildAbi handles a single ABI passed as a file path", () => {
- const abiSimplePath = path.join(tmpDir, "abiSimple.json");
- writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
+// test("buildAbi handles a single ABI passed as a file path", () => {
+// const abiSimplePath = path.join(tmpDir, "abiSimple.json");
+// writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
- const { abi, filePaths } = buildAbi({
- abiConfig: "./abiSimple.json",
- configFilePath,
- });
+// const { abi, filePaths } = buildAbi({
+// abiConfig: "./abiSimple.json",
+// configFilePath,
+// });
- expect(abi).toMatchObject(abiSimple);
- expect(filePaths).toMatchObject([abiSimplePath]);
-});
+// expect(abi).toMatchObject(abiSimple);
+// expect(filePaths).toMatchObject([abiSimplePath]);
+// });
-test("buildAbi handles a single ABI passed as an object", () => {
- const { abi, filePaths } = buildAbi({
- abiConfig: abiSimple,
- configFilePath,
- });
+// test("buildAbi handles a single ABI passed as an object", () => {
+// const { abi, filePaths } = buildAbi({
+// abiConfig: abiSimple,
+// configFilePath,
+// });
- expect(abi).toMatchObject(abiSimple);
- expect(filePaths).toMatchObject([]);
-});
+// expect(abi).toMatchObject(abiSimple);
+// expect(filePaths).toMatchObject([]);
+// });
-test("buildAbi handles an array with a single ABI passed as a file path", () => {
- const abiSimplePath = path.join(tmpDir, "abiSimple.json");
- writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
+// test("buildAbi handles an array with a single ABI passed as a file path", () => {
+// const abiSimplePath = path.join(tmpDir, "abiSimple.json");
+// writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
- const { abi, filePaths } = buildAbi({
- abiConfig: ["./abiSimple.json"],
- configFilePath,
- });
+// const { abi, filePaths } = buildAbi({
+// abiConfig: ["./abiSimple.json"],
+// configFilePath,
+// });
- expect(abi).toMatchObject(abiSimple.filter((x) => x.type !== "constructor"));
- expect(filePaths).toMatchObject([abiSimplePath]);
-});
+// expect(abi).toMatchObject(abiSimple.filter((x) => x.type !== "constructor"));
+// expect(filePaths).toMatchObject([abiSimplePath]);
+// });
-test("buildAbi handles an array of ABIs passed as file paths", () => {
- const abiSimplePath = path.join(tmpDir, "abiSimple.json");
- writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
- const abiWithSameEventPath = path.join(tmpDir, "abiWithSameEvent.json");
- writeFileSync(abiWithSameEventPath, JSON.stringify(abiWithSameEvent));
+// test("buildAbi handles an array of ABIs passed as file paths", () => {
+// const abiSimplePath = path.join(tmpDir, "abiSimple.json");
+// writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
+// const abiWithSameEventPath = path.join(tmpDir, "abiWithSameEvent.json");
+// writeFileSync(abiWithSameEventPath, JSON.stringify(abiWithSameEvent));
- const { abi, filePaths } = buildAbi({
- abiConfig: ["./abiSimple.json", "./abiWithSameEvent.json"],
- configFilePath,
- });
+// const { abi, filePaths } = buildAbi({
+// abiConfig: ["./abiSimple.json", "./abiWithSameEvent.json"],
+// configFilePath,
+// });
- expect(abi.filter((x) => x.type === "event")).toMatchObject(
- abiSimple.filter((x) => x.type === "event")
- );
- expect(filePaths).toMatchObject([abiSimplePath, abiWithSameEventPath]);
-});
+// expect(abi.filter((x) => x.type === "event")).toMatchObject(
+// abiSimple.filter((x) => x.type === "event")
+// );
+// expect(filePaths).toMatchObject([abiSimplePath, abiWithSameEventPath]);
+// });
-test("buildAbi handles an array of ABIs with both file paths and objects", () => {
- const abiSimplePath = path.join(tmpDir, "abiSimple.json");
- writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
+// test("buildAbi handles an array of ABIs with both file paths and objects", () => {
+// const abiSimplePath = path.join(tmpDir, "abiSimple.json");
+// writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
- const { abi, filePaths } = buildAbi({
- abiConfig: ["./abiSimple.json", abiWithSameEvent],
- configFilePath,
- });
+// const { abi, filePaths } = buildAbi({
+// abiConfig: ["./abiSimple.json", abiWithSameEvent],
+// configFilePath,
+// });
- expect(abi.filter((x) => x.type === "event")).toMatchObject(
- abiSimple.filter((x) => x.type === "event")
- );
- expect(filePaths).toMatchObject([abiSimplePath]);
-});
+// expect(abi.filter((x) => x.type === "event")).toMatchObject(
+// abiSimple.filter((x) => x.type === "event")
+// );
+// expect(filePaths).toMatchObject([abiSimplePath]);
+// });
-test("buildAbi handles an array of ABIs and removes duplicate abi items", () => {
- const abiSimplePath = path.join(tmpDir, "abiSimple.json");
- writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
+// test("buildAbi handles an array of ABIs and removes duplicate abi items", () => {
+// const abiSimplePath = path.join(tmpDir, "abiSimple.json");
+// writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
- const { abi, filePaths } = buildAbi({
- abiConfig: ["./abiSimple.json", abiWithSameEvent],
- configFilePath,
- });
+// const { abi, filePaths } = buildAbi({
+// abiConfig: ["./abiSimple.json", abiWithSameEvent],
+// configFilePath,
+// });
- expect(abi.filter((x) => x.type === "event")).toMatchObject(
- abiSimple.filter((x) => x.type === "event")
- );
- expect(filePaths).toMatchObject([abiSimplePath]);
-});
+// expect(abi.filter((x) => x.type === "event")).toMatchObject(
+// abiSimple.filter((x) => x.type === "event")
+// );
+// expect(filePaths).toMatchObject([abiSimplePath]);
+// });
-const abiWithOverloadedEvents = [
- {
- inputs: [],
- stateMutability: "nonpayable",
- type: "constructor",
- },
- {
- inputs: [
- {
- indexed: true,
- type: "address",
- },
- {
- indexed: true,
- type: "address",
- },
- {
- indexed: false,
- type: "uint256",
- },
- ],
- name: "Transfer",
- type: "event",
- },
- {
- inputs: [
- {
- indexed: true,
- type: "uint8",
- },
- {
- indexed: true,
- type: "uint256",
- },
- {
- indexed: false,
- type: "uint256",
- },
- {
- indexed: false,
- type: "address",
- },
- ],
- name: "Transfer",
- type: "event",
- },
-] as const;
+// const abiWithOverloadedEvents = [
+// {
+// inputs: [],
+// stateMutability: "nonpayable",
+// type: "constructor",
+// },
+// {
+// inputs: [
+// {
+// indexed: true,
+// type: "address",
+// },
+// {
+// indexed: true,
+// type: "address",
+// },
+// {
+// indexed: false,
+// type: "uint256",
+// },
+// ],
+// name: "Transfer",
+// type: "event",
+// },
+// {
+// inputs: [
+// {
+// indexed: true,
+// type: "uint8",
+// },
+// {
+// indexed: true,
+// type: "uint256",
+// },
+// {
+// indexed: false,
+// type: "uint256",
+// },
+// {
+// indexed: false,
+// type: "address",
+// },
+// ],
+// name: "Transfer",
+// type: "event",
+// },
+// ] as const;
-test("getEvents handles overloaded events", () => {
- const events = getEvents({ abi: abiWithOverloadedEvents });
+// test("getEvents handles overloaded events", () => {
+// const events = getEvents({ abi: abiWithOverloadedEvents });
- expect(events).toMatchObject({
- "Transfer(address indexed, address indexed, uint256)": {
- safeName: "Transfer(address indexed, address indexed, uint256)",
- signature: "event Transfer(address indexed, address indexed, uint256)",
- selector:
- "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
- abiItem: {
- inputs: [
- {
- indexed: true,
- type: "address",
- },
- {
- indexed: true,
- type: "address",
- },
- {
- indexed: false,
- type: "uint256",
- },
- ],
- name: "Transfer",
- type: "event",
- },
- },
- "Transfer(uint8 indexed, uint256 indexed, uint256, address)": {
- safeName: "Transfer(uint8 indexed, uint256 indexed, uint256, address)",
- signature:
- "event Transfer(uint8 indexed, uint256 indexed, uint256, address)",
- selector:
- "0x7d80b356169a1ce57762f79d1bc650835653d9798678ef3691964dfcde65cd76",
- abiItem: {
- inputs: [
- {
- indexed: true,
- type: "uint8",
- },
- {
- indexed: true,
- type: "uint256",
- },
- {
- indexed: false,
- type: "uint256",
- },
- {
- indexed: false,
- type: "address",
- },
- ],
- name: "Transfer",
- type: "event",
- },
- },
- });
-});
+// expect(events).toMatchObject({
+// "Transfer(address indexed, address indexed, uint256)": {
+// safeName: "Transfer(address indexed, address indexed, uint256)",
+// signature: "event Transfer(address indexed, address indexed, uint256)",
+// selector:
+// "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
+// abiItem: {
+// inputs: [
+// {
+// indexed: true,
+// type: "address",
+// },
+// {
+// indexed: true,
+// type: "address",
+// },
+// {
+// indexed: false,
+// type: "uint256",
+// },
+// ],
+// name: "Transfer",
+// type: "event",
+// },
+// },
+// "Transfer(uint8 indexed, uint256 indexed, uint256, address)": {
+// safeName: "Transfer(uint8 indexed, uint256 indexed, uint256, address)",
+// signature:
+// "event Transfer(uint8 indexed, uint256 indexed, uint256, address)",
+// selector:
+// "0x7d80b356169a1ce57762f79d1bc650835653d9798678ef3691964dfcde65cd76",
+// abiItem: {
+// inputs: [
+// {
+// indexed: true,
+// type: "uint8",
+// },
+// {
+// indexed: true,
+// type: "uint256",
+// },
+// {
+// indexed: false,
+// type: "uint256",
+// },
+// {
+// indexed: false,
+// type: "address",
+// },
+// ],
+// name: "Transfer",
+// type: "event",
+// },
+// },
+// });
+// });
diff --git a/packages/core/src/config/abi.ts b/packages/core/src/config/abi.ts
index 80766415b..13e6d40cd 100644
--- a/packages/core/src/config/abi.ts
+++ b/packages/core/src/config/abi.ts
@@ -1,87 +1,83 @@
import { type Abi, type AbiEvent, formatAbiItem } from "abitype";
-import { readFileSync } from "node:fs";
-import path from "node:path";
-import { getEventSelector, Hex } from "viem";
+import { type Hex, getEventSelector } from "viem";
import { getDuplicateElements } from "@/utils/duplicates";
-export const buildAbi = ({
- abiConfig,
- configFilePath,
-}: {
- abiConfig: string | any[] | object | (string | any[] | object)[];
- configFilePath: string;
-}) => {
- let resolvedAbi: Abi;
- const filePaths: string[] = [];
-
- if (
- typeof abiConfig === "string" ||
- (Array.isArray(abiConfig) &&
- (abiConfig.length === 0 || typeof abiConfig[0] === "object"))
- ) {
- // If abiConfig is a string or an ABI itself, treat it as a single ABI.
- const { abi, filePath } = buildSingleAbi({ abiConfig, configFilePath });
- resolvedAbi = abi;
- if (filePath) filePaths.push(filePath);
- } else {
- // Otherwise, handle as an array of of ABIs.
- const results = (abiConfig as (object | any[])[]).map((a) =>
- buildSingleAbi({ abiConfig: a, configFilePath })
- );
-
- const mergedAbi = results
- .map(({ abi }) => abi.filter((item) => item.type !== "constructor"))
- .flat()
- .flat();
- const mergedUniqueAbi = [
- ...new Map(
- mergedAbi.map((item) => [JSON.stringify(item), item])
- ).values(),
- ];
-
- filePaths.push(
- ...results.map((r) => r.filePath).filter((f): f is string => !!f)
- );
-
- resolvedAbi = mergedUniqueAbi;
- }
-
- return {
- abi: resolvedAbi,
- filePaths,
- };
-};
-
-const buildSingleAbi = ({
- abiConfig,
- configFilePath,
-}: {
- abiConfig: string | any[] | object;
- configFilePath: string;
-}) => {
- let filePath: string | undefined = undefined;
- let abi: Abi;
-
- if (typeof abiConfig === "string") {
- // If a string, treat it as a file path.
- filePath = path.isAbsolute(abiConfig)
- ? abiConfig
- : path.join(path.dirname(configFilePath), abiConfig);
-
- const abiString = readFileSync(filePath, "utf-8");
- abi = JSON.parse(abiString);
- } else {
- // Otherwise, treat as the ABI itself
- abi = abiConfig as unknown as Abi;
- }
-
- // NOTE: Not currently using the filePath arg here, but eventually
- // could use it to watch for changes and reload.
- return { abi, filePath };
-};
-
-type SafeEventName = string;
+// export const buildAbi = ({
+// abiConfig,
+// configFilePath,
+// }: {
+// abiConfig: string | any[] | object | (string | any[] | object)[];
+// configFilePath: string;
+// }) => {
+// let resolvedAbi: Abi;
+// const filePaths: string[] = [];
+
+// if (
+// typeof abiConfig === "string" ||
+// (Array.isArray(abiConfig) &&
+// (abiConfig.length === 0 || typeof abiConfig[0] === "object"))
+// ) {
+// // If abiConfig is a string or an ABI itself, treat it as a single ABI.
+// const { abi, filePath } = buildSingleAbi({ abiConfig, configFilePath });
+// resolvedAbi = abi;
+// if (filePath) filePaths.push(filePath);
+// } else {
+// // Otherwise, handle as an array of of ABIs.
+// const results = (abiConfig as (object | any[])[]).map((a) =>
+// buildSingleAbi({ abiConfig: a, configFilePath })
+// );
+
+// const mergedAbi = results
+// .map(({ abi }) => abi.filter((item) => item.type !== "constructor"))
+// .flat()
+// .flat();
+// const mergedUniqueAbi = [
+// ...new Map(
+// mergedAbi.map((item) => [JSON.stringify(item), item])
+// ).values(),
+// ];
+
+// filePaths.push(
+// ...results.map((r) => r.filePath).filter((f): f is string => !!f)
+// );
+
+// resolvedAbi = mergedUniqueAbi;
+// }
+
+// return {
+// abi: resolvedAbi,
+// filePaths,
+// };
+// };
+
+// const buildSingleAbi = ({
+// abiConfig,
+// configFilePath,
+// }: {
+// abiConfig: string | any[] | object;
+// configFilePath: string;
+// }) => {
+// let filePath: string | undefined = undefined;
+// let abi: Abi;
+
+// if (typeof abiConfig === "string") {
+// // If a string, treat it as a file path.
+// filePath = path.isAbsolute(abiConfig)
+// ? abiConfig
+// : path.join(path.dirname(configFilePath), abiConfig);
+
+// const abiString = readFileSync(filePath, "utf-8");
+// abi = JSON.parse(abiString);
+// } else {
+// // Otherwise, treat as the ABI itself
+// abi = abiConfig as unknown as Abi;
+// }
+
+// // NOTE: Not currently using the filePath arg here, but eventually
+// // could use it to watch for changes and reload.
+// return { abi, filePath };
+// };
export type LogEventMetadata = {
// Event name (if no overloads) or full event signature (if name is overloaded).
@@ -95,6 +91,8 @@ export type LogEventMetadata = {
abiItem: AbiEvent;
};
+type SafeEventName = string;
+
export type AbiEvents = { [key: SafeEventName]: LogEventMetadata | undefined };
export const getEvents = ({ abi }: { abi: Abi }) => {
diff --git a/packages/core/src/config/contracts.ts b/packages/core/src/config/contracts.ts
index 38120e986..90e753490 100644
--- a/packages/core/src/config/contracts.ts
+++ b/packages/core/src/config/contracts.ts
@@ -1,53 +1,53 @@
-import type { Abi, Address } from "abitype";
-
-import type { Options } from "@/config/options";
-import type { ResolvedConfig } from "@/config/types";
-import { toLowerCase } from "@/utils/lowercase";
-
-import { buildAbi } from "./abi";
-import type { Network } from "./networks";
-
-export type Contract = {
- name: string;
- address: Address;
- network: Network;
- abi: Abi;
-};
-
-export function buildContracts({
- config,
- options,
- networks,
-}: {
- config: ResolvedConfig;
- options: Options;
- networks: Network[];
-}) {
- const contracts = config.contracts ?? [];
-
- return contracts
- .filter(
- (
- contract
- ): contract is (typeof contracts)[number] & { address: Address } =>
- !!contract.address
- )
- .map((contract) => {
- const address = toLowerCase(contract.address);
-
- const { abi } = buildAbi({
- abiConfig: contract.abi,
- configFilePath: options.configFile,
- });
-
- // Get the contract network/provider.
- const network = networks.find((n) => n.name === contract.network);
- if (!network) {
- throw new Error(
- `Network [${contract.network}] not found for contract: ${contract.name}`
- );
- }
-
- return { name: contract.name, address, network, abi } satisfies Contract;
- });
-}
+// import type { Abi, Address } from "abitype";
+
+// import type { Options } from "@/config/options";
+// import type { ResolvedConfig } from "@/config/types";
+// import { toLowerCase } from "@/utils/lowercase";
+
+// import { buildAbi } from "./abi";
+// import type { Network } from "./networks";
+
+// export type Contract = {
+// name: string;
+// address: Address;
+// network: Network;
+// abi: Abi;
+// };
+
+// export function buildContracts({
+// config,
+// options,
+// networks,
+// }: {
+// config: ResolvedConfig;
+// options: Options;
+// networks: Network[];
+// }) {
+// const contracts = config.contracts ?? [];
+
+// return contracts
+// .filter(
+// (
+// contract
+// ): contract is (typeof contracts)[number] & { address: Address } =>
+// !!contract.address
+// )
+// .map((contract) => {
+// const address = toLowerCase(contract.address);
+
+// const { abi } = buildAbi({
+// abiConfig: contract.abi,
+// configFilePath: options.configFile,
+// });
+
+// // Get the contract network/provider.
+// const network = networks.find((n) => n.name === contract.network);
+// if (!network) {
+// throw new Error(
+// `Network [${contract.network}] not found for contract: ${contract.name}`
+// );
+// }
+
+// return { name: contract.name, address, network, abi } satisfies Contract;
+// });
+// }
diff --git a/packages/core/src/config/factories.ts b/packages/core/src/config/factories.ts
index 3a373ee7e..0c6ad5389 100644
--- a/packages/core/src/config/factories.ts
+++ b/packages/core/src/config/factories.ts
@@ -1,81 +1,10 @@
-import type { Abi, AbiEvent, Address } from "abitype";
-import { getEventSelector, Hex, RpcLog } from "viem";
+import type { AbiEvent } from "abitype";
+import { getEventSelector, RpcLog } from "viem";
-import type { Options } from "@/config/options";
-import type { ResolvedConfig } from "@/config/types";
import { toLowerCase } from "@/utils/lowercase";
import { getBytesConsumedByParam } from "@/utils/offset";
-import { AbiEvents, buildAbi, getEvents } from "./abi";
-
-export type FactoryCriteria = {
- address: Address;
- eventSelector: Hex;
- childAddressLocation: "topic1" | "topic2" | "topic3" | `offset${number}`;
- topics?: (Hex | Hex[] | null)[];
-};
-
-export type Factory = {
- name: string;
- network: string;
- chainId: number;
- criteria: FactoryCriteria;
- abi: Abi;
- events: AbiEvents;
- startBlock: number;
- endBlock?: number;
- maxBlockRange?: number;
-};
-
-export function buildFactories({
- config,
- options,
-}: {
- config: ResolvedConfig;
- options: Options;
-}) {
- const contracts = config.contracts ?? [];
-
- const factories = contracts
- .filter(
- (
- contract
- ): contract is (typeof contracts)[number] & {
- factory: NonNullable<(typeof contracts)[number]["factory"]>;
- } => !!contract.factory
- )
- .map((contract) => {
- const criteria = buildFactoryCriteria(contract.factory);
-
- const { abi } = buildAbi({
- abiConfig: contract.abi,
- configFilePath: options.configFile,
- });
-
- const childEvents = getEvents({ abi });
-
- const network = config.networks.find((n) => n.name === contract.network);
- if (!network) {
- throw new Error(
- `Network [${contract.network}] not found for factory contract: ${contract.name}`
- );
- }
-
- return {
- name: contract.name,
- network: network.name,
- chainId: network.chainId,
- criteria,
- abi,
- events: childEvents,
- startBlock: contract.startBlock ?? 0,
- endBlock: contract.endBlock,
- maxBlockRange: contract.maxBlockRange,
- } satisfies Factory;
- });
-
- return factories;
-}
+import { FactoryCriteria } from "./sources";
export function buildFactoryCriteria({
address: _address,
diff --git a/packages/core/src/config/logFilters.ts b/packages/core/src/config/logFilters.ts
deleted file mode 100644
index 5d323984d..000000000
--- a/packages/core/src/config/logFilters.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-import type { Abi, Address } from "abitype";
-import { type Hex, encodeEventTopics } from "viem";
-
-import type { Options } from "@/config/options";
-import type { ResolvedConfig } from "@/config/types";
-import { toLowerCase } from "@/utils/lowercase";
-
-import { AbiEvents, buildAbi, getEvents } from "./abi";
-
-export type LogFilterCriteria = {
- address?: Address | Address[];
- topics?: (Hex | Hex[] | null)[];
-};
-
-export type LogFilter = {
- name: string;
- network: string;
- chainId: number;
- criteria: LogFilterCriteria;
- abi: Abi;
- events: AbiEvents;
- startBlock: number;
- endBlock?: number;
- maxBlockRange?: number;
-};
-
-export function buildLogFilters({
- config,
- options,
-}: {
- config: ResolvedConfig;
- options: Options;
-}) {
- const contracts = config.contracts ?? [];
-
- const contractLogFilters = contracts
- .filter(
- (
- contract
- ): contract is (typeof contracts)[number] & { address: Address } =>
- !!contract.address
- )
- .filter((contract) => contract.isLogEventSource ?? true)
- .map((contract) => {
- const { abi } = buildAbi({
- abiConfig: contract.abi,
- configFilePath: options.configFile,
- });
-
- const events = getEvents({ abi });
-
- // Get the contract network/provider.
- const network = config.networks.find((n) => n.name === contract.network);
- if (!network) {
- throw new Error(
- `Network [${contract.network}] not found for contract: ${contract.name}`
- );
- }
-
- const address = toLowerCase(contract.address);
- const topics = undefined;
-
- return {
- name: contract.name,
- network: network.name,
- chainId: network.chainId,
- abi,
- events,
- criteria: { address, topics },
- startBlock: contract.startBlock ?? 0,
- endBlock: contract.endBlock,
- maxBlockRange: contract.maxBlockRange,
- } satisfies LogFilter;
- });
-
- const filterLogFilters = (config.filters ?? []).map((filter) => {
- const { abi } = buildAbi({
- abiConfig: filter.abi,
- configFilePath: options.configFile,
- });
-
- const events = getEvents({ abi });
-
- // Get the contract network/provider.
- const network = config.networks.find((n) => n.name === filter.network);
- if (!network) {
- throw new Error(
- `Network [${filter.network}] not found for filter: ${filter.name}`
- );
- }
-
- const address = Array.isArray(filter.filter.address)
- ? filter.filter.address.map(toLowerCase)
- : typeof filter.filter.address === "string"
- ? toLowerCase(filter.filter.address)
- : undefined;
-
- const topics = filter.filter.event
- ? encodeEventTopics({
- abi: [filter.filter.event],
- eventName: filter.filter.event.name,
- args: filter.filter.args as any,
- })
- : undefined;
-
- return {
- name: filter.name,
- network: network.name,
- chainId: network.chainId,
- abi,
- events,
- criteria: { address, topics },
- startBlock: filter.startBlock ?? 0,
- endBlock: filter.endBlock,
- maxBlockRange: filter.maxBlockRange,
- } satisfies LogFilter;
- });
-
- return (contractLogFilters as LogFilter[]).concat(filterLogFilters);
-}
diff --git a/packages/core/src/config/sources.ts b/packages/core/src/config/sources.ts
new file mode 100644
index 000000000..830d7419d
--- /dev/null
+++ b/packages/core/src/config/sources.ts
@@ -0,0 +1,164 @@
+import { Abi, Address, encodeEventTopics, Hex } from "viem";
+
+import { AbiEvents, getEvents } from "./abi";
+import { buildFactoryCriteria } from "./factories";
+import { Options } from "./options";
+import { ResolvedConfig } from "./types";
+
+/**
+ * There are up to 4 topics in an EVM event
+ *
+ * Technically, only the element could be an array
+ */
+type Topics = [
+ Hex | Hex[] | null,
+ Hex | Hex[] | null,
+ Hex | Hex[] | null,
+ Hex | Hex[] | null
+];
+
+export type LogFilterCriteria = {
+ address?: Address | Address[];
+ topics?: Topics;
+};
+
+export type FactoryCriteria = {
+ address: Address;
+ eventSelector: Hex;
+ childAddressLocation: "topic1" | "topic2" | "topic3" | `offset${number}`;
+ topics?: Topics;
+};
+
+type BaseSource = {
+ name: string;
+ network: string;
+ chainId: number;
+ abi: Abi;
+ events: AbiEvents;
+ startBlock: number;
+ endBlock?: number;
+ maxBlockRange?: number;
+};
+
+export type LogFilter = BaseSource & {
+ type: "logFilter";
+ criteria: LogFilterCriteria;
+};
+
+export type Factory = BaseSource & {
+ type: "factory";
+ criteria: FactoryCriteria;
+};
+
+export type Source = LogFilter | Factory;
+
+export const sourceIsLogFilter = (source: Source): source is LogFilter =>
+ source.type === "logFilter";
+
+export const sourceIsFactory = (source: Source): source is Factory =>
+ source.type === "factory";
+
+export const buildSources = ({
+ config,
+}: {
+ config: ResolvedConfig;
+ options: Options;
+}): Source[] => {
+ const contracts = config.contracts ?? [];
+
+ return contracts
+ .map((contract) => {
+ // Note: should we filter down which indexing functions are available based on the filters
+ const events = getEvents({ abi: contract.abi });
+
+ // Resolve the contract per network, filling in default values where applicable
+ return contract.network
+ .map((networkContract) => {
+ // Note: this is missing config validation for checking if the network is valid
+ const network = config.networks.find(
+ (n) => n.name === networkContract.name
+ )!;
+
+ const resolvedEvents = networkContract.event ?? contract.event;
+
+ const topics = resolvedEvents
+ ? buildTopics(resolvedEvents)
+ : undefined;
+
+ const sharedSource = {
+ // constants
+ name: contract.name,
+ abi: contract.abi,
+ network: network.name,
+ chainId: network.chainId,
+ events,
+ // optionally overridden properties
+ startBlock: networkContract.startBlock ?? contract.startBlock ?? 0,
+ endBlock: networkContract.endBlock ?? contract.endBlock,
+ maxBlockRange:
+ networkContract.maxBlockRange ?? contract.maxBlockRange,
+ } as const;
+
+ if ("factory" in contract) {
+ // factory
+
+ return {
+ ...sharedSource,
+ type: "factory",
+ criteria: {
+ ...buildFactoryCriteria(contract.factory),
+ topics,
+ },
+ } as const satisfies Factory;
+ } else {
+ // log filter
+
+ return {
+ ...sharedSource,
+ type: "logFilter",
+ criteria: {
+ address: contract.address,
+ topics,
+ },
+ } as const satisfies LogFilter;
+ }
+ })
+ .flat();
+ })
+ .flat();
+};
+
+const buildTopics = (
+ events: NonNullable[number]["event"]>
+): Topics => {
+ if (Array.isArray(events)) {
+ // List of event signatures
+ return [
+ events
+ .map((event) =>
+ encodeEventTopics({
+ abi: [event],
+ eventName: event.name,
+ })
+ )
+ .flat(),
+ null,
+ null,
+ null,
+ ];
+ } else {
+ // Single event with args
+ const singleTopics = encodeEventTopics({
+ abi: [events.signature],
+ eventName: events.signature.name,
+ args: events.args,
+ });
+
+ return [
+ singleTopics[0] ?? null,
+ singleTopics[1] ?? null,
+ singleTopics[2] ?? null,
+ singleTopics[3] ?? null,
+ ];
+ }
+};
diff --git a/packages/core/src/config/types.ts b/packages/core/src/config/types.ts
index 23f02e201..5c3045375 100644
--- a/packages/core/src/config/types.ts
+++ b/packages/core/src/config/types.ts
@@ -1,6 +1,49 @@
-import type { AbiEvent } from "abitype";
+import type { Abi, AbiEvent } from "abitype";
import type { Transport } from "viem";
+type ContractRequired = {
+ /** Contract name. Must be unique across `contracts` and `filters`. */
+ name: string;
+ /**
+ * Network that this contract is deployed to. Must match a network name in `networks`.
+ * Any filter information overrides the values in the higher level "contracts" property. Factories cannot override an address and vice versa.
+ */
+ network: ({ name: string } & Partial)[];
+ abi: Abi;
+};
+
+type ContractFilter = (
+ | {
+ /** Contract address. */
+ address?: `0x${string}`;
+ }
+ | {
+ /** Factory contract configuration. */
+ factory: {
+ /** Address of the factory contract that creates this contract. */
+ address: `0x${string}`;
+ /** ABI event that announces the creation of a new instance of this contract. */
+ event: AbiEvent;
+ /** Name of the factory event parameter that contains the new child contract address. */
+ parameter: string; // TODO: Narrow type to known parameter names from `event`.
+ };
+ }
+) & {
+ /** Block number at which to start indexing events (inclusive). Default: `0`. */
+ startBlock?: number;
+ /** Block number at which to stop indexing events (inclusive). If `undefined`, events will be processed in real-time. Default: `undefined`. */
+ endBlock?: number;
+ /** Maximum block range to use when calling `eth_getLogs`. Default: `10_000`. */
+ maxBlockRange?: number;
+
+ event?:
+ | {
+ signature: AbiEvent;
+ args: any[];
+ }
+ | AbiEvent[];
+};
+
export type ResolvedConfig = {
/** Database to use for storing blockchain & entity data. Default: `"postgres"` if `DATABASE_URL` env var is present, otherwise `"sqlite"`. */
database?:
@@ -41,72 +84,7 @@ export type ResolvedConfig = {
maxRpcRequestConcurrency?: number;
}[];
/** List of contracts to sync & index events from. Contracts defined here will be present in `context.contracts`. */
- contracts?: ({
- /** Contract name. Must be unique across `contracts` and `filters`. */
- name: string;
- /** Network that this contract is deployed to. Must match a network name in `networks`. */
- network: string; // TODO: narrow this type to TNetworks[number]['name']
- /** Contract ABI as a file path or an Array object. Accepts a single ABI or a list of ABIs to be merged. */
- abi: string | any[] | readonly any[] | (string | any[] | readonly any[])[];
- } & (
- | {
- /** Contract address. */
- address: `0x${string}`;
- factory?: never;
- }
- | {
- address?: never;
- /** Factory contract configuration. */
- factory: {
- /** Address of the factory contract that creates this contract. */
- address: `0x${string}`;
- /** ABI event that announces the creation of a new instance of this contract. */
- event: AbiEvent;
- /** Name of the factory event parameter that contains the new child contract address. */
- parameter: string; // TODO: Narrow type to known parameter names from `event`.
- };
- }
- ) & {
- /** Block number at which to start indexing events (inclusive). Default: `0`. */
- startBlock?: number;
- /** Block number at which to stop indexing events (inclusive). If `undefined`, events will be processed in real-time. Default: `undefined`. */
- endBlock?: number;
- /** Maximum block range to use when calling `eth_getLogs`. Default: `10_000`. */
- maxBlockRange?: number;
- /** Whether to fetch & process event logs for this contract. If `false`, this contract will still be present in `context.contracts`. Default: `true`. */
- isLogEventSource?: boolean;
- })[];
- /** List of log filters from which to sync & index event logs. */
- filters?: {
- /** Filter name. Must be unique across `contracts` and `filters`. */
- name: string;
- /** Network that this filter is deployed to. Must match a network name in `networks`. */
- network: string; // TODO: narrow this type to TNetworks[number]['name']
- /** Log filter ABI as a file path or an Array object. Accepts a single ABI or a list of ABIs to be merged. */
- abi: string | any[] | readonly any[] | (string | any[] | readonly any[])[];
- /** Log filter options. */
- filter: {
- /** Contract addresses to include. If `undefined`, no filter will be applied. Default: `undefined`. */
- address?: `0x${string}` | `0x${string}`[];
- } & (
- | {
- /** Event signature to include. If `undefined`, no filter will be applied. Default: `undefined`. */
- event?: AbiEvent;
- /** Event arguments to include. If `undefined`, no filter will be applied. Default: `undefined`. */
- args?: any[];
- }
- | {
- event?: never;
- args?: never;
- }
- );
- /** Block number at which to start indexing events (inclusive). Default: `0`. */
- startBlock?: number;
- /** Block number at which to stop indexing events (inclusive). If `undefined`, events will be processed in real-time. Default: `undefined`. */
- endBlock?: number;
- /** Maximum block range to use when calling `eth_getLogs`. Default: `10_000`. */
- maxBlockRange?: number;
- }[];
+ contracts?: (ContractRequired & ContractFilter)[];
/** Configuration for Ponder internals. */
options?: {
/** Maximum number of seconds to wait for event processing to be complete before responding as healthy. If event processing exceeds this duration, the API may serve incomplete data. Default: `240` (4 minutes). */
diff --git a/packages/core/src/historical-sync/service.ts b/packages/core/src/historical-sync/service.ts
index caa350cd8..8cd06a8fd 100644
--- a/packages/core/src/historical-sync/service.ts
+++ b/packages/core/src/historical-sync/service.ts
@@ -15,6 +15,7 @@ import {
import type { Factory } from "@/config/factories";
import type { LogFilter, LogFilterCriteria } from "@/config/logFilters";
import type { Network } from "@/config/networks";
+import { Source } from "@/config/sources";
import type { EventStore } from "@/event-store/store";
import type { Common } from "@/Ponder";
import { formatEta, formatPercentage } from "@/utils/format";
@@ -87,8 +88,7 @@ export class HistoricalSyncService extends Emittery {
* Service configuration. Will eventually be reloadable.
*/
private finalizedBlockNumber: number = null!;
- private logFilters: LogFilter[];
- private factories: Factory[];
+ private sources: Source[];
/**
* Block progress trackers for each task type.
@@ -126,22 +126,19 @@ export class HistoricalSyncService extends Emittery {
common,
eventStore,
network,
- logFilters = [],
- factories = [],
+ sources = [],
}: {
common: Common;
eventStore: EventStore;
network: Network;
- logFilters?: LogFilter[];
- factories?: Factory[];
+ sources?: Source[];
}) {
super();
this.common = common;
this.eventStore = eventStore;
this.network = network;
- this.logFilters = logFilters;
- this.factories = factories;
+ this.sources = sources;
this.queue = this.buildQueue();
@@ -966,10 +963,7 @@ export class HistoricalSyncService extends Emittery {
await this.common.metrics.ponder_historical_completed_blocks.get()
).values;
- const eventSourceNames = [
- ...this.logFilters.map((l) => l.name),
- ...this.factories.map((f) => f.name),
- ];
+ const eventSourceNames = this.sources.map((s) => s.name);
return eventSourceNames.map((name) => {
const totalBlocks = totalBlocksMetric.find(
diff --git a/packages/core/src/indexing/contract.test.ts b/packages/core/src/indexing/contract.test.ts
index 6c65cf041..e2f098d53 100644
--- a/packages/core/src/indexing/contract.test.ts
+++ b/packages/core/src/indexing/contract.test.ts
@@ -1,136 +1,136 @@
-import { getFunctionSelector, hexToBigInt } from "viem";
-import { beforeEach, expect, test, vi } from "vitest";
-
-import { usdcContractConfig } from "@/_test/constants";
-import { setupEventStore } from "@/_test/setup";
-import { publicClient } from "@/_test/utils";
-import type { Contract } from "@/config/contracts";
-import type { Network } from "@/config/networks";
-
-import { buildReadOnlyContracts } from "./contract";
-
-beforeEach((context) => setupEventStore(context));
-
-const network: Network = {
- name: "mainnet",
- chainId: 1,
- client: publicClient,
- pollingInterval: 1_000,
- defaultMaxBlockRange: 3,
- finalityBlockCount: 10,
- maxRpcRequestConcurrency: 10,
-};
-
-const contracts: Contract[] = [
- {
- name: "USDC",
- address: usdcContractConfig.address,
- abi: usdcContractConfig.abi,
- network: network,
- },
-];
-
-// Test data generated from Alchemy Composer.
-const usdcTotalSupply16375000 = 40921687992499550n;
-const usdcTotalSupply16380000 = 40695630049769550n; // This is "latest" for our test setup.
-
-test("getInjectedContract() returns data", async (context) => {
- const { eventStore } = context;
-
- const readOnlyContracts = buildReadOnlyContracts({
- contracts,
- getCurrentBlockNumber: () => 16375000n,
- eventStore,
- });
- const contract = readOnlyContracts["USDC"];
-
- const decimals = await contract.read.decimals();
- expect(decimals).toBe(6);
-});
-
-test("getInjectedContract() uses current block number if no overrides are provided", async (context) => {
- const { eventStore } = context;
-
- const readOnlyContracts = buildReadOnlyContracts({
- contracts,
- getCurrentBlockNumber: () => 16375000n,
- eventStore,
- });
- const contract = readOnlyContracts["USDC"];
-
- const totalSupply = await contract.read.totalSupply();
-
- expect(totalSupply).toBe(usdcTotalSupply16375000);
-});
-
-test("getInjectedContract() caches the read result if no overrides are provided", async (context) => {
- const { eventStore } = context;
-
- const callSpy = vi.spyOn(network.client, "call");
-
- const readOnlyContracts = buildReadOnlyContracts({
- contracts,
- getCurrentBlockNumber: () => 16375000n,
- eventStore,
- });
- const contract = readOnlyContracts["USDC"];
-
- await contract.read.totalSupply();
-
- expect(callSpy).toHaveBeenCalledTimes(1);
-
- const cachedContractReadResult = await eventStore.getContractReadResult({
- address: contract.address,
- blockNumber: 16375000n,
- chainId: 1,
- data: getFunctionSelector("totalSupply()"),
- });
-
- expect(cachedContractReadResult).not.toBeNull();
- expect(hexToBigInt(cachedContractReadResult!.result)).toBe(
- usdcTotalSupply16375000
- );
-
- expect(callSpy).toHaveBeenCalledTimes(1);
-});
-
-test("getInjectedContract() uses blockTag override if provided", async (context) => {
- const { eventStore } = context;
-
- const readOnlyContracts = buildReadOnlyContracts({
- contracts,
- getCurrentBlockNumber: () => 16375000n,
- eventStore,
- });
- const contract = readOnlyContracts["USDC"];
-
- const totalSupply = await contract.read.totalSupply({
- blockTag: "latest",
- });
-
- expect(totalSupply).toBe(usdcTotalSupply16380000);
-});
-
-test("getInjectedContract() does not cache data if blockTag override is provided", async (context) => {
- const { eventStore } = context;
-
- const readOnlyContracts = buildReadOnlyContracts({
- contracts,
- getCurrentBlockNumber: () => 16375000n,
- eventStore,
- });
- const contract = readOnlyContracts["USDC"];
-
- await contract.read.totalSupply({
- blockTag: "latest",
- });
-
- const cachedContractReadResult = await eventStore.getContractReadResult({
- address: contract.address,
- blockNumber: 16375000n,
- chainId: 1,
- data: getFunctionSelector("totalSupply()"),
- });
-
- expect(cachedContractReadResult).toBeNull();
-});
+// import { getFunctionSelector, hexToBigInt } from "viem";
+// import { beforeEach, expect, test, vi } from "vitest";
+
+// import { usdcContractConfig } from "@/_test/constants";
+// import { setupEventStore } from "@/_test/setup";
+// import { publicClient } from "@/_test/utils";
+// import type { Contract } from "@/config/contracts";
+// import type { Network } from "@/config/networks";
+
+// import { buildReadOnlyContracts } from "./contract";
+
+// beforeEach((context) => setupEventStore(context));
+
+// const network: Network = {
+// name: "mainnet",
+// chainId: 1,
+// client: publicClient,
+// pollingInterval: 1_000,
+// defaultMaxBlockRange: 3,
+// finalityBlockCount: 10,
+// maxRpcRequestConcurrency: 10,
+// };
+
+// const contracts: Contract[] = [
+// {
+// name: "USDC",
+// address: usdcContractConfig.address,
+// abi: usdcContractConfig.abi,
+// network: network,
+// },
+// ];
+
+// // Test data generated from Alchemy Composer.
+// const usdcTotalSupply16375000 = 40921687992499550n;
+// const usdcTotalSupply16380000 = 40695630049769550n; // This is "latest" for our test setup.
+
+// test("getInjectedContract() returns data", async (context) => {
+// const { eventStore } = context;
+
+// const readOnlyContracts = buildReadOnlyContracts({
+// contracts,
+// getCurrentBlockNumber: () => 16375000n,
+// eventStore,
+// });
+// const contract = readOnlyContracts["USDC"];
+
+// const decimals = await contract.read.decimals();
+// expect(decimals).toBe(6);
+// });
+
+// test("getInjectedContract() uses current block number if no overrides are provided", async (context) => {
+// const { eventStore } = context;
+
+// const readOnlyContracts = buildReadOnlyContracts({
+// contracts,
+// getCurrentBlockNumber: () => 16375000n,
+// eventStore,
+// });
+// const contract = readOnlyContracts["USDC"];
+
+// const totalSupply = await contract.read.totalSupply();
+
+// expect(totalSupply).toBe(usdcTotalSupply16375000);
+// });
+
+// test("getInjectedContract() caches the read result if no overrides are provided", async (context) => {
+// const { eventStore } = context;
+
+// const callSpy = vi.spyOn(network.client, "call");
+
+// const readOnlyContracts = buildReadOnlyContracts({
+// contracts,
+// getCurrentBlockNumber: () => 16375000n,
+// eventStore,
+// });
+// const contract = readOnlyContracts["USDC"];
+
+// await contract.read.totalSupply();
+
+// expect(callSpy).toHaveBeenCalledTimes(1);
+
+// const cachedContractReadResult = await eventStore.getContractReadResult({
+// address: contract.address,
+// blockNumber: 16375000n,
+// chainId: 1,
+// data: getFunctionSelector("totalSupply()"),
+// });
+
+// expect(cachedContractReadResult).not.toBeNull();
+// expect(hexToBigInt(cachedContractReadResult!.result)).toBe(
+// usdcTotalSupply16375000
+// );
+
+// expect(callSpy).toHaveBeenCalledTimes(1);
+// });
+
+// test("getInjectedContract() uses blockTag override if provided", async (context) => {
+// const { eventStore } = context;
+
+// const readOnlyContracts = buildReadOnlyContracts({
+// contracts,
+// getCurrentBlockNumber: () => 16375000n,
+// eventStore,
+// });
+// const contract = readOnlyContracts["USDC"];
+
+// const totalSupply = await contract.read.totalSupply({
+// blockTag: "latest",
+// });
+
+// expect(totalSupply).toBe(usdcTotalSupply16380000);
+// });
+
+// test("getInjectedContract() does not cache data if blockTag override is provided", async (context) => {
+// const { eventStore } = context;
+
+// const readOnlyContracts = buildReadOnlyContracts({
+// contracts,
+// getCurrentBlockNumber: () => 16375000n,
+// eventStore,
+// });
+// const contract = readOnlyContracts["USDC"];
+
+// await contract.read.totalSupply({
+// blockTag: "latest",
+// });
+
+// const cachedContractReadResult = await eventStore.getContractReadResult({
+// address: contract.address,
+// blockNumber: 16375000n,
+// chainId: 1,
+// data: getFunctionSelector("totalSupply()"),
+// });
+
+// expect(cachedContractReadResult).toBeNull();
+// });
diff --git a/packages/core/src/indexing/contract.ts b/packages/core/src/indexing/contract.ts
index f1c7120dd..a76bf1600 100644
--- a/packages/core/src/indexing/contract.ts
+++ b/packages/core/src/indexing/contract.ts
@@ -1,151 +1,151 @@
-import {
- type Abi,
- type BaseError,
- type CallParameters,
- type GetContractReturnType,
- type Hex,
- type PublicClient,
- type ReadContractParameters,
- decodeFunctionResult,
- encodeFunctionData,
- getContract,
- getContractError,
-} from "viem";
-
-import type { Contract } from "@/config/contracts";
-import type { EventStore } from "@/event-store/store";
-
-export function buildReadOnlyContracts({
- contracts,
- eventStore,
- getCurrentBlockNumber,
-}: {
- contracts: Contract[];
- eventStore: EventStore;
- getCurrentBlockNumber: () => bigint;
-}): Record> {
- return contracts.reduce<
- Record>
- >((acc, { name, abi, address, network }) => {
- const { chainId, client: publicClient } = network;
-
- const readOnlyContract = getContract({ abi, address, publicClient });
-
- readOnlyContract.read = new Proxy(
- {},
- {
- get(_, functionName: string) {
- return async (
- ...parameters: [
- args?: readonly unknown[],
- options?: Omit<
- ReadContractParameters,
- "abi" | "address" | "functionName" | "args"
- >
- ]
- ) => {
- const { args, options } = getFunctionParameters(parameters);
-
- // If the user specified a block tag, serve the request as normal (no caching).
- if (options?.blockTag) {
- return publicClient.readContract({
- abi,
- address,
- functionName,
- args,
- ...options,
- } as ReadContractParameters);
- }
-
- // If the user specified a block number, use it, otherwise use the
- // block number of the current event being processed.
- const blockNumber = options?.blockNumber ?? getCurrentBlockNumber();
-
- const calldata = encodeFunctionData({ abi, args, functionName });
-
- const decodeRawResult = (rawResult: Hex) => {
- try {
- return decodeFunctionResult({
- abi,
- args,
- functionName,
- data: rawResult,
- });
- } catch (err) {
- throw getContractError(err as BaseError, {
- abi,
- address,
- args,
- docsPath: "/docs/contract/readContract",
- functionName,
- });
- }
- };
-
- // Check if this request can be served from the cache.
- const cachedContractReadResult =
- await eventStore.getContractReadResult({
- address,
- blockNumber,
- chainId,
- data: calldata,
- });
-
- if (cachedContractReadResult) {
- return decodeRawResult(cachedContractReadResult.result);
- }
-
- // Cache miss. Make the RPC request, then add to the cache.
- let rawResult: Hex;
- try {
- const { data } = await publicClient.call({
- data: calldata,
- to: address,
- ...{
- ...options,
- blockNumber,
- },
- } as unknown as CallParameters);
-
- rawResult = data || "0x";
- } catch (err) {
- throw getContractError(err as BaseError, {
- abi,
- address,
- args,
- docsPath: "/docs/contract/readContract",
- functionName,
- });
- }
-
- await eventStore.insertContractReadResult({
- address,
- blockNumber,
- chainId,
- data: calldata,
- result: rawResult,
- });
-
- return decodeRawResult(rawResult);
- };
- },
- }
- );
-
- acc[name] = readOnlyContract;
-
- return acc;
- }, {});
-}
-
-function getFunctionParameters(
- values: [args?: readonly unknown[], options?: object]
-) {
- const hasArgs = values.length && Array.isArray(values[0]);
- const args = hasArgs ? values[0]! : [];
- const options = ((hasArgs ? values[1] : values[0]) ?? {}) as Omit<
- ReadContractParameters,
- "abi" | "address" | "functionName" | "args"
- >;
- return { args, options };
-}
+// import {
+// type Abi,
+// type BaseError,
+// type CallParameters,
+// type GetContractReturnType,
+// type Hex,
+// type PublicClient,
+// type ReadContractParameters,
+// decodeFunctionResult,
+// encodeFunctionData,
+// getContract,
+// getContractError,
+// } from "viem";
+
+// // import type { Contract } from "@/config/contracts";
+// import type { EventStore } from "@/event-store/store";
+
+// export function buildReadOnlyContracts({
+// contracts,
+// eventStore,
+// getCurrentBlockNumber,
+// }: {
+// contracts: Contract[];
+// eventStore: EventStore;
+// getCurrentBlockNumber: () => bigint;
+// }): Record> {
+// return contracts.reduce<
+// Record>
+// >((acc, { name, abi, address, network }) => {
+// const { chainId, client: publicClient } = network;
+
+// const readOnlyContract = getContract({ abi, address, publicClient });
+
+// readOnlyContract.read = new Proxy(
+// {},
+// {
+// get(_, functionName: string) {
+// return async (
+// ...parameters: [
+// args?: readonly unknown[],
+// options?: Omit<
+// ReadContractParameters,
+// "abi" | "address" | "functionName" | "args"
+// >
+// ]
+// ) => {
+// const { args, options } = getFunctionParameters(parameters);
+
+// // If the user specified a block tag, serve the request as normal (no caching).
+// if (options?.blockTag) {
+// return publicClient.readContract({
+// abi,
+// address,
+// functionName,
+// args,
+// ...options,
+// } as ReadContractParameters);
+// }
+
+// // If the user specified a block number, use it, otherwise use the
+// // block number of the current event being processed.
+// const blockNumber = options?.blockNumber ?? getCurrentBlockNumber();
+
+// const calldata = encodeFunctionData({ abi, args, functionName });
+
+// const decodeRawResult = (rawResult: Hex) => {
+// try {
+// return decodeFunctionResult({
+// abi,
+// args,
+// functionName,
+// data: rawResult,
+// });
+// } catch (err) {
+// throw getContractError(err as BaseError, {
+// abi,
+// address,
+// args,
+// docsPath: "/docs/contract/readContract",
+// functionName,
+// });
+// }
+// };
+
+// // Check if this request can be served from the cache.
+// const cachedContractReadResult =
+// await eventStore.getContractReadResult({
+// address,
+// blockNumber,
+// chainId,
+// data: calldata,
+// });
+
+// if (cachedContractReadResult) {
+// return decodeRawResult(cachedContractReadResult.result);
+// }
+
+// // Cache miss. Make the RPC request, then add to the cache.
+// let rawResult: Hex;
+// try {
+// const { data } = await publicClient.call({
+// data: calldata,
+// to: address,
+// ...{
+// ...options,
+// blockNumber,
+// },
+// } as unknown as CallParameters);
+
+// rawResult = data || "0x";
+// } catch (err) {
+// throw getContractError(err as BaseError, {
+// abi,
+// address,
+// args,
+// docsPath: "/docs/contract/readContract",
+// functionName,
+// });
+// }
+
+// await eventStore.insertContractReadResult({
+// address,
+// blockNumber,
+// chainId,
+// data: calldata,
+// result: rawResult,
+// });
+
+// return decodeRawResult(rawResult);
+// };
+// },
+// }
+// );
+
+// acc[name] = readOnlyContract;
+
+// return acc;
+// }, {});
+// }
+
+// function getFunctionParameters(
+// values: [args?: readonly unknown[], options?: object]
+// ) {
+// const hasArgs = values.length && Array.isArray(values[0]);
+// const args = hasArgs ? values[0]! : [];
+// const options = ((hasArgs ? values[1] : values[0]) ?? {}) as Omit<
+// ReadContractParameters,
+// "abi" | "address" | "functionName" | "args"
+// >;
+// return { args, options };
+// }
diff --git a/packages/core/src/indexing/service.test.ts b/packages/core/src/indexing/service.test.ts
index 201c8aad3..c6d67452f 100644
--- a/packages/core/src/indexing/service.test.ts
+++ b/packages/core/src/indexing/service.test.ts
@@ -35,7 +35,7 @@ const logFilters = [
},
];
-const contracts = [{ name: "USDC", ...usdcContractConfig, network }];
+// const contracts = [{ name: "USDC", ...usdcContractConfig, network }];
const schema = buildSchema(
buildGraphqlSchema(`${schemaHeader}
@@ -123,7 +123,7 @@ test("processEvents() calls getEvents with sequential timestamp ranges", async (
eventStore,
userStore,
eventAggregatorService,
- contracts,
+ // contracts,
logFilters,
});
@@ -162,7 +162,7 @@ test("processEvents() calls indexing functions with correct arguments", async (c
eventStore,
userStore,
eventAggregatorService,
- contracts,
+ // contracts,
logFilters,
});
@@ -200,7 +200,7 @@ test("processEvents() model methods insert data into the user store", async (con
eventStore,
userStore,
eventAggregatorService,
- contracts,
+ // contracts,
logFilters,
});
@@ -225,7 +225,7 @@ test("processEvents() updates event count metrics", async (context) => {
eventStore,
userStore,
eventAggregatorService,
- contracts,
+ // contracts,
logFilters,
});
@@ -267,7 +267,7 @@ test("reset() reloads the user store", async (context) => {
eventStore,
userStore,
eventAggregatorService,
- contracts,
+ // contracts,
logFilters,
});
@@ -303,7 +303,7 @@ test("handleReorg() updates ponder_handlers_latest_processed_timestamp metric",
eventStore,
userStore,
eventAggregatorService,
- contracts,
+ // contracts,
logFilters,
});
@@ -335,7 +335,7 @@ test("handleReorg() reverts the user store", async (context) => {
eventStore,
userStore,
eventAggregatorService,
- contracts,
+ // contracts,
logFilters,
});
@@ -361,7 +361,7 @@ test("handleReorg() does nothing if there is a user error", async (context) => {
eventStore,
userStore,
eventAggregatorService,
- contracts,
+ // contracts,
logFilters,
});
@@ -391,7 +391,7 @@ test("handleReorg() processes the correct range of events after a reorg", async
eventStore,
userStore,
eventAggregatorService,
- contracts,
+ // contracts,
logFilters,
});
@@ -431,7 +431,7 @@ test("handleReorg() updates ponder_handlers_latest_processed_timestamp metric",
eventStore,
userStore,
eventAggregatorService,
- contracts,
+ // contracts,
logFilters,
});
diff --git a/packages/core/src/indexing/service.ts b/packages/core/src/indexing/service.ts
index f76c3d0c5..be3ed4a19 100644
--- a/packages/core/src/indexing/service.ts
+++ b/packages/core/src/indexing/service.ts
@@ -3,7 +3,6 @@ import Emittery from "emittery";
import type { IndexingFunctions } from "@/build/functions";
import { LogEventMetadata } from "@/config/abi";
-import type { Contract } from "@/config/contracts";
import { Factory } from "@/config/factories";
import type { LogFilter } from "@/config/logFilters";
import { UserError } from "@/errors/user";
@@ -22,7 +21,6 @@ import { prettyPrint } from "@/utils/print";
import { type Queue, type Worker, createQueue } from "@/utils/queue";
import { wait } from "@/utils/wait";
-import { buildReadOnlyContracts } from "./contract";
import { buildModels } from "./model";
import { getStackTrace } from "./trace";
@@ -56,15 +54,15 @@ export class IndexingService extends Emittery {
private eventsProcessedToTimestamp = 0;
private hasError = false;
- private currentEventBlockNumber = 0n;
+ // private currentEventBlockNumber = 0n;
private currentEventTimestamp = 0;
constructor({
common,
- eventStore,
+ // eventStore,
userStore,
eventAggregatorService,
- contracts,
+ // contracts,
logFilters = [],
factories = [],
}: {
@@ -72,7 +70,6 @@ export class IndexingService extends Emittery {
eventStore: EventStore;
userStore: UserStore;
eventAggregatorService: EventAggregatorService;
- contracts: Contract[];
logFilters?: LogFilter[];
factories?: Factory[];
}) {
@@ -85,11 +82,11 @@ export class IndexingService extends Emittery {
// The read-only contract objects only depend on config, so they can
// be built in the constructor (they can't be hot-reloaded).
- this.readOnlyContracts = buildReadOnlyContracts({
- contracts,
- getCurrentBlockNumber: () => this.currentEventBlockNumber,
- eventStore,
- });
+ // this.readOnlyContracts = buildReadOnlyContracts({
+ // contracts,
+ // getCurrentBlockNumber: () => this.currentEventBlockNumber,
+ // eventStore,
+ // });
this.eventProcessingMutex = new Mutex();
}
@@ -452,7 +449,7 @@ export class IndexingService extends Emittery {
// This enables contract calls occurring within the
// user code to use the event block number by default.
- this.currentEventBlockNumber = event.block.number;
+ // this.currentEventBlockNumber = event.block.number;
this.currentEventTimestamp = Number(event.block.timestamp);
try {
diff --git a/packages/core/src/realtime-sync/service.ts b/packages/core/src/realtime-sync/service.ts
index 90d7b4593..04d3d6308 100644
--- a/packages/core/src/realtime-sync/service.ts
+++ b/packages/core/src/realtime-sync/service.ts
@@ -8,9 +8,8 @@ import {
numberToHex,
} from "viem";
-import type { Factory } from "@/config/factories";
-import type { LogFilter } from "@/config/logFilters";
import type { Network } from "@/config/networks";
+import type { Source } from "@/config/sources";
import type { EventStore } from "@/event-store/store";
import type { Common } from "@/Ponder";
import { poll } from "@/utils/poll";
@@ -40,8 +39,7 @@ export class RealtimeSyncService extends Emittery {
private common: Common;
private eventStore: EventStore;
private network: Network;
- private logFilters: LogFilter[];
- private factories: Factory[];
+ private sources: Source[];
// Queue of unprocessed blocks.
private queue: RealtimeSyncQueue;
@@ -56,22 +54,19 @@ export class RealtimeSyncService extends Emittery {
common,
eventStore,
network,
- logFilters = [],
- factories = [],
+ sources = [],
}: {
common: Common;
eventStore: EventStore;
network: Network;
- logFilters?: LogFilter[];
- factories?: Factory[];
+ sources?: Source[];
}) {
super();
this.common = common;
this.eventStore = eventStore;
this.network = network;
- this.logFilters = logFilters;
- this.factories = factories;
+ this.sources = sources;
this.queue = this.buildQueue();
}
@@ -111,9 +106,7 @@ export class RealtimeSyncService extends Emittery {
// If an endBlock is specified for every event source on this network, and the
// latest end blcock is less than the finalized block number, we can stop here.
// The service won't poll for new blocks and won't emit any events.
- const endBlocks = [...this.logFilters, ...this.factories].map(
- (f) => f.endBlock
- );
+ const endBlocks = this.sources.map((f) => f.endBlock);
if (
endBlocks.every(
(endBlock) =>
diff --git a/packages/core/src/ui/app.tsx b/packages/core/src/ui/app.tsx
index f3b43ec34..87cbbd743 100644
--- a/packages/core/src/ui/app.tsx
+++ b/packages/core/src/ui/app.tsx
@@ -1,8 +1,7 @@
import { Box, Newline, render as inkRender, Text } from "ink";
import React from "react";
-import { Factory } from "@/config/factories";
-import type { LogFilter } from "@/config/logFilters";
+import { Source } from "@/config/sources";
import { HistoricalBar } from "./HistoricalBar";
import { IndexingBar } from "./IndexingBar";
@@ -25,13 +24,7 @@ export type UiState = {
networks: string[];
};
-export const buildUiState = ({
- logFilters,
- factories,
-}: {
- logFilters: LogFilter[];
- factories: Factory[];
-}) => {
+export const buildUiState = ({ sources }: { sources: Source[] }) => {
const ui: UiState = {
port: 0,
@@ -48,10 +41,7 @@ export const buildUiState = ({
networks: [],
};
- const eventSourceNames = [
- ...logFilters.map((l) => l.name),
- ...factories.map((f) => f.name),
- ];
+ const eventSourceNames = sources.map((s) => s.name);
eventSourceNames.forEach((name) => {
ui.historicalSyncEventSourceStats[name] = {
diff --git a/packages/core/src/ui/service.ts b/packages/core/src/ui/service.ts
index 301c64de0..78efaeaa6 100644
--- a/packages/core/src/ui/service.ts
+++ b/packages/core/src/ui/service.ts
@@ -1,35 +1,23 @@
-import type { Factory } from "@/config/factories";
-import type { LogFilter } from "@/config/logFilters";
+import { Source } from "@/config/sources";
import type { Common } from "@/Ponder";
import { type UiState, buildUiState, setupInkApp } from "./app";
export class UiService {
private common: Common;
- private logFilters: LogFilter[];
- private factories: Factory[];
+ private sources: Source[];
ui: UiState;
renderInterval: NodeJS.Timer;
render: () => void;
unmount: () => void;
- constructor({
- common,
- logFilters,
- factories,
- }: {
- common: Common;
- logFilters: LogFilter[];
- factories: Factory[];
- }) {
+ constructor({ common, sources }: { common: Common; sources: Source[] }) {
this.common = common;
- this.logFilters = logFilters;
- this.factories = factories;
+ this.sources = sources;
this.ui = buildUiState({
- logFilters: this.logFilters,
- factories: this.factories,
+ sources: this.sources,
});
if (this.common.options.uiEnabled) {
diff --git a/packages/core/src/utils/fragments.ts b/packages/core/src/utils/fragments.ts
index 775b90c34..c30575613 100644
--- a/packages/core/src/utils/fragments.ts
+++ b/packages/core/src/utils/fragments.ts
@@ -1,7 +1,6 @@
import type { Address, Hex } from "viem";
-import { FactoryCriteria } from "@/config/factories";
-import type { LogFilterCriteria } from "@/config/logFilters";
+import type { FactoryCriteria, LogFilterCriteria } from "@/config/sources";
/**
* Generates log filter fragments from a log filter.
From f794dae3d50b1ffb9b4c5e23599cbf4a364af0b6 Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 11:03:29 -0400
Subject: [PATCH 07/44] more work more progress
---
packages/core/src/build/functions.ts | 12 +-
packages/core/src/codegen/event.ts | 81 +---
packages/core/src/config/sources.ts | 4 +-
packages/core/src/event-aggregator/service.ts | 19 +-
.../core/src/event-store/postgres/store.ts | 9 +-
packages/core/src/event-store/sqlite/store.ts | 9 +-
packages/core/src/event-store/store.test.ts | 34 +-
packages/core/src/event-store/store.ts | 3 +-
packages/core/src/historical-sync/service.ts | 452 +++++++++---------
packages/core/src/indexing/service.ts | 27 +-
packages/core/src/realtime-sync/service.ts | 26 +-
11 files changed, 341 insertions(+), 335 deletions(-)
diff --git a/packages/core/src/build/functions.ts b/packages/core/src/build/functions.ts
index 0503f0690..a2818efdd 100644
--- a/packages/core/src/build/functions.ts
+++ b/packages/core/src/build/functions.ts
@@ -222,17 +222,17 @@ export const hydrateIndexingFunctions = ({
Object.entries(rawIndexingFunctions.eventSources).forEach(
([eventSourceName, eventSourceFunctions]) => {
- const logFilter = logFilters.find((l) => l.name === eventSourceName);
- const factory = factories.find((f) => f.name === eventSourceName);
+ // const logFilter = logFilters.find((l) => l.name === eventSourceName);
+ // const factory = factories.find((f) => f.name === eventSourceName);
- if (!logFilter && !factory) {
+ const source = sources.find((source) => source.name === eventSourceName);
+
+ if (!source) {
throw new Error(`Event source not found in config: ${eventSourceName}`);
}
Object.entries(eventSourceFunctions).forEach(([eventName, fn]) => {
- const eventData = logFilter
- ? logFilter.events[eventName]
- : factory?.events[eventName];
+ const eventData = source.events[eventName];
if (!eventData) {
throw new Error(`Log event not found in ABI: ${eventName}`);
diff --git a/packages/core/src/codegen/event.ts b/packages/core/src/codegen/event.ts
index b2232e7d1..f4180eede 100644
--- a/packages/core/src/codegen/event.ts
+++ b/packages/core/src/codegen/event.ts
@@ -2,62 +2,33 @@ import type { LogEventMetadata } from "@/config/abi";
import { Source } from "@/config/sources";
export const buildEventTypes = ({ sources }: { sources: Source[] }) => {
- const allIndexingFunctions = [
- ...logFilters.map((logFilter) => {
- return Object.values(logFilter.events)
- .filter((val): val is LogEventMetadata => !!val)
- .map(({ safeName, abiItem }) => {
- const paramsType = `{${abiItem.inputs
- .map((input, index) => {
- const inputName = input.name ? input.name : `param_${index}`;
- return `${inputName}:
- AbiParameterToPrimitiveType<${JSON.stringify(input)}>`;
- })
- .join(";")}}`;
+ const allIndexingFunctions = sources.map((source) =>
+ Object.values(source.events)
+ .filter((val): val is LogEventMetadata => !!val)
+ .map(({ safeName, abiItem }) => {
+ const paramsType = `{${abiItem.inputs
+ .map((input, index) => {
+ const inputName = input.name ? input.name : `param_${index}`;
+ return `${inputName}:
+ AbiParameterToPrimitiveType<${JSON.stringify(input)}>`;
+ })
+ .join(";")}}`;
- return `["${logFilter.name}:${safeName}"]: ({
- event, context
- }: {
- event: {
- name: "${abiItem.name}";
- params: ${paramsType};
- log: Log;
- block: Block;
- transaction: Transaction;
- };
- context: Context;
- }) => Promise | any;`;
- })
- .join("");
- }),
- ...factories.map((factory) => {
- return Object.values(factory.events)
- .filter((val): val is LogEventMetadata => !!val)
- .map(({ safeName, abiItem }) => {
- const paramsType = `{${abiItem.inputs
- .map((input, index) => {
- const inputName = input.name ? input.name : `param_${index}`;
- return `${inputName}:
- AbiParameterToPrimitiveType<${JSON.stringify(input)}>`;
- })
- .join(";")}}`;
-
- return `["${factory.name}:${safeName}"]: ({
- event, context
- }: {
- event: {
- name: "${abiItem.name}";
- params: ${paramsType};
- log: Log;
- block: Block;
- transaction: Transaction;
- };
- context: Context;
- }) => Promise | any;`;
- })
- .join("");
- }),
- ];
+ return `["${source.name}:${safeName}"]: ({
+ event, context
+ }: {
+ event: {
+ name: "${abiItem.name}";
+ params: ${paramsType};
+ log: Log;
+ block: Block;
+ transaction: Transaction;
+ };
+ context: Context;
+ }) => Promise | any;`;
+ })
+ .join("")
+ );
allIndexingFunctions.unshift(
`["setup"]: ({ context }: { context: Context; }) => Promise | any;`
diff --git a/packages/core/src/config/sources.ts b/packages/core/src/config/sources.ts
index 830d7419d..8a57612d1 100644
--- a/packages/core/src/config/sources.ts
+++ b/packages/core/src/config/sources.ts
@@ -8,9 +8,9 @@ import { ResolvedConfig } from "./types";
/**
* There are up to 4 topics in an EVM event
*
- * Technically, only the element could be an array
+ * Technically, only the first element could be an array
*/
-type Topics = [
+export type Topics = [
Hex | Hex[] | null,
Hex | Hex[] | null,
Hex | Hex[] | null,
diff --git a/packages/core/src/event-aggregator/service.ts b/packages/core/src/event-aggregator/service.ts
index 97f0a9117..544d1f78b 100644
--- a/packages/core/src/event-aggregator/service.ts
+++ b/packages/core/src/event-aggregator/service.ts
@@ -2,9 +2,8 @@ import Emittery from "emittery";
import { type Hex, decodeEventLog } from "viem";
import { LogEventMetadata } from "@/config/abi";
-import { Factory } from "@/config/factories";
-import type { LogFilter } from "@/config/logFilters";
import type { Network } from "@/config/networks";
+import { Source, sourceIsFactory, sourceIsLogFilter } from "@/config/sources";
import type { EventStore } from "@/event-store/store";
import type { Common } from "@/Ponder";
import type { Block } from "@/types/block";
@@ -44,8 +43,7 @@ export class EventAggregatorService extends Emittery {
private common: Common;
private eventStore: EventStore;
private networks: Network[];
- private logFilters: LogFilter[];
- private factories: Factory[];
+ private sources: Source[];
// Minimum timestamp at which events are available (across all networks).
checkpoint: number;
@@ -72,22 +70,19 @@ export class EventAggregatorService extends Emittery {
common,
eventStore,
networks,
- logFilters = [],
- factories = [],
+ sources = [],
}: {
common: Common;
eventStore: EventStore;
networks: Network[];
- logFilters?: LogFilter[];
- factories?: Factory[];
+ sources?: Source[];
}) {
super();
this.common = common;
this.eventStore = eventStore;
this.networks = networks;
- this.logFilters = logFilters;
- this.factories = factories;
+ this.sources = sources;
this.metrics = {};
this.checkpoint = 0;
@@ -129,7 +124,7 @@ export class EventAggregatorService extends Emittery {
const iterator = this.eventStore.getLogEvents({
fromTimestamp,
toTimestamp,
- logFilters: this.logFilters.map((logFilter) => ({
+ logFilters: this.sources.filter(sourceIsLogFilter).map((logFilter) => ({
name: logFilter.name,
chainId: logFilter.chainId,
criteria: logFilter.criteria,
@@ -139,7 +134,7 @@ export class EventAggregatorService extends Emittery {
indexingMetadata[logFilter.name]?.bySelector ?? {}
) as Hex[],
})),
- factories: this.factories.map((factory) => ({
+ factories: this.sources.filter(sourceIsFactory).map((factory) => ({
name: factory.name,
chainId: factory.chainId,
criteria: factory.criteria,
diff --git a/packages/core/src/event-store/postgres/store.ts b/packages/core/src/event-store/postgres/store.ts
index 3075812ac..bddc858c1 100644
--- a/packages/core/src/event-store/postgres/store.ts
+++ b/packages/core/src/event-store/postgres/store.ts
@@ -10,8 +10,11 @@ import {
import type { Pool } from "pg";
import type { Address, Hex, RpcBlock, RpcLog, RpcTransaction } from "viem";
-import { type FactoryCriteria } from "@/config/factories";
-import type { LogFilterCriteria } from "@/config/logFilters";
+import type {
+ FactoryCriteria,
+ LogFilterCriteria,
+ Topics,
+} from "@/config/sources";
import type { Block } from "@/types/block";
import type { Log } from "@/types/log";
import type { Transaction } from "@/types/transaction";
@@ -541,7 +544,7 @@ export class PostgresEventStore implements EventStore {
...logFilters,
...factories.map((f) => ({
address: f.address,
- topics: [f.eventSelector],
+ topics: [f.eventSelector, null, null, null] as Topics,
})),
],
interval,
diff --git a/packages/core/src/event-store/sqlite/store.ts b/packages/core/src/event-store/sqlite/store.ts
index ab04ae122..881ef2151 100644
--- a/packages/core/src/event-store/sqlite/store.ts
+++ b/packages/core/src/event-store/sqlite/store.ts
@@ -9,8 +9,11 @@ import {
} from "kysely";
import type { Address, Hex, RpcBlock, RpcLog, RpcTransaction } from "viem";
-import { type FactoryCriteria } from "@/config/factories";
-import type { LogFilterCriteria } from "@/config/logFilters";
+import type {
+ FactoryCriteria,
+ LogFilterCriteria,
+ Topics,
+} from "@/config/sources";
import type { Block } from "@/types/block";
import type { Log } from "@/types/log";
import type { Transaction } from "@/types/transaction";
@@ -509,7 +512,7 @@ export class SqliteEventStore implements EventStore {
...logFilters,
...factories.map((f) => ({
address: f.address,
- topics: [f.eventSelector],
+ topics: [f.eventSelector, null, null, null] as Topics,
})),
],
interval,
diff --git a/packages/core/src/event-store/store.test.ts b/packages/core/src/event-store/store.test.ts
index b1273fc15..cb1766262 100644
--- a/packages/core/src/event-store/store.test.ts
+++ b/packages/core/src/event-store/store.test.ts
@@ -12,8 +12,7 @@ import {
usdcContractConfig,
} from "@/_test/constants";
import { setupEventStore } from "@/_test/setup";
-import type { FactoryCriteria } from "@/config/factories";
-import type { LogFilterCriteria } from "@/config/logFilters";
+import type { FactoryCriteria, LogFilterCriteria } from "@/config/sources";
beforeEach((context) => setupEventStore(context));
@@ -246,7 +245,7 @@ test("getLogFilterRanges handles complex log filter inclusivity rules", async (c
await eventStore.insertLogFilterInterval({
chainId: 1,
- logFilter: { topics: [null, ["0xc", "0xd"]] },
+ logFilter: { topics: [null, ["0xc", "0xd"], null, null] },
block: blockOne,
transactions: [],
logs: [],
@@ -263,7 +262,7 @@ test("getLogFilterRanges handles complex log filter inclusivity rules", async (c
// Narrower criteria includes both broad and specific intervals.
logFilterIntervals = await eventStore.getLogFilterIntervals({
chainId: 1,
- logFilter: { topics: [null, "0xc"] },
+ logFilter: { topics: [null, "0xc", null, null] },
});
expect(logFilterIntervals).toMatchObject([
[0, 100],
@@ -671,6 +670,9 @@ test("getFactoryLogFilterIntervals handles topic filtering rules", async (contex
...factoryCriteria,
topics: [
"0x0000000000000000000000000000000000000000000factoryeventsignature",
+ null,
+ null,
+ null,
],
} as FactoryCriteria,
});
@@ -740,7 +742,7 @@ test("insertRealtimeInterval inserts log filter intervals", async (context) => {
chainId: 1,
logFilter: {
address: factoryCriteriaOne.address,
- topics: [factoryCriteriaOne.eventSelector],
+ topics: [factoryCriteriaOne.eventSelector, null, null, null],
},
})
).toMatchObject([[500, 550]]);
@@ -749,7 +751,7 @@ test("insertRealtimeInterval inserts log filter intervals", async (context) => {
chainId: 1,
logFilter: {
address: factoryCriteriaOne.address,
- topics: [factoryCriteriaOne.eventSelector],
+ topics: [factoryCriteriaOne.eventSelector, null, null, null],
},
})
).toMatchObject([[500, 550]]);
@@ -1263,7 +1265,14 @@ test("getLogEvents filters on log filter with single topic", async (context) =>
{
name: "singleTopic",
chainId: 1,
- criteria: { topics: [blockOneLogs[0].topics[0] as `0x${string}`] },
+ criteria: {
+ topics: [
+ blockOneLogs[0].topics[0] as `0x${string}`,
+ null,
+ null,
+ null,
+ ],
+ },
},
],
});
@@ -1313,6 +1322,8 @@ test("getLogEvents filters on log filter with multiple topics", async (context)
topics: [
blockOneLogs[0].topics[0] as `0x${string}`,
blockOneLogs[0].topics[1] as `0x${string}`,
+ null,
+ null,
],
},
},
@@ -1454,7 +1465,14 @@ test("getLogEvents filters on multiple filters", async (context) => {
{
name: "singleTopic", // This should match blockOneLogs[0] AND blockTwoLogs[0]
chainId: 1,
- criteria: { topics: [blockOneLogs[0].topics[0] as `0x${string}`] },
+ criteria: {
+ topics: [
+ blockOneLogs[0].topics[0] as `0x${string}`,
+ null,
+ null,
+ null,
+ ],
+ },
},
],
});
diff --git a/packages/core/src/event-store/store.ts b/packages/core/src/event-store/store.ts
index f5275d761..2c01c2882 100644
--- a/packages/core/src/event-store/store.ts
+++ b/packages/core/src/event-store/store.ts
@@ -1,8 +1,7 @@
import type { Kysely, Migrator } from "kysely";
import type { Address, Hex, RpcBlock, RpcLog, RpcTransaction } from "viem";
-import { FactoryCriteria } from "@/config/factories";
-import { LogFilterCriteria } from "@/config/logFilters";
+import type { FactoryCriteria, LogFilterCriteria } from "@/config/sources";
import type { Block } from "@/types/block";
import type { Log } from "@/types/log";
import type { Transaction } from "@/types/transaction";
diff --git a/packages/core/src/historical-sync/service.ts b/packages/core/src/historical-sync/service.ts
index 8cd06a8fd..acecd6629 100644
--- a/packages/core/src/historical-sync/service.ts
+++ b/packages/core/src/historical-sync/service.ts
@@ -12,10 +12,14 @@ import {
toHex,
} from "viem";
-import type { Factory } from "@/config/factories";
-import type { LogFilter, LogFilterCriteria } from "@/config/logFilters";
import type { Network } from "@/config/networks";
-import { Source } from "@/config/sources";
+import {
+ type Factory,
+ type LogFilter,
+ type LogFilterCriteria,
+ type Source,
+ sourceIsLogFilter,
+} from "@/config/sources";
import type { EventStore } from "@/event-store/store";
import type { Common } from "@/Ponder";
import { formatEta, formatPercentage } from "@/utils/format";
@@ -154,252 +158,264 @@ export class HistoricalSyncService extends Emittery {
}) {
this.finalizedBlockNumber = finalizedBlockNumber;
- await Promise.all([
- ...this.logFilters.map(async (logFilter) => {
+ await Promise.all(
+ this.sources.map(async (source) => {
const { isHistoricalSyncRequired, startBlock, endBlock } =
validateHistoricalBlockRange({
- startBlock: logFilter.startBlock,
- endBlock: logFilter.endBlock,
+ startBlock: source.startBlock,
+ endBlock: source.endBlock,
finalizedBlockNumber,
latestBlockNumber,
});
- if (!isHistoricalSyncRequired) {
- this.logFilterProgressTrackers[logFilter.name] = new ProgressTracker({
- target: [startBlock, finalizedBlockNumber],
- completed: [[startBlock, finalizedBlockNumber]],
- });
- this.common.metrics.ponder_historical_total_blocks.set(
- { network: this.network.name, eventSource: logFilter.name },
- 0
- );
- this.common.logger.warn({
- service: "historical",
- msg: `Start block is in unfinalized range, skipping historical sync (eventSource=${logFilter.name})`,
+ if (sourceIsLogFilter(source)) {
+ // Log filter
+
+ if (!isHistoricalSyncRequired) {
+ this.logFilterProgressTrackers[source.name] = new ProgressTracker({
+ target: [startBlock, finalizedBlockNumber],
+ completed: [[startBlock, finalizedBlockNumber]],
+ });
+ this.common.metrics.ponder_historical_total_blocks.set(
+ { network: this.network.name, eventSource: source.name },
+ 0
+ );
+ this.common.logger.warn({
+ service: "historical",
+ msg: `Start block is in unfinalized range, skipping historical sync (eventSource=${source.name})`,
+ });
+ return;
+ }
+
+ const completedLogFilterIntervals =
+ await this.eventStore.getLogFilterIntervals({
+ chainId: source.chainId,
+ logFilter: {
+ address: source.criteria.address,
+ topics: source.criteria.topics,
+ },
+ });
+ const logFilterProgressTracker = new ProgressTracker({
+ target: [startBlock, endBlock],
+ completed: completedLogFilterIntervals,
});
- return;
- }
+ this.logFilterProgressTrackers[source.name] =
+ logFilterProgressTracker;
- const completedLogFilterIntervals =
- await this.eventStore.getLogFilterIntervals({
- chainId: logFilter.chainId,
- logFilter: {
- address: logFilter.criteria.address,
- topics: logFilter.criteria.topics,
- },
+ const requiredLogFilterIntervals =
+ logFilterProgressTracker.getRequired();
+
+ const logFilterTaskChunks = getChunks({
+ intervals: requiredLogFilterIntervals,
+ maxChunkSize:
+ source.maxBlockRange ?? this.network.defaultMaxBlockRange,
});
- const logFilterProgressTracker = new ProgressTracker({
- target: [startBlock, endBlock],
- completed: completedLogFilterIntervals,
- });
- this.logFilterProgressTrackers[logFilter.name] =
- logFilterProgressTracker;
- const requiredLogFilterIntervals =
- logFilterProgressTracker.getRequired();
+ for (const [fromBlock, toBlock] of logFilterTaskChunks) {
+ this.queue.addTask(
+ { kind: "LOG_FILTER", logFilter: source, fromBlock, toBlock },
+ { priority: Number.MAX_SAFE_INTEGER - fromBlock }
+ );
+ }
+ if (logFilterTaskChunks.length > 0) {
+ const total = intervalSum(requiredLogFilterIntervals);
+ this.common.logger.debug({
+ service: "historical",
+ msg: `Added LOG_FILTER tasks for ${total}-block range (logFilter=${source.name}, network=${this.network.name})`,
+ });
+ }
- const logFilterTaskChunks = getChunks({
- intervals: requiredLogFilterIntervals,
- maxChunkSize:
- logFilter.maxBlockRange ?? this.network.defaultMaxBlockRange,
- });
+ const targetBlockCount = endBlock - startBlock + 1;
+ const cachedBlockCount = intervalSum(completedLogFilterIntervals);
- for (const [fromBlock, toBlock] of logFilterTaskChunks) {
- this.queue.addTask(
- { kind: "LOG_FILTER", logFilter, fromBlock, toBlock },
- { priority: Number.MAX_SAFE_INTEGER - fromBlock }
+ this.common.metrics.ponder_historical_total_blocks.set(
+ { network: this.network.name, eventSource: source.name },
+ targetBlockCount
);
- }
- if (logFilterTaskChunks.length > 0) {
- const total = intervalSum(requiredLogFilterIntervals);
- this.common.logger.debug({
+ this.common.metrics.ponder_historical_cached_blocks.set(
+ { network: this.network.name, eventSource: source.name },
+ cachedBlockCount
+ );
+
+ this.common.logger.info({
service: "historical",
- msg: `Added LOG_FILTER tasks for ${total}-block range (logFilter=${logFilter.name}, network=${this.network.name})`,
+ msg: `Started sync with ${formatPercentage(
+ Math.min(1, cachedBlockCount / (targetBlockCount || 1))
+ )} cached (eventSource=${source.name} network=${
+ this.network.name
+ })`,
});
- }
-
- const targetBlockCount = endBlock - startBlock + 1;
- const cachedBlockCount = intervalSum(completedLogFilterIntervals);
-
- this.common.metrics.ponder_historical_total_blocks.set(
- { network: this.network.name, eventSource: logFilter.name },
- targetBlockCount
- );
- this.common.metrics.ponder_historical_cached_blocks.set(
- { network: this.network.name, eventSource: logFilter.name },
- cachedBlockCount
- );
+ } else {
+ // Factory
+
+ if (!isHistoricalSyncRequired) {
+ this.factoryChildAddressProgressTrackers[source.name] =
+ new ProgressTracker({
+ target: [startBlock, finalizedBlockNumber],
+ completed: [[startBlock, finalizedBlockNumber]],
+ });
+ this.factoryLogFilterProgressTrackers[source.name] =
+ new ProgressTracker({
+ target: [startBlock, finalizedBlockNumber],
+ completed: [[startBlock, finalizedBlockNumber]],
+ });
+ this.common.metrics.ponder_historical_total_blocks.set(
+ { network: this.network.name, eventSource: source.name },
+ 0
+ );
+ this.common.logger.warn({
+ service: "historical",
+ msg: `Start block is in unfinalized range, skipping historical sync (eventSource=${source.name})`,
+ });
+ return;
+ }
- this.common.logger.info({
- service: "historical",
- msg: `Started sync with ${formatPercentage(
- Math.min(1, cachedBlockCount / (targetBlockCount || 1))
- )} cached (eventSource=${logFilter.name} network=${
- this.network.name
- })`,
- });
- }),
- ...this.factories.map(async (factory) => {
- const { isHistoricalSyncRequired, startBlock, endBlock } =
- validateHistoricalBlockRange({
- startBlock: factory.startBlock,
- endBlock: factory.endBlock,
- finalizedBlockNumber,
- latestBlockNumber,
+ // Note that factory child address progress is stored using
+ // log intervals for the factory log.
+ const completedFactoryChildAddressIntervals =
+ await this.eventStore.getLogFilterIntervals({
+ chainId: source.chainId,
+ logFilter: {
+ address: source.criteria.address,
+ topics: [source.criteria.eventSelector, null, null, null],
+ },
+ });
+ const factoryChildAddressProgressTracker = new ProgressTracker({
+ target: [startBlock, endBlock],
+ completed: completedFactoryChildAddressIntervals,
+ });
+ this.factoryChildAddressProgressTrackers[source.name] =
+ factoryChildAddressProgressTracker;
+
+ const requiredFactoryChildAddressIntervals =
+ factoryChildAddressProgressTracker.getRequired();
+ const factoryChildAddressTaskChunks = getChunks({
+ intervals: requiredFactoryChildAddressIntervals,
+ maxChunkSize:
+ source.maxBlockRange ?? this.network.defaultMaxBlockRange,
});
- if (!isHistoricalSyncRequired) {
- this.factoryChildAddressProgressTrackers[factory.name] =
- new ProgressTracker({
- target: [startBlock, finalizedBlockNumber],
- completed: [[startBlock, finalizedBlockNumber]],
- });
- this.factoryLogFilterProgressTrackers[factory.name] =
- new ProgressTracker({
- target: [startBlock, finalizedBlockNumber],
- completed: [[startBlock, finalizedBlockNumber]],
+ for (const [fromBlock, toBlock] of factoryChildAddressTaskChunks) {
+ this.queue.addTask(
+ {
+ kind: "FACTORY_CHILD_ADDRESS",
+ factory: source,
+ fromBlock,
+ toBlock,
+ },
+ { priority: Number.MAX_SAFE_INTEGER - fromBlock }
+ );
+ }
+ if (factoryChildAddressTaskChunks.length > 0) {
+ const total = intervalSum(requiredFactoryChildAddressIntervals);
+ this.common.logger.debug({
+ service: "historical",
+ msg: `Added FACTORY_CHILD_ADDRESS tasks for ${total}-block range (factory=${source.name}, network=${this.network.name})`,
});
- this.common.metrics.ponder_historical_total_blocks.set(
- { network: this.network.name, eventSource: factory.name },
- 0
+ }
+
+ const targetFactoryChildAddressBlockCount = endBlock - startBlock + 1;
+ const cachedFactoryChildAddressBlockCount = intervalSum(
+ completedFactoryChildAddressIntervals
);
- this.common.logger.warn({
- service: "historical",
- msg: `Start block is in unfinalized range, skipping historical sync (eventSource=${factory.name})`,
- });
- return;
- }
- // Note that factory child address progress is stored using
- // log intervals for the factory log.
- const completedFactoryChildAddressIntervals =
- await this.eventStore.getLogFilterIntervals({
- chainId: factory.chainId,
- logFilter: {
- address: factory.criteria.address,
- topics: [factory.criteria.eventSelector],
+ this.common.metrics.ponder_historical_total_blocks.set(
+ {
+ network: this.network.name,
+ eventSource: `${source.name}_factory`,
},
- });
- const factoryChildAddressProgressTracker = new ProgressTracker({
- target: [startBlock, endBlock],
- completed: completedFactoryChildAddressIntervals,
- });
- this.factoryChildAddressProgressTrackers[factory.name] =
- factoryChildAddressProgressTracker;
-
- const requiredFactoryChildAddressIntervals =
- factoryChildAddressProgressTracker.getRequired();
- const factoryChildAddressTaskChunks = getChunks({
- intervals: requiredFactoryChildAddressIntervals,
- maxChunkSize:
- factory.maxBlockRange ?? this.network.defaultMaxBlockRange,
- });
-
- for (const [fromBlock, toBlock] of factoryChildAddressTaskChunks) {
- this.queue.addTask(
- { kind: "FACTORY_CHILD_ADDRESS", factory, fromBlock, toBlock },
- { priority: Number.MAX_SAFE_INTEGER - fromBlock }
+ targetFactoryChildAddressBlockCount
);
- }
- if (factoryChildAddressTaskChunks.length > 0) {
- const total = intervalSum(requiredFactoryChildAddressIntervals);
- this.common.logger.debug({
- service: "historical",
- msg: `Added FACTORY_CHILD_ADDRESS tasks for ${total}-block range (factory=${factory.name}, network=${this.network.name})`,
+ this.common.metrics.ponder_historical_cached_blocks.set(
+ {
+ network: this.network.name,
+ eventSource: `${source.name}_factory`,
+ },
+ cachedFactoryChildAddressBlockCount
+ );
+
+ const completedFactoryLogFilterIntervals =
+ await this.eventStore.getFactoryLogFilterIntervals({
+ chainId: source.chainId,
+ factory: source.criteria,
+ });
+ const factoryLogFilterProgressTracker = new ProgressTracker({
+ target: [startBlock, endBlock],
+ completed: completedFactoryLogFilterIntervals,
});
- }
+ this.factoryLogFilterProgressTrackers[source.name] =
+ factoryLogFilterProgressTracker;
+
+ // Manually add factory log filter tasks for any intervals where the
+ // child address tasks are completed, but the child log filter tasks are not,
+ // because these won't be added automatically by the factory child address tasks.
+ const requiredFactoryLogFilterIntervals =
+ factoryLogFilterProgressTracker.getRequired();
+ const missingFactoryLogFilterIntervals = intervalDifference(
+ requiredFactoryLogFilterIntervals,
+ requiredFactoryChildAddressIntervals
+ );
- const targetFactoryChildAddressBlockCount = endBlock - startBlock + 1;
- const cachedFactoryChildAddressBlockCount = intervalSum(
- completedFactoryChildAddressIntervals
- );
+ const missingFactoryLogFilterTaskChunks = getChunks({
+ intervals: missingFactoryLogFilterIntervals,
+ maxChunkSize:
+ source.maxBlockRange ?? this.network.defaultMaxBlockRange,
+ });
- this.common.metrics.ponder_historical_total_blocks.set(
- {
- network: this.network.name,
- eventSource: `${factory.name}_factory`,
- },
- targetFactoryChildAddressBlockCount
- );
- this.common.metrics.ponder_historical_cached_blocks.set(
- {
- network: this.network.name,
- eventSource: `${factory.name}_factory`,
- },
- cachedFactoryChildAddressBlockCount
- );
+ for (const [
+ fromBlock,
+ toBlock,
+ ] of missingFactoryLogFilterTaskChunks) {
+ this.queue.addTask(
+ {
+ kind: "FACTORY_LOG_FILTER",
+ factory: source,
+ fromBlock,
+ toBlock,
+ },
+ { priority: Number.MAX_SAFE_INTEGER - fromBlock }
+ );
+ }
+ if (missingFactoryLogFilterTaskChunks.length > 0) {
+ const total = intervalSum(missingFactoryLogFilterIntervals);
+ this.common.logger.debug({
+ service: "historical",
+ msg: `Added FACTORY_LOG_FILTER tasks for ${total}-block range (factory=${source.name}, network=${this.network.name})`,
+ });
+ }
- const completedFactoryLogFilterIntervals =
- await this.eventStore.getFactoryLogFilterIntervals({
- chainId: factory.chainId,
- factory: factory.criteria,
- });
- const factoryLogFilterProgressTracker = new ProgressTracker({
- target: [startBlock, endBlock],
- completed: completedFactoryLogFilterIntervals,
- });
- this.factoryLogFilterProgressTrackers[factory.name] =
- factoryLogFilterProgressTracker;
-
- // Manually add factory log filter tasks for any intervals where the
- // child address tasks are completed, but the child log filter tasks are not,
- // because these won't be added automatically by the factory child address tasks.
- const requiredFactoryLogFilterIntervals =
- factoryLogFilterProgressTracker.getRequired();
- const missingFactoryLogFilterIntervals = intervalDifference(
- requiredFactoryLogFilterIntervals,
- requiredFactoryChildAddressIntervals
- );
+ const targetFactoryLogFilterBlockCount = endBlock - startBlock + 1;
+ const cachedFactoryLogFilterBlockCount = intervalSum(
+ completedFactoryLogFilterIntervals
+ );
- const missingFactoryLogFilterTaskChunks = getChunks({
- intervals: missingFactoryLogFilterIntervals,
- maxChunkSize:
- factory.maxBlockRange ?? this.network.defaultMaxBlockRange,
- });
+ this.common.metrics.ponder_historical_total_blocks.set(
+ { network: this.network.name, eventSource: source.name },
+ targetFactoryLogFilterBlockCount
+ );
+ this.common.metrics.ponder_historical_cached_blocks.set(
+ { network: this.network.name, eventSource: source.name },
+ cachedFactoryLogFilterBlockCount
+ );
- for (const [fromBlock, toBlock] of missingFactoryLogFilterTaskChunks) {
- this.queue.addTask(
- { kind: "FACTORY_LOG_FILTER", factory, fromBlock, toBlock },
- { priority: Number.MAX_SAFE_INTEGER - fromBlock }
+ // Use factory log filter progress for the logger because it better represents
+ // user-facing progress.
+ const cacheRate = Math.min(
+ 1,
+ cachedFactoryLogFilterBlockCount /
+ (targetFactoryLogFilterBlockCount || 1)
);
- }
- if (missingFactoryLogFilterTaskChunks.length > 0) {
- const total = intervalSum(missingFactoryLogFilterIntervals);
- this.common.logger.debug({
+ this.common.logger.info({
service: "historical",
- msg: `Added FACTORY_LOG_FILTER tasks for ${total}-block range (factory=${factory.name}, network=${this.network.name})`,
+ msg: `Started sync with ${formatPercentage(
+ cacheRate
+ )} cached (eventSource=${source.name} network=${
+ this.network.name
+ })`,
});
}
-
- const targetFactoryLogFilterBlockCount = endBlock - startBlock + 1;
- const cachedFactoryLogFilterBlockCount = intervalSum(
- completedFactoryLogFilterIntervals
- );
-
- this.common.metrics.ponder_historical_total_blocks.set(
- { network: this.network.name, eventSource: factory.name },
- targetFactoryLogFilterBlockCount
- );
- this.common.metrics.ponder_historical_cached_blocks.set(
- { network: this.network.name, eventSource: factory.name },
- cachedFactoryLogFilterBlockCount
- );
-
- // Use factory log filter progress for the logger because it better represents
- // user-facing progress.
- const cacheRate = Math.min(
- 1,
- cachedFactoryLogFilterBlockCount /
- (targetFactoryLogFilterBlockCount || 1)
- );
- this.common.logger.info({
- service: "historical",
- msg: `Started sync with ${formatPercentage(
- cacheRate
- )} cached (eventSource=${factory.name} network=${this.network.name})`,
- });
- }),
- ]);
+ })
+ );
}
start() {
@@ -611,7 +627,7 @@ export class HistoricalSyncService extends Emittery {
const logs = await this._eth_getLogs({
address: factory.criteria.address,
- topics: [factory.criteria.eventSelector],
+ topics: [factory.criteria.eventSelector, null, null, null],
fromBlock: toHex(fromBlock),
toBlock: toHex(toBlock),
});
@@ -633,7 +649,7 @@ export class HistoricalSyncService extends Emittery {
chainId: factory.chainId,
logFilter: {
address: factory.criteria.address,
- topics: [factory.criteria.eventSelector],
+ topics: [factory.criteria.eventSelector, null, null, null],
},
block,
transactions: block.transactions.filter((tx) =>
diff --git a/packages/core/src/indexing/service.ts b/packages/core/src/indexing/service.ts
index be3ed4a19..5149c9aca 100644
--- a/packages/core/src/indexing/service.ts
+++ b/packages/core/src/indexing/service.ts
@@ -3,8 +3,7 @@ import Emittery from "emittery";
import type { IndexingFunctions } from "@/build/functions";
import { LogEventMetadata } from "@/config/abi";
-import { Factory } from "@/config/factories";
-import type { LogFilter } from "@/config/logFilters";
+import type { Source } from "@/config/sources";
import { UserError } from "@/errors/user";
import type {
EventAggregatorService,
@@ -37,8 +36,7 @@ export class IndexingService extends Emittery {
private common: Common;
private userStore: UserStore;
private eventAggregatorService: EventAggregatorService;
- private logFilters: LogFilter[];
- private factories: Factory[];
+ private sources: Source[];
private readOnlyContracts: Record = {};
@@ -62,23 +60,20 @@ export class IndexingService extends Emittery {
// eventStore,
userStore,
eventAggregatorService,
- // contracts,
- logFilters = [],
- factories = [],
+ sources = [],
}: {
common: Common;
eventStore: EventStore;
userStore: UserStore;
eventAggregatorService: EventAggregatorService;
- logFilters?: LogFilter[];
- factories?: Factory[];
+
+ sources?: Source[];
}) {
super();
this.common = common;
this.userStore = userStore;
this.eventAggregatorService = eventAggregatorService;
- this.logFilters = logFilters;
- this.factories = factories;
+ this.sources = sources;
// The read-only contract objects only depend on config, so they can
// be built in the constructor (they can't be hot-reloaded).
@@ -290,12 +285,10 @@ export class IndexingService extends Emittery {
// Increment the metrics for the total number of matching & indexed events in this timestamp range.
if (pageIndex === 0) {
metadata.counts.forEach(({ eventSourceName, selector, count }) => {
- const safeName = Object.values({
- ...(this.logFilters.find((f) => f.name === eventSourceName)
- ?.events || {}),
- ...(this.factories.find((f) => f.name === eventSourceName)
- ?.events || {}),
- })
+ const safeName = Object.values(
+ this.sources.find((s) => s.name === eventSourceName)?.events ||
+ {}
+ )
.filter((m): m is LogEventMetadata => !!m)
.find((m) => m.selector === selector)?.safeName;
diff --git a/packages/core/src/realtime-sync/service.ts b/packages/core/src/realtime-sync/service.ts
index 04d3d6308..bafd6344e 100644
--- a/packages/core/src/realtime-sync/service.ts
+++ b/packages/core/src/realtime-sync/service.ts
@@ -9,7 +9,11 @@ import {
} from "viem";
import type { Network } from "@/config/networks";
-import type { Source } from "@/config/sources";
+import {
+ type Source,
+ sourceIsFactory,
+ sourceIsLogFilter,
+} from "@/config/sources";
import type { EventStore } from "@/event-store/store";
import type { Common } from "@/Ponder";
import { poll } from "@/utils/poll";
@@ -267,12 +271,12 @@ export class RealtimeSyncService extends Emittery {
let logs: RpcLog[];
let matchedLogs: RpcLog[];
- if (this.factories.length === 0) {
+ if (!this.sources.some(sourceIsFactory)) {
// If there are no factory contracts, we can attempt to skip calling eth_getLogs by
// checking if the block logsBloom matches any of the log filters.
const doesBlockHaveLogFilterLogs = isMatchedLogInBloomFilter({
bloom: newBlockWithTransactions.logsBloom!,
- logFilters: this.logFilters.map((l) => l.criteria),
+ logFilters: this.sources.map((s) => s.criteria),
});
if (!doesBlockHaveLogFilterLogs) {
@@ -296,7 +300,7 @@ export class RealtimeSyncService extends Emittery {
matchedLogs = filterLogs({
logs,
- logFilters: this.logFilters.map((l) => l.criteria),
+ logFilters: this.sources.map((s) => s.criteria),
});
}
} else {
@@ -314,7 +318,7 @@ export class RealtimeSyncService extends Emittery {
// Find and insert any new child contracts.
await Promise.all(
- this.factories.map(async (factory) => {
+ this.sources.filter(sourceIsFactory).map(async (factory) => {
const matchedFactoryLogs = filterLogs({
logs,
logFilters: [
@@ -337,7 +341,7 @@ export class RealtimeSyncService extends Emittery {
// a potentially slow DB operation here. It's a tradeoff between sync
// latency and database growth.
const factoryLogFilters = await Promise.all(
- this.factories.map(async (factory) => {
+ this.sources.filter(sourceIsFactory).map(async (factory) => {
const iterator = this.eventStore.getFactoryChildAddresses({
chainId: this.network.chainId,
factory: factory.criteria,
@@ -357,7 +361,7 @@ export class RealtimeSyncService extends Emittery {
matchedLogs = filterLogs({
logs,
logFilters: [
- ...this.logFilters.map((l) => l.criteria),
+ ...this.sources.filter(sourceIsLogFilter).map((l) => l.criteria),
...factoryLogFilters,
],
});
@@ -439,8 +443,12 @@ export class RealtimeSyncService extends Emittery {
// 3) Child filter intervals
await this.eventStore.insertRealtimeInterval({
chainId: this.network.chainId,
- logFilters: this.logFilters.map((l) => l.criteria),
- factories: this.factories.map((f) => f.criteria),
+ logFilters: this.sources
+ .filter(sourceIsLogFilter)
+ .map((l) => l.criteria),
+ factories: this.sources
+ .filter(sourceIsFactory)
+ .map((f) => f.criteria),
interval: {
startBlock: BigInt(this.finalizedBlockNumber + 1),
endBlock: BigInt(newFinalizedBlock.number),
From 22cdc993be1feb888fa644829959d36895d94b14 Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 11:20:11 -0400
Subject: [PATCH 08/44] fix type issues with tests
---
.../core/src/event-aggregator/service.test.ts | 23 +++++-----
.../core/src/historical-sync/service.test.ts | 46 +++++++++----------
packages/core/src/indexing/service.test.ts | 36 ++++++---------
.../core/src/realtime-sync/service.test.ts | 33 ++++++-------
4 files changed, 65 insertions(+), 73 deletions(-)
diff --git a/packages/core/src/event-aggregator/service.test.ts b/packages/core/src/event-aggregator/service.test.ts
index 84be0850a..e77aeb684 100644
--- a/packages/core/src/event-aggregator/service.test.ts
+++ b/packages/core/src/event-aggregator/service.test.ts
@@ -3,8 +3,8 @@ import { beforeEach, expect, test, vi } from "vitest";
import { usdcContractConfig } from "@/_test/constants";
import { setupEventStore } from "@/_test/setup";
import { publicClient } from "@/_test/utils";
-import type { LogFilter } from "@/config/logFilters";
import type { Network } from "@/config/networks";
+import type { Source } from "@/config/sources";
import { EventAggregatorService } from "./service";
@@ -35,9 +35,10 @@ const usdcLogFilter = {
chainId: mainnet.chainId,
criteria: { address: usdcContractConfig.address },
startBlock: 16369950,
-};
+ type: "logFilter",
+} as const;
-const logFilters: LogFilter[] = [
+const sources: Source[] = [
usdcLogFilter,
{
...usdcLogFilter,
@@ -54,7 +55,7 @@ test("handleNewHistoricalCheckpoint emits new checkpoint", async (context) => {
common,
eventStore,
networks,
- logFilters,
+ sources,
});
const emitSpy = vi.spyOn(service, "emit");
@@ -76,7 +77,7 @@ test("handleNewHistoricalCheckpoint does not emit new checkpoint if not best", a
const service = new EventAggregatorService({
common,
eventStore,
- logFilters,
+ sources,
networks,
});
const emitSpy = vi.spyOn(service, "emit");
@@ -106,7 +107,7 @@ test("handleHistoricalSyncComplete sets historicalSyncCompletedAt if final histo
const service = new EventAggregatorService({
common,
eventStore,
- logFilters,
+ sources,
networks,
});
const emitSpy = vi.spyOn(service, "emit");
@@ -134,7 +135,7 @@ test("handleNewRealtimeCheckpoint does not emit new checkpoint if historical syn
const service = new EventAggregatorService({
common,
eventStore,
- logFilters,
+ sources,
networks,
});
const emitSpy = vi.spyOn(service, "emit");
@@ -163,7 +164,7 @@ test("handleNewRealtimeCheckpoint emits new checkpoint if historical sync is com
const service = new EventAggregatorService({
common,
eventStore,
- logFilters,
+ sources,
networks,
});
const emitSpy = vi.spyOn(service, "emit");
@@ -201,7 +202,7 @@ test("handleNewFinalityCheckpoint emits newFinalityCheckpoint", async (context)
const service = new EventAggregatorService({
common,
eventStore,
- logFilters,
+ sources,
networks,
});
const emitSpy = vi.spyOn(service, "emit");
@@ -227,7 +228,7 @@ test("handleNewFinalityCheckpoint does not emit newFinalityCheckpoint if subsequ
const service = new EventAggregatorService({
common,
eventStore,
- logFilters,
+ sources,
networks,
});
const emitSpy = vi.spyOn(service, "emit");
@@ -257,7 +258,7 @@ test("handleNewFinalityCheckpoint emits newFinalityCheckpoint if subsequent even
const service = new EventAggregatorService({
common,
eventStore,
- logFilters,
+ sources,
networks,
});
const emitSpy = vi.spyOn(service, "emit");
diff --git a/packages/core/src/historical-sync/service.test.ts b/packages/core/src/historical-sync/service.test.ts
index c0e9189ec..edae2ab8c 100644
--- a/packages/core/src/historical-sync/service.test.ts
+++ b/packages/core/src/historical-sync/service.test.ts
@@ -11,9 +11,8 @@ import {
} from "@/_test/constants";
import { setupEventStore } from "@/_test/setup";
import { publicClient } from "@/_test/utils";
-import { Factory } from "@/config/factories";
-import type { LogFilter } from "@/config/logFilters";
import type { Network } from "@/config/networks";
+import type { Source } from "@/config/sources";
import { HistoricalSyncService } from "./service";
@@ -46,13 +45,15 @@ const usdcLogFilter = {
criteria: { address: usdcContractConfig.address },
startBlock: 16369995, // 5 blocks
maxBlockRange: 3,
-} satisfies LogFilter;
+ type: "logFilter",
+} satisfies Source;
const uniswapV3Factory = {
...uniswapV3PoolFactoryConfig,
network: network.name,
startBlock: 16369500, // 500 blocks
-} satisfies Factory;
+ type: "factory",
+} satisfies Source;
test("start() with log filter inserts log filter interval records", async (context) => {
const { common, eventStore } = context;
@@ -61,7 +62,7 @@ test("start() with log filter inserts log filter interval records", async (conte
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup(blockNumbers);
service.start();
@@ -84,7 +85,7 @@ test("start() with factory contract inserts log filter and factory log filter in
common,
eventStore,
network,
- factories: [uniswapV3Factory],
+ sources: [uniswapV3Factory],
});
await service.setup(blockNumbers);
service.start();
@@ -95,7 +96,7 @@ test("start() with factory contract inserts log filter and factory log filter in
chainId: network.chainId,
logFilter: {
address: uniswapV3Factory.criteria.address,
- topics: [uniswapV3Factory.criteria.eventSelector],
+ topics: [uniswapV3Factory.criteria.eventSelector, null, null, null],
},
}
);
@@ -118,7 +119,7 @@ test("start() with factory contract inserts child contract addresses", async (co
common,
eventStore,
network,
- factories: [uniswapV3Factory],
+ sources: [uniswapV3Factory],
});
await service.setup(blockNumbers);
service.start();
@@ -152,8 +153,7 @@ test("setup() with log filter and factory contract updates block metrics", async
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
- factories: [uniswapV3Factory],
+ sources: [usdcLogFilter, uniswapV3Factory],
});
await service.setup(blockNumbers);
@@ -194,8 +194,7 @@ test("start() with log filter and factory contract updates completed blocks metr
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
- factories: [uniswapV3Factory],
+ sources: [usdcLogFilter, uniswapV3Factory],
});
await service.setup(blockNumbers);
service.start();
@@ -226,7 +225,7 @@ test("start() with log filter and factory contract updates rpc request duration
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup(blockNumbers);
service.start();
@@ -264,7 +263,7 @@ test("start() adds log filter events to event store", async (context) => {
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup(blockNumbers);
service.start();
@@ -318,8 +317,7 @@ test("start() adds log filter and factory contract events to event store", async
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
- factories: [uniswapV3Factory],
+ sources: [usdcLogFilter, uniswapV3Factory],
});
await service.setup(blockNumbers);
service.start();
@@ -363,7 +361,7 @@ test("start() retries unexpected error in log filter task", async (context) => {
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup(blockNumbers);
service.start();
@@ -389,7 +387,7 @@ test("start() retries unexpected error in block task", async (context) => {
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup(blockNumbers);
service.start();
@@ -421,7 +419,7 @@ test("start() handles Alchemy 'Log response size exceeded' error", async (contex
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup(blockNumbers);
service.start();
@@ -451,7 +449,7 @@ test("start() handles Quicknode 'eth_getLogs and eth_newFilter are limited to a
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup(blockNumbers);
service.start();
@@ -473,7 +471,7 @@ test("start() emits sync completed event", async (context) => {
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
const emitSpy = vi.spyOn(service, "emit");
@@ -492,7 +490,7 @@ test("start() emits checkpoint and sync completed event if 100% cached", async (
let service = new HistoricalSyncService({
common,
eventStore,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
network,
});
@@ -504,7 +502,7 @@ test("start() emits checkpoint and sync completed event if 100% cached", async (
service = new HistoricalSyncService({
common,
eventStore,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
network,
});
@@ -531,7 +529,7 @@ test("start() emits historicalCheckpoint event", async (context) => {
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
const emitSpy = vi.spyOn(service, "emit");
diff --git a/packages/core/src/indexing/service.test.ts b/packages/core/src/indexing/service.test.ts
index c6d67452f..cf1c4ae5a 100644
--- a/packages/core/src/indexing/service.test.ts
+++ b/packages/core/src/indexing/service.test.ts
@@ -7,6 +7,7 @@ import { publicClient } from "@/_test/utils";
import type { IndexingFunctions } from "@/build/functions";
import { schemaHeader } from "@/build/schema";
import { LogEventMetadata } from "@/config/abi";
+import { Source } from "@/config/sources";
import { EventAggregatorService } from "@/event-aggregator/service";
import { buildSchema } from "@/schema/schema";
@@ -25,13 +26,14 @@ const network = {
maxRpcRequestConcurrency: 10,
};
-const logFilters = [
+const sources: Source[] = [
{
name: "USDC",
...usdcContractConfig,
network: network.name,
criteria: { address: usdcContractConfig.address },
startBlock: 16369950,
+ type: "logFilter",
},
];
@@ -123,8 +125,7 @@ test("processEvents() calls getEvents with sequential timestamp ranges", async (
eventStore,
userStore,
eventAggregatorService,
- // contracts,
- logFilters,
+ sources,
});
await service.reset({ schema, indexingFunctions });
@@ -162,8 +163,7 @@ test("processEvents() calls indexing functions with correct arguments", async (c
eventStore,
userStore,
eventAggregatorService,
- // contracts,
- logFilters,
+ sources,
});
await service.reset({ schema, indexingFunctions });
@@ -183,7 +183,7 @@ test("processEvents() calls indexing functions with correct arguments", async (c
name: "Transfer",
},
context: {
- contracts: { USDC: expect.anything() },
+ contracts: {},
entities: { TransferEvent: expect.anything() },
},
})
@@ -200,8 +200,7 @@ test("processEvents() model methods insert data into the user store", async (con
eventStore,
userStore,
eventAggregatorService,
- // contracts,
- logFilters,
+ sources,
});
await service.reset({ schema, indexingFunctions });
@@ -225,8 +224,7 @@ test("processEvents() updates event count metrics", async (context) => {
eventStore,
userStore,
eventAggregatorService,
- // contracts,
- logFilters,
+ sources,
});
await service.reset({ schema, indexingFunctions });
@@ -267,8 +265,7 @@ test("reset() reloads the user store", async (context) => {
eventStore,
userStore,
eventAggregatorService,
- // contracts,
- logFilters,
+ sources,
});
await service.reset({ schema, indexingFunctions });
@@ -303,8 +300,7 @@ test("handleReorg() updates ponder_handlers_latest_processed_timestamp metric",
eventStore,
userStore,
eventAggregatorService,
- // contracts,
- logFilters,
+ sources,
});
await service.reset({ schema, indexingFunctions });
@@ -335,8 +331,7 @@ test("handleReorg() reverts the user store", async (context) => {
eventStore,
userStore,
eventAggregatorService,
- // contracts,
- logFilters,
+ sources,
});
const userStoreRevertSpy = vi.spyOn(userStore, "revert");
@@ -361,8 +356,7 @@ test("handleReorg() does nothing if there is a user error", async (context) => {
eventStore,
userStore,
eventAggregatorService,
- // contracts,
- logFilters,
+ sources,
});
const userStoreRevertSpy = vi.spyOn(userStore, "revert");
@@ -391,8 +385,7 @@ test("handleReorg() processes the correct range of events after a reorg", async
eventStore,
userStore,
eventAggregatorService,
- // contracts,
- logFilters,
+ sources,
});
await service.reset({ schema, indexingFunctions });
@@ -431,8 +424,7 @@ test("handleReorg() updates ponder_handlers_latest_processed_timestamp metric",
eventStore,
userStore,
eventAggregatorService,
- // contracts,
- logFilters,
+ sources,
});
await service.reset({ schema, indexingFunctions });
diff --git a/packages/core/src/realtime-sync/service.test.ts b/packages/core/src/realtime-sync/service.test.ts
index c39d65969..1139cdd9a 100644
--- a/packages/core/src/realtime-sync/service.test.ts
+++ b/packages/core/src/realtime-sync/service.test.ts
@@ -10,9 +10,8 @@ import {
} from "@/_test/constants";
import { resetTestClient, setupEventStore } from "@/_test/setup";
import { publicClient, testClient, walletClient } from "@/_test/utils";
-import { Factory } from "@/config/factories";
-import type { LogFilter } from "@/config/logFilters";
import type { Network } from "@/config/networks";
+import type { Source } from "@/config/sources";
import { decodeToBigInt } from "@/utils/encoding";
import { range } from "@/utils/range";
@@ -43,7 +42,8 @@ const usdcLogFilter = {
criteria: { address: usdcContractConfig.address },
startBlock: 16369995, // 5 blocks
maxBlockRange: 3,
-} satisfies LogFilter;
+ type: "logFilter",
+} satisfies Source;
const sendUsdcTransferTransaction = async () => {
await walletClient.writeContract({
@@ -59,7 +59,8 @@ const uniswapV3Factory = {
...uniswapV3PoolFactoryConfig,
network: network.name,
startBlock: 16369500, // 500 blocks
-} satisfies Factory;
+ type: "factory",
+} satisfies Source;
const createAndInitializeUniswapV3Pool = async () => {
await walletClient.writeContract({
@@ -105,7 +106,7 @@ test("setup() returns block numbers", async (context) => {
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
const { latestBlockNumber, finalizedBlockNumber } = await service.setup();
@@ -123,7 +124,7 @@ test("start() adds blocks to the store from finalized to latest", async (context
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup();
@@ -150,7 +151,7 @@ test("start() adds all required transactions to the store", async (context) => {
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup();
@@ -180,7 +181,7 @@ test("start() adds all matched logs to the store", async (context) => {
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup();
@@ -203,7 +204,7 @@ test("start() handles new blocks", async (context) => {
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup();
@@ -250,7 +251,7 @@ test("start() handles error while fetching new latest block gracefully", async (
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup();
@@ -291,7 +292,7 @@ test("start() emits realtimeCheckpoint events", async (context) => {
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
const emitSpy = vi.spyOn(service, "emit");
@@ -332,7 +333,7 @@ test("start() inserts log filter interval records for finalized blocks", async (
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
const emitSpy = vi.spyOn(service, "emit");
@@ -370,7 +371,7 @@ test("start() deletes data from the store after 3 block shallow reorg", async (c
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
await service.setup();
@@ -440,7 +441,7 @@ test("start() emits shallowReorg event after 3 block shallow reorg", async (cont
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
const emitSpy = vi.spyOn(service, "emit");
@@ -482,7 +483,7 @@ test("emits deepReorg event after deep reorg", async (context) => {
common,
eventStore,
network,
- logFilters: [usdcLogFilter],
+ sources: [usdcLogFilter],
});
const emitSpy = vi.spyOn(service, "emit");
@@ -535,7 +536,7 @@ test("start() with factory contract inserts new child contracts records and chil
common,
eventStore,
network,
- factories: [uniswapV3Factory],
+ sources: [uniswapV3Factory],
});
await service.setup();
From 5885d0abb12396c401d40af0146770b47cee5f7f Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 12:33:07 -0400
Subject: [PATCH 09/44] change topcis back to original shape, fix bug with
address normalization, and update end to end tests
---
.../_test/art-gobblers/app/ArtGobblers.abi.ts | 947 +++++++++++++++++
.../_test/art-gobblers/app/ArtGobblers.json | 989 ------------------
.../_test/art-gobblers/app/ponder.config.ts | 7 +-
.../app/BaseRegistrarImplementation.abi.json | 497 ---------
.../app/BaseRegistrarImplementation.abi.ts | 479 +++++++++
.../core/src/_test/ens/app/ponder.config.ts | 7 +-
packages/core/src/_test/ens/app/tsconfig.json | 2 +-
packages/core/src/build/config.ts | 3 +-
packages/core/src/config/sources.ts | 25 +-
.../core/src/event-store/postgres/store.ts | 8 +-
packages/core/src/event-store/sqlite/store.ts | 8 +-
packages/core/src/event-store/store.test.ts | 27 +-
.../core/src/historical-sync/service.test.ts | 2 +-
packages/core/src/historical-sync/service.ts | 6 +-
packages/core/src/realtime-sync/bloom.ts | 4 +-
packages/core/src/realtime-sync/filter.ts | 6 +-
16 files changed, 1465 insertions(+), 1552 deletions(-)
create mode 100644 packages/core/src/_test/art-gobblers/app/ArtGobblers.abi.ts
delete mode 100644 packages/core/src/_test/art-gobblers/app/ArtGobblers.json
delete mode 100644 packages/core/src/_test/ens/app/BaseRegistrarImplementation.abi.json
create mode 100644 packages/core/src/_test/ens/app/BaseRegistrarImplementation.abi.ts
diff --git a/packages/core/src/_test/art-gobblers/app/ArtGobblers.abi.ts b/packages/core/src/_test/art-gobblers/app/ArtGobblers.abi.ts
new file mode 100644
index 000000000..e790b12dd
--- /dev/null
+++ b/packages/core/src/_test/art-gobblers/app/ArtGobblers.abi.ts
@@ -0,0 +1,947 @@
+export const ArtGobblersAbi = [
+ {
+ inputs: [
+ { internalType: "bytes32", name: "_merkleRoot", type: "bytes32" },
+ { internalType: "uint256", name: "_mintStart", type: "uint256" },
+ { internalType: "contract Goo", name: "_goo", type: "address" },
+ { internalType: "contract Pages", name: "_pages", type: "address" },
+ { internalType: "address", name: "_team", type: "address" },
+ { internalType: "address", name: "_community", type: "address" },
+ {
+ internalType: "contract RandProvider",
+ name: "_randProvider",
+ type: "address",
+ },
+ { internalType: "string", name: "_baseUri", type: "string" },
+ { internalType: "string", name: "_unrevealedUri", type: "string" },
+ {
+ internalType: "bytes32",
+ name: "_provenanceHash",
+ type: "bytes32",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "constructor",
+ },
+ { inputs: [], name: "AlreadyClaimed", type: "error" },
+ { inputs: [], name: "Cannibalism", type: "error" },
+ {
+ inputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ name: "CannotBurnLegendary",
+ type: "error",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "cost", type: "uint256" }],
+ name: "InsufficientGobblerAmount",
+ type: "error",
+ },
+ { inputs: [], name: "InvalidProof", type: "error" },
+ {
+ inputs: [
+ { internalType: "uint256", name: "gobblersLeft", type: "uint256" },
+ ],
+ name: "LegendaryAuctionNotStarted",
+ type: "error",
+ },
+ { inputs: [], name: "MintStartPending", type: "error" },
+ { inputs: [], name: "NoRemainingLegendaryGobblers", type: "error" },
+ {
+ inputs: [
+ {
+ internalType: "uint256",
+ name: "totalRemainingToBeRevealed",
+ type: "uint256",
+ },
+ ],
+ name: "NotEnoughRemainingToBeRevealed",
+ type: "error",
+ },
+ { inputs: [], name: "NotRandProvider", type: "error" },
+ {
+ inputs: [{ internalType: "address", name: "owner", type: "address" }],
+ name: "OwnerMismatch",
+ type: "error",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "currentPrice", type: "uint256" },
+ ],
+ name: "PriceExceededMax",
+ type: "error",
+ },
+ { inputs: [], name: "RequestTooEarly", type: "error" },
+ { inputs: [], name: "ReserveImbalance", type: "error" },
+ { inputs: [], name: "RevealsPending", type: "error" },
+ { inputs: [], name: "SeedPending", type: "error" },
+ {
+ inputs: [{ internalType: "address", name: "caller", type: "address" }],
+ name: "UnauthorizedCaller",
+ type: "error",
+ },
+ { inputs: [], name: "ZeroToBeRevealed", type: "error" },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "spender",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ ],
+ name: "Approval",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "operator",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bool",
+ name: "approved",
+ type: "bool",
+ },
+ ],
+ name: "ApprovalForAll",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "gobblerId",
+ type: "uint256",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "nft",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ ],
+ name: "ArtGobbled",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "gobblerId",
+ type: "uint256",
+ },
+ ],
+ name: "GobblerClaimed",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "gobblerId",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "price",
+ type: "uint256",
+ },
+ ],
+ name: "GobblerPurchased",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "numGobblers",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "lastRevealedId",
+ type: "uint256",
+ },
+ ],
+ name: "GobblersRevealed",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "newGooBalance",
+ type: "uint256",
+ },
+ ],
+ name: "GooBalanceUpdated",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "gobblerId",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256[]",
+ name: "burnedGobblerIds",
+ type: "uint256[]",
+ },
+ ],
+ name: "LegendaryGobblerMinted",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "newOwner",
+ type: "address",
+ },
+ ],
+ name: "OwnershipTransferred",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "contract RandProvider",
+ name: "newRandProvider",
+ type: "address",
+ },
+ ],
+ name: "RandProviderUpgraded",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "randomness",
+ type: "uint256",
+ },
+ ],
+ name: "RandomnessFulfilled",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "toBeRevealed",
+ type: "uint256",
+ },
+ ],
+ name: "RandomnessRequested",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "lastMintedGobblerId",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "numGobblersEach",
+ type: "uint256",
+ },
+ ],
+ name: "ReservedGobblersMinted",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "from",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "to",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ ],
+ name: "Transfer",
+ type: "event",
+ },
+ {
+ inputs: [],
+ name: "BASE_URI",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "FIRST_LEGENDARY_GOBBLER_ID",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "LEGENDARY_AUCTION_INTERVAL",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "LEGENDARY_GOBBLER_INITIAL_START_PRICE",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "LEGENDARY_SUPPLY",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "MAX_MINTABLE",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "MAX_SUPPLY",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "MINTLIST_SUPPLY",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "PROVENANCE_HASH",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "RESERVED_SUPPLY",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "UNREVEALED_URI",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "bytes32", name: "", type: "bytes32" },
+ { internalType: "uint256", name: "randomness", type: "uint256" },
+ ],
+ name: "acceptRandomSeed",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "gooAmount", type: "uint256" }],
+ name: "addGoo",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "spender", type: "address" },
+ { internalType: "uint256", name: "id", type: "uint256" },
+ ],
+ name: "approve",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "owner", type: "address" }],
+ name: "balanceOf",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "user", type: "address" },
+ { internalType: "uint256", name: "gooAmount", type: "uint256" },
+ ],
+ name: "burnGooForPages",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "bytes32[]", name: "proof", type: "bytes32[]" }],
+ name: "claimGobbler",
+ outputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "community",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "currentNonLegendaryId",
+ outputs: [{ internalType: "uint128", name: "", type: "uint128" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ name: "getApproved",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "", type: "uint256" },
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "uint256", name: "", type: "uint256" },
+ ],
+ name: "getCopiesOfArtGobbledByGobbler",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ name: "getGobblerData",
+ outputs: [
+ { internalType: "address", name: "owner", type: "address" },
+ { internalType: "uint64", name: "idx", type: "uint64" },
+ { internalType: "uint32", name: "emissionMultiple", type: "uint32" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ name: "getGobblerEmissionMultiple",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "int256", name: "sold", type: "int256" }],
+ name: "getTargetSaleTime",
+ outputs: [{ internalType: "int256", name: "", type: "int256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "", type: "address" }],
+ name: "getUserData",
+ outputs: [
+ { internalType: "uint32", name: "gobblersOwned", type: "uint32" },
+ {
+ internalType: "uint32",
+ name: "emissionMultiple",
+ type: "uint32",
+ },
+ { internalType: "uint128", name: "lastBalance", type: "uint128" },
+ { internalType: "uint64", name: "lastTimestamp", type: "uint64" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "user", type: "address" }],
+ name: "getUserEmissionMultiple",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "int256", name: "timeSinceStart", type: "int256" },
+ { internalType: "uint256", name: "sold", type: "uint256" },
+ ],
+ name: "getVRGDAPrice",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "gobblerId", type: "uint256" },
+ { internalType: "address", name: "nft", type: "address" },
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "bool", name: "isERC1155", type: "bool" },
+ ],
+ name: "gobble",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "gobblerPrice",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "gobblerRevealsData",
+ outputs: [
+ { internalType: "uint64", name: "randomSeed", type: "uint64" },
+ {
+ internalType: "uint64",
+ name: "nextRevealTimestamp",
+ type: "uint64",
+ },
+ { internalType: "uint64", name: "lastRevealedId", type: "uint64" },
+ { internalType: "uint56", name: "toBeRevealed", type: "uint56" },
+ { internalType: "bool", name: "waitingForSeed", type: "bool" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "goo",
+ outputs: [{ internalType: "contract Goo", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "user", type: "address" }],
+ name: "gooBalance",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "", type: "address" }],
+ name: "hasClaimedMintlistGobbler",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "address", name: "", type: "address" },
+ ],
+ name: "isApprovedForAll",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "legendaryGobblerAuctionData",
+ outputs: [
+ { internalType: "uint128", name: "startPrice", type: "uint128" },
+ { internalType: "uint128", name: "numSold", type: "uint128" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "legendaryGobblerPrice",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "merkleRoot",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "maxPrice", type: "uint256" },
+ { internalType: "bool", name: "useVirtualBalance", type: "bool" },
+ ],
+ name: "mintFromGoo",
+ outputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256[]", name: "gobblerIds", type: "uint256[]" },
+ ],
+ name: "mintLegendaryGobbler",
+ outputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "uint256",
+ name: "numGobblersEach",
+ type: "uint256",
+ },
+ ],
+ name: "mintReservedGobblers",
+ outputs: [
+ {
+ internalType: "uint256",
+ name: "lastMintedGobblerId",
+ type: "uint256",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "mintStart",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "name",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "numMintedForReserves",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "numMintedFromGoo",
+ outputs: [{ internalType: "uint128", name: "", type: "uint128" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "uint256[]", name: "", type: "uint256[]" },
+ { internalType: "uint256[]", name: "", type: "uint256[]" },
+ { internalType: "bytes", name: "", type: "bytes" },
+ ],
+ name: "onERC1155BatchReceived",
+ outputs: [{ internalType: "bytes4", name: "", type: "bytes4" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "uint256", name: "", type: "uint256" },
+ { internalType: "uint256", name: "", type: "uint256" },
+ { internalType: "bytes", name: "", type: "bytes" },
+ ],
+ name: "onERC1155Received",
+ outputs: [{ internalType: "bytes4", name: "", type: "bytes4" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "owner",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "id", type: "uint256" }],
+ name: "ownerOf",
+ outputs: [{ internalType: "address", name: "owner", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "pages",
+ outputs: [{ internalType: "contract Pages", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "randProvider",
+ outputs: [
+ { internalType: "contract RandProvider", name: "", type: "address" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "gooAmount", type: "uint256" }],
+ name: "removeGoo",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "requestRandomSeed",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "numGobblers", type: "uint256" }],
+ name: "revealGobblers",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "id", type: "uint256" },
+ ],
+ name: "safeTransferFrom",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ ],
+ name: "safeTransferFrom",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "operator", type: "address" },
+ { internalType: "bool", name: "approved", type: "bool" },
+ ],
+ name: "setApprovalForAll",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "bytes4", name: "interfaceId", type: "bytes4" }],
+ name: "supportsInterface",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "pure",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "symbol",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "targetPrice",
+ outputs: [{ internalType: "int256", name: "", type: "int256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "team",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ name: "tokenURI",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "id", type: "uint256" },
+ ],
+ name: "transferFrom",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "newOwner", type: "address" }],
+ name: "transferOwnership",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract RandProvider",
+ name: "newRandProvider",
+ type: "address",
+ },
+ ],
+ name: "upgradeRandProvider",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+] as const;
diff --git a/packages/core/src/_test/art-gobblers/app/ArtGobblers.json b/packages/core/src/_test/art-gobblers/app/ArtGobblers.json
deleted file mode 100644
index ce916cce2..000000000
--- a/packages/core/src/_test/art-gobblers/app/ArtGobblers.json
+++ /dev/null
@@ -1,989 +0,0 @@
-[
- {
- "inputs": [
- { "internalType": "bytes32", "name": "_merkleRoot", "type": "bytes32" },
- { "internalType": "uint256", "name": "_mintStart", "type": "uint256" },
- { "internalType": "contract Goo", "name": "_goo", "type": "address" },
- { "internalType": "contract Pages", "name": "_pages", "type": "address" },
- { "internalType": "address", "name": "_team", "type": "address" },
- { "internalType": "address", "name": "_community", "type": "address" },
- {
- "internalType": "contract RandProvider",
- "name": "_randProvider",
- "type": "address"
- },
- { "internalType": "string", "name": "_baseUri", "type": "string" },
- { "internalType": "string", "name": "_unrevealedUri", "type": "string" },
- {
- "internalType": "bytes32",
- "name": "_provenanceHash",
- "type": "bytes32"
- }
- ],
- "stateMutability": "nonpayable",
- "type": "constructor"
- },
- { "inputs": [], "name": "AlreadyClaimed", "type": "error" },
- { "inputs": [], "name": "Cannibalism", "type": "error" },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "name": "CannotBurnLegendary",
- "type": "error"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "cost", "type": "uint256" }
- ],
- "name": "InsufficientGobblerAmount",
- "type": "error"
- },
- { "inputs": [], "name": "InvalidProof", "type": "error" },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gobblersLeft", "type": "uint256" }
- ],
- "name": "LegendaryAuctionNotStarted",
- "type": "error"
- },
- { "inputs": [], "name": "MintStartPending", "type": "error" },
- { "inputs": [], "name": "NoRemainingLegendaryGobblers", "type": "error" },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "totalRemainingToBeRevealed",
- "type": "uint256"
- }
- ],
- "name": "NotEnoughRemainingToBeRevealed",
- "type": "error"
- },
- { "inputs": [], "name": "NotRandProvider", "type": "error" },
- {
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" }
- ],
- "name": "OwnerMismatch",
- "type": "error"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "currentPrice", "type": "uint256" }
- ],
- "name": "PriceExceededMax",
- "type": "error"
- },
- { "inputs": [], "name": "RequestTooEarly", "type": "error" },
- { "inputs": [], "name": "ReserveImbalance", "type": "error" },
- { "inputs": [], "name": "RevealsPending", "type": "error" },
- { "inputs": [], "name": "SeedPending", "type": "error" },
- {
- "inputs": [
- { "internalType": "address", "name": "caller", "type": "address" }
- ],
- "name": "UnauthorizedCaller",
- "type": "error"
- },
- { "inputs": [], "name": "ZeroToBeRevealed", "type": "error" },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "spender",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "operator",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "approved",
- "type": "bool"
- }
- ],
- "name": "ApprovalForAll",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "gobblerId",
- "type": "uint256"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "nft",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- }
- ],
- "name": "ArtGobbled",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "gobblerId",
- "type": "uint256"
- }
- ],
- "name": "GobblerClaimed",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "gobblerId",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "price",
- "type": "uint256"
- }
- ],
- "name": "GobblerPurchased",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "numGobblers",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "lastRevealedId",
- "type": "uint256"
- }
- ],
- "name": "GobblersRevealed",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "newGooBalance",
- "type": "uint256"
- }
- ],
- "name": "GooBalanceUpdated",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "gobblerId",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256[]",
- "name": "burnedGobblerIds",
- "type": "uint256[]"
- }
- ],
- "name": "LegendaryGobblerMinted",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "OwnershipTransferred",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "contract RandProvider",
- "name": "newRandProvider",
- "type": "address"
- }
- ],
- "name": "RandProviderUpgraded",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "randomness",
- "type": "uint256"
- }
- ],
- "name": "RandomnessFulfilled",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "toBeRevealed",
- "type": "uint256"
- }
- ],
- "name": "RandomnessRequested",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "lastMintedGobblerId",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "numGobblersEach",
- "type": "uint256"
- }
- ],
- "name": "ReservedGobblersMinted",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "inputs": [],
- "name": "BASE_URI",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "FIRST_LEGENDARY_GOBBLER_ID",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "LEGENDARY_AUCTION_INTERVAL",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "LEGENDARY_GOBBLER_INITIAL_START_PRICE",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "LEGENDARY_SUPPLY",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "MAX_MINTABLE",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "MAX_SUPPLY",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "MINTLIST_SUPPLY",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "PROVENANCE_HASH",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "RESERVED_SUPPLY",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "UNREVEALED_URI",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes32", "name": "", "type": "bytes32" },
- { "internalType": "uint256", "name": "randomness", "type": "uint256" }
- ],
- "name": "acceptRandomSeed",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gooAmount", "type": "uint256" }
- ],
- "name": "addGoo",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "spender", "type": "address" },
- { "internalType": "uint256", "name": "id", "type": "uint256" }
- ],
- "name": "approve",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" }
- ],
- "name": "balanceOf",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "user", "type": "address" },
- { "internalType": "uint256", "name": "gooAmount", "type": "uint256" }
- ],
- "name": "burnGooForPages",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes32[]", "name": "proof", "type": "bytes32[]" }
- ],
- "name": "claimGobbler",
- "outputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "community",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "currentNonLegendaryId",
- "outputs": [{ "internalType": "uint128", "name": "", "type": "uint128" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "name": "getApproved",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "", "type": "uint256" },
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "uint256", "name": "", "type": "uint256" }
- ],
- "name": "getCopiesOfArtGobbledByGobbler",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "name": "getGobblerData",
- "outputs": [
- { "internalType": "address", "name": "owner", "type": "address" },
- { "internalType": "uint64", "name": "idx", "type": "uint64" },
- { "internalType": "uint32", "name": "emissionMultiple", "type": "uint32" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "name": "getGobblerEmissionMultiple",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "int256", "name": "sold", "type": "int256" }],
- "name": "getTargetSaleTime",
- "outputs": [{ "internalType": "int256", "name": "", "type": "int256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "name": "getUserData",
- "outputs": [
- { "internalType": "uint32", "name": "gobblersOwned", "type": "uint32" },
- {
- "internalType": "uint32",
- "name": "emissionMultiple",
- "type": "uint32"
- },
- { "internalType": "uint128", "name": "lastBalance", "type": "uint128" },
- { "internalType": "uint64", "name": "lastTimestamp", "type": "uint64" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "user", "type": "address" }
- ],
- "name": "getUserEmissionMultiple",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "int256", "name": "timeSinceStart", "type": "int256" },
- { "internalType": "uint256", "name": "sold", "type": "uint256" }
- ],
- "name": "getVRGDAPrice",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" },
- { "internalType": "address", "name": "nft", "type": "address" },
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "bool", "name": "isERC1155", "type": "bool" }
- ],
- "name": "gobble",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "gobblerPrice",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "gobblerRevealsData",
- "outputs": [
- { "internalType": "uint64", "name": "randomSeed", "type": "uint64" },
- {
- "internalType": "uint64",
- "name": "nextRevealTimestamp",
- "type": "uint64"
- },
- { "internalType": "uint64", "name": "lastRevealedId", "type": "uint64" },
- { "internalType": "uint56", "name": "toBeRevealed", "type": "uint56" },
- { "internalType": "bool", "name": "waitingForSeed", "type": "bool" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "goo",
- "outputs": [
- { "internalType": "contract Goo", "name": "", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "user", "type": "address" }
- ],
- "name": "gooBalance",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "name": "hasClaimedMintlistGobbler",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "address", "name": "", "type": "address" }
- ],
- "name": "isApprovedForAll",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "legendaryGobblerAuctionData",
- "outputs": [
- { "internalType": "uint128", "name": "startPrice", "type": "uint128" },
- { "internalType": "uint128", "name": "numSold", "type": "uint128" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "legendaryGobblerPrice",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "merkleRoot",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "maxPrice", "type": "uint256" },
- { "internalType": "bool", "name": "useVirtualBalance", "type": "bool" }
- ],
- "name": "mintFromGoo",
- "outputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256[]", "name": "gobblerIds", "type": "uint256[]" }
- ],
- "name": "mintLegendaryGobbler",
- "outputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "numGobblersEach",
- "type": "uint256"
- }
- ],
- "name": "mintReservedGobblers",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "lastMintedGobblerId",
- "type": "uint256"
- }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "mintStart",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "name",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "numMintedForReserves",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "numMintedFromGoo",
- "outputs": [{ "internalType": "uint128", "name": "", "type": "uint128" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "uint256[]", "name": "", "type": "uint256[]" },
- { "internalType": "uint256[]", "name": "", "type": "uint256[]" },
- { "internalType": "bytes", "name": "", "type": "bytes" }
- ],
- "name": "onERC1155BatchReceived",
- "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "uint256", "name": "", "type": "uint256" },
- { "internalType": "uint256", "name": "", "type": "uint256" },
- { "internalType": "bytes", "name": "", "type": "bytes" }
- ],
- "name": "onERC1155Received",
- "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "owner",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "id", "type": "uint256" }],
- "name": "ownerOf",
- "outputs": [
- { "internalType": "address", "name": "owner", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "pages",
- "outputs": [
- { "internalType": "contract Pages", "name": "", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "randProvider",
- "outputs": [
- { "internalType": "contract RandProvider", "name": "", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gooAmount", "type": "uint256" }
- ],
- "name": "removeGoo",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "requestRandomSeed",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "numGobblers", "type": "uint256" }
- ],
- "name": "revealGobblers",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "id", "type": "uint256" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "operator", "type": "address" },
- { "internalType": "bool", "name": "approved", "type": "bool" }
- ],
- "name": "setApprovalForAll",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }
- ],
- "name": "supportsInterface",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "pure",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "symbol",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "targetPrice",
- "outputs": [{ "internalType": "int256", "name": "", "type": "int256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "team",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "name": "tokenURI",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "id", "type": "uint256" }
- ],
- "name": "transferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "newOwner", "type": "address" }
- ],
- "name": "transferOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract RandProvider",
- "name": "newRandProvider",
- "type": "address"
- }
- ],
- "name": "upgradeRandProvider",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- }
-]
diff --git a/packages/core/src/_test/art-gobblers/app/ponder.config.ts b/packages/core/src/_test/art-gobblers/app/ponder.config.ts
index b4ecd5d1f..7540d1e6a 100644
--- a/packages/core/src/_test/art-gobblers/app/ponder.config.ts
+++ b/packages/core/src/_test/art-gobblers/app/ponder.config.ts
@@ -1,15 +1,16 @@
import { http } from "viem";
-import ArtGobblersAbi from "./ArtGobblers.json";
+import type { Config } from "../../../../dist";
+import { ArtGobblersAbi } from "./ArtGobblers.abi";
-export const config = {
+export const config: Config = {
networks: [
{ name: "mainnet", chainId: 1, transport: http("http://127.0.0.1:8545") },
],
contracts: [
{
name: "ArtGobblers",
- network: "mainnet",
+ network: [{ name: "mainnet" }],
abi: ArtGobblersAbi,
address: "0x60bb1e2aa1c9acafb4d34f71585d7e959f387769",
startBlock: 15870400,
diff --git a/packages/core/src/_test/ens/app/BaseRegistrarImplementation.abi.json b/packages/core/src/_test/ens/app/BaseRegistrarImplementation.abi.json
deleted file mode 100644
index 93d79df9c..000000000
--- a/packages/core/src/_test/ens/app/BaseRegistrarImplementation.abi.json
+++ /dev/null
@@ -1,497 +0,0 @@
-[
- {
- "inputs": [
- { "internalType": "contract ENS", "name": "_ens", "type": "address" },
- { "internalType": "bytes32", "name": "_baseNode", "type": "bytes32" }
- ],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "constructor"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "approved",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "operator",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "approved",
- "type": "bool"
- }
- ],
- "name": "ApprovalForAll",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "controller",
- "type": "address"
- }
- ],
- "name": "ControllerAdded",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "controller",
- "type": "address"
- }
- ],
- "name": "ControllerRemoved",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "expires",
- "type": "uint256"
- }
- ],
- "name": "NameMigrated",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "expires",
- "type": "uint256"
- }
- ],
- "name": "NameRegistered",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "expires",
- "type": "uint256"
- }
- ],
- "name": "NameRenewed",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "previousOwner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "OwnershipTransferred",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "GRACE_PERIOD",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "address", "name": "controller", "type": "address" }
- ],
- "name": "addController",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "approve",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [{ "internalType": "uint256", "name": "id", "type": "uint256" }],
- "name": "available",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" }
- ],
- "name": "balanceOf",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "baseNode",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "name": "controllers",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "ens",
- "outputs": [
- { "internalType": "contract ENS", "name": "", "type": "address" }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "getApproved",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" },
- { "internalType": "address", "name": "operator", "type": "address" }
- ],
- "name": "isApprovedForAll",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "isOwner",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [{ "internalType": "uint256", "name": "id", "type": "uint256" }],
- "name": "nameExpires",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "owner",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "ownerOf",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "owner", "type": "address" }
- ],
- "name": "reclaim",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "owner", "type": "address" },
- { "internalType": "uint256", "name": "duration", "type": "uint256" }
- ],
- "name": "register",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "owner", "type": "address" },
- { "internalType": "uint256", "name": "duration", "type": "uint256" }
- ],
- "name": "registerOnly",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "address", "name": "controller", "type": "address" }
- ],
- "name": "removeController",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "uint256", "name": "duration", "type": "uint256" }
- ],
- "name": "renew",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [],
- "name": "renounceOwnership",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
- { "internalType": "bytes", "name": "_data", "type": "bytes" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "bool", "name": "approved", "type": "bool" }
- ],
- "name": "setApprovalForAll",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "address", "name": "resolver", "type": "address" }
- ],
- "name": "setResolver",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
- ],
- "name": "supportsInterface",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "transferFrom",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- { "internalType": "address", "name": "newOwner", "type": "address" }
- ],
- "name": "transferOwnership",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- }
-]
diff --git a/packages/core/src/_test/ens/app/BaseRegistrarImplementation.abi.ts b/packages/core/src/_test/ens/app/BaseRegistrarImplementation.abi.ts
new file mode 100644
index 000000000..2d9718a2d
--- /dev/null
+++ b/packages/core/src/_test/ens/app/BaseRegistrarImplementation.abi.ts
@@ -0,0 +1,479 @@
+export const BaseRegistrarImplementationAbi = [
+ {
+ inputs: [
+ { internalType: "contract ENS", name: "_ens", type: "address" },
+ { internalType: "bytes32", name: "_baseNode", type: "bytes32" },
+ ],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "constructor",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "approved",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "tokenId",
+ type: "uint256",
+ },
+ ],
+ name: "Approval",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "operator",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bool",
+ name: "approved",
+ type: "bool",
+ },
+ ],
+ name: "ApprovalForAll",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "controller",
+ type: "address",
+ },
+ ],
+ name: "ControllerAdded",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "controller",
+ type: "address",
+ },
+ ],
+ name: "ControllerRemoved",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "expires",
+ type: "uint256",
+ },
+ ],
+ name: "NameMigrated",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "expires",
+ type: "uint256",
+ },
+ ],
+ name: "NameRegistered",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "expires",
+ type: "uint256",
+ },
+ ],
+ name: "NameRenewed",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "previousOwner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "newOwner",
+ type: "address",
+ },
+ ],
+ name: "OwnershipTransferred",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "from",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "to",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "tokenId",
+ type: "uint256",
+ },
+ ],
+ name: "Transfer",
+ type: "event",
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: "GRACE_PERIOD",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [{ internalType: "address", name: "controller", type: "address" }],
+ name: "addController",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "tokenId", type: "uint256" },
+ ],
+ name: "approve",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [{ internalType: "uint256", name: "id", type: "uint256" }],
+ name: "available",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [{ internalType: "address", name: "owner", type: "address" }],
+ name: "balanceOf",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: "baseNode",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [{ internalType: "address", name: "", type: "address" }],
+ name: "controllers",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: "ens",
+ outputs: [{ internalType: "contract ENS", name: "", type: "address" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }],
+ name: "getApproved",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [
+ { internalType: "address", name: "owner", type: "address" },
+ { internalType: "address", name: "operator", type: "address" },
+ ],
+ name: "isApprovedForAll",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: "isOwner",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [{ internalType: "uint256", name: "id", type: "uint256" }],
+ name: "nameExpires",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: "owner",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }],
+ name: "ownerOf",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "owner", type: "address" },
+ ],
+ name: "reclaim",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "owner", type: "address" },
+ { internalType: "uint256", name: "duration", type: "uint256" },
+ ],
+ name: "register",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "owner", type: "address" },
+ { internalType: "uint256", name: "duration", type: "uint256" },
+ ],
+ name: "registerOnly",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [{ internalType: "address", name: "controller", type: "address" }],
+ name: "removeController",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "uint256", name: "duration", type: "uint256" },
+ ],
+ name: "renew",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [],
+ name: "renounceOwnership",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "tokenId", type: "uint256" },
+ ],
+ name: "safeTransferFrom",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "tokenId", type: "uint256" },
+ { internalType: "bytes", name: "_data", type: "bytes" },
+ ],
+ name: "safeTransferFrom",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "bool", name: "approved", type: "bool" },
+ ],
+ name: "setApprovalForAll",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [{ internalType: "address", name: "resolver", type: "address" }],
+ name: "setResolver",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: true,
+ inputs: [{ internalType: "bytes4", name: "interfaceID", type: "bytes4" }],
+ name: "supportsInterface",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ payable: false,
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "tokenId", type: "uint256" },
+ ],
+ name: "transferFrom",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ constant: false,
+ inputs: [{ internalType: "address", name: "newOwner", type: "address" }],
+ name: "transferOwnership",
+ outputs: [],
+ payable: false,
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+] as const;
diff --git a/packages/core/src/_test/ens/app/ponder.config.ts b/packages/core/src/_test/ens/app/ponder.config.ts
index 7c689b2c7..0b733399f 100644
--- a/packages/core/src/_test/ens/app/ponder.config.ts
+++ b/packages/core/src/_test/ens/app/ponder.config.ts
@@ -1,15 +1,16 @@
import { http } from "viem";
-import BaseRegistrarImplementationAbi from "./BaseRegistrarImplementation.abi.json";
+import type { Config } from "../../../../dist";
+import { BaseRegistrarImplementationAbi } from "./BaseRegistrarImplementation.abi";
-export const config = {
+export const config: Config = {
networks: [
{ name: "mainnet", chainId: 1, transport: http("http://127.0.0.1:8545") },
],
contracts: [
{
name: "BaseRegistrarImplementation",
- network: "mainnet",
+ network: [{ name: "mainnet" }],
abi: BaseRegistrarImplementationAbi,
address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
startBlock: 16370000,
diff --git a/packages/core/src/_test/ens/app/tsconfig.json b/packages/core/src/_test/ens/app/tsconfig.json
index b9a2419b9..dce160e47 100644
--- a/packages/core/src/_test/ens/app/tsconfig.json
+++ b/packages/core/src/_test/ens/app/tsconfig.json
@@ -6,6 +6,6 @@
"@/generated": ["./generated/index.ts"]
}
},
- "include": ["src"],
+ "include": ["src", "BaseRegistrarImplementation.abi.ts"],
"exclude": []
}
diff --git a/packages/core/src/build/config.ts b/packages/core/src/build/config.ts
index ab3500953..601ee0d8d 100644
--- a/packages/core/src/build/config.ts
+++ b/packages/core/src/build/config.ts
@@ -22,7 +22,8 @@ export const buildConfig = async ({ configFile }: { configFile: string }) => {
outfile: buildFile,
platform: "node",
format: "cjs",
- bundle: false,
+ // Note: Flipped to true in order to be able to import external files into ponder.config.ts
+ bundle: true,
logLevel: "silent",
});
diff --git a/packages/core/src/config/sources.ts b/packages/core/src/config/sources.ts
index 8a57612d1..50b74b519 100644
--- a/packages/core/src/config/sources.ts
+++ b/packages/core/src/config/sources.ts
@@ -1,5 +1,7 @@
import { Abi, Address, encodeEventTopics, Hex } from "viem";
+import { toLowerCase } from "@/utils/lowercase";
+
import { AbiEvents, getEvents } from "./abi";
import { buildFactoryCriteria } from "./factories";
import { Options } from "./options";
@@ -10,12 +12,7 @@ import { ResolvedConfig } from "./types";
*
* Technically, only the first element could be an array
*/
-export type Topics = [
- Hex | Hex[] | null,
- Hex | Hex[] | null,
- Hex | Hex[] | null,
- Hex | Hex[] | null
-];
+export type Topics = (Hex | Hex[] | null)[];
export type LogFilterCriteria = {
address?: Address | Address[];
@@ -117,7 +114,9 @@ export const buildSources = ({
...sharedSource,
type: "logFilter",
criteria: {
- address: contract.address,
+ address: contract.address
+ ? toLowerCase(contract.address)
+ : undefined,
topics,
},
} as const satisfies LogFilter;
@@ -142,23 +141,13 @@ const buildTopics = (
})
)
.flat(),
- null,
- null,
- null,
];
} else {
// Single event with args
- const singleTopics = encodeEventTopics({
+ return encodeEventTopics({
abi: [events.signature],
eventName: events.signature.name,
args: events.args,
});
-
- return [
- singleTopics[0] ?? null,
- singleTopics[1] ?? null,
- singleTopics[2] ?? null,
- singleTopics[3] ?? null,
- ];
}
};
diff --git a/packages/core/src/event-store/postgres/store.ts b/packages/core/src/event-store/postgres/store.ts
index bddc858c1..8a85b0156 100644
--- a/packages/core/src/event-store/postgres/store.ts
+++ b/packages/core/src/event-store/postgres/store.ts
@@ -10,11 +10,7 @@ import {
import type { Pool } from "pg";
import type { Address, Hex, RpcBlock, RpcLog, RpcTransaction } from "viem";
-import type {
- FactoryCriteria,
- LogFilterCriteria,
- Topics,
-} from "@/config/sources";
+import type { FactoryCriteria, LogFilterCriteria } from "@/config/sources";
import type { Block } from "@/types/block";
import type { Log } from "@/types/log";
import type { Transaction } from "@/types/transaction";
@@ -544,7 +540,7 @@ export class PostgresEventStore implements EventStore {
...logFilters,
...factories.map((f) => ({
address: f.address,
- topics: [f.eventSelector, null, null, null] as Topics,
+ topics: [f.eventSelector],
})),
],
interval,
diff --git a/packages/core/src/event-store/sqlite/store.ts b/packages/core/src/event-store/sqlite/store.ts
index 881ef2151..e07e6aef3 100644
--- a/packages/core/src/event-store/sqlite/store.ts
+++ b/packages/core/src/event-store/sqlite/store.ts
@@ -9,11 +9,7 @@ import {
} from "kysely";
import type { Address, Hex, RpcBlock, RpcLog, RpcTransaction } from "viem";
-import type {
- FactoryCriteria,
- LogFilterCriteria,
- Topics,
-} from "@/config/sources";
+import type { FactoryCriteria, LogFilterCriteria } from "@/config/sources";
import type { Block } from "@/types/block";
import type { Log } from "@/types/log";
import type { Transaction } from "@/types/transaction";
@@ -512,7 +508,7 @@ export class SqliteEventStore implements EventStore {
...logFilters,
...factories.map((f) => ({
address: f.address,
- topics: [f.eventSelector, null, null, null] as Topics,
+ topics: [f.eventSelector],
})),
],
interval,
diff --git a/packages/core/src/event-store/store.test.ts b/packages/core/src/event-store/store.test.ts
index cb1766262..0d6aa518a 100644
--- a/packages/core/src/event-store/store.test.ts
+++ b/packages/core/src/event-store/store.test.ts
@@ -245,7 +245,7 @@ test("getLogFilterRanges handles complex log filter inclusivity rules", async (c
await eventStore.insertLogFilterInterval({
chainId: 1,
- logFilter: { topics: [null, ["0xc", "0xd"], null, null] },
+ logFilter: { topics: [null, ["0xc", "0xd"]] },
block: blockOne,
transactions: [],
logs: [],
@@ -262,7 +262,7 @@ test("getLogFilterRanges handles complex log filter inclusivity rules", async (c
// Narrower criteria includes both broad and specific intervals.
logFilterIntervals = await eventStore.getLogFilterIntervals({
chainId: 1,
- logFilter: { topics: [null, "0xc", null, null] },
+ logFilter: { topics: [null, "0xc"] },
});
expect(logFilterIntervals).toMatchObject([
[0, 100],
@@ -670,9 +670,6 @@ test("getFactoryLogFilterIntervals handles topic filtering rules", async (contex
...factoryCriteria,
topics: [
"0x0000000000000000000000000000000000000000000factoryeventsignature",
- null,
- null,
- null,
],
} as FactoryCriteria,
});
@@ -742,7 +739,7 @@ test("insertRealtimeInterval inserts log filter intervals", async (context) => {
chainId: 1,
logFilter: {
address: factoryCriteriaOne.address,
- topics: [factoryCriteriaOne.eventSelector, null, null, null],
+ topics: [factoryCriteriaOne.eventSelector],
},
})
).toMatchObject([[500, 550]]);
@@ -751,7 +748,7 @@ test("insertRealtimeInterval inserts log filter intervals", async (context) => {
chainId: 1,
logFilter: {
address: factoryCriteriaOne.address,
- topics: [factoryCriteriaOne.eventSelector, null, null, null],
+ topics: [factoryCriteriaOne.eventSelector],
},
})
).toMatchObject([[500, 550]]);
@@ -1266,12 +1263,7 @@ test("getLogEvents filters on log filter with single topic", async (context) =>
name: "singleTopic",
chainId: 1,
criteria: {
- topics: [
- blockOneLogs[0].topics[0] as `0x${string}`,
- null,
- null,
- null,
- ],
+ topics: [blockOneLogs[0].topics[0] as `0x${string}`],
},
},
],
@@ -1322,8 +1314,6 @@ test("getLogEvents filters on log filter with multiple topics", async (context)
topics: [
blockOneLogs[0].topics[0] as `0x${string}`,
blockOneLogs[0].topics[1] as `0x${string}`,
- null,
- null,
],
},
},
@@ -1466,12 +1456,7 @@ test("getLogEvents filters on multiple filters", async (context) => {
name: "singleTopic", // This should match blockOneLogs[0] AND blockTwoLogs[0]
chainId: 1,
criteria: {
- topics: [
- blockOneLogs[0].topics[0] as `0x${string}`,
- null,
- null,
- null,
- ],
+ topics: [blockOneLogs[0].topics[0] as `0x${string}`],
},
},
],
diff --git a/packages/core/src/historical-sync/service.test.ts b/packages/core/src/historical-sync/service.test.ts
index edae2ab8c..2971b650f 100644
--- a/packages/core/src/historical-sync/service.test.ts
+++ b/packages/core/src/historical-sync/service.test.ts
@@ -96,7 +96,7 @@ test("start() with factory contract inserts log filter and factory log filter in
chainId: network.chainId,
logFilter: {
address: uniswapV3Factory.criteria.address,
- topics: [uniswapV3Factory.criteria.eventSelector, null, null, null],
+ topics: [uniswapV3Factory.criteria.eventSelector],
},
}
);
diff --git a/packages/core/src/historical-sync/service.ts b/packages/core/src/historical-sync/service.ts
index acecd6629..21c146b60 100644
--- a/packages/core/src/historical-sync/service.ts
+++ b/packages/core/src/historical-sync/service.ts
@@ -277,7 +277,7 @@ export class HistoricalSyncService extends Emittery {
chainId: source.chainId,
logFilter: {
address: source.criteria.address,
- topics: [source.criteria.eventSelector, null, null, null],
+ topics: [source.criteria.eventSelector],
},
});
const factoryChildAddressProgressTracker = new ProgressTracker({
@@ -627,7 +627,7 @@ export class HistoricalSyncService extends Emittery {
const logs = await this._eth_getLogs({
address: factory.criteria.address,
- topics: [factory.criteria.eventSelector, null, null, null],
+ topics: [factory.criteria.eventSelector],
fromBlock: toHex(fromBlock),
toBlock: toHex(toBlock),
});
@@ -649,7 +649,7 @@ export class HistoricalSyncService extends Emittery {
chainId: factory.chainId,
logFilter: {
address: factory.criteria.address,
- topics: [factory.criteria.eventSelector, null, null, null],
+ topics: [factory.criteria.eventSelector],
},
block,
transactions: block.transactions.filter((tx) =>
diff --git a/packages/core/src/realtime-sync/bloom.ts b/packages/core/src/realtime-sync/bloom.ts
index dad97aef7..544c49909 100644
--- a/packages/core/src/realtime-sync/bloom.ts
+++ b/packages/core/src/realtime-sync/bloom.ts
@@ -4,6 +4,8 @@ import {
} from "ethereum-bloom-filters";
import type { Address, Hex } from "viem";
+import { Topics } from "@/config/sources";
+
export function isMatchedLogInBloomFilter({
bloom,
logFilters,
@@ -11,7 +13,7 @@ export function isMatchedLogInBloomFilter({
bloom: Hex;
logFilters: {
address?: Address | Address[];
- topics?: (Hex | Hex[] | null)[];
+ topics?: Topics;
}[];
}) {
const allAddresses: Address[] = [];
diff --git a/packages/core/src/realtime-sync/filter.ts b/packages/core/src/realtime-sync/filter.ts
index d80722a58..01171b03e 100644
--- a/packages/core/src/realtime-sync/filter.ts
+++ b/packages/core/src/realtime-sync/filter.ts
@@ -1,5 +1,7 @@
import type { Address, Hex, RpcLog } from "viem";
+import { Topics } from "@/config/sources";
+
export function filterLogs({
logs,
logFilters,
@@ -7,7 +9,7 @@ export function filterLogs({
logs: RpcLog[];
logFilters: {
address?: Address | Address[];
- topics?: (Hex | Hex[] | null)[];
+ topics?: Topics;
}[];
}) {
return logs.filter((log) => {
@@ -28,7 +30,7 @@ export function isLogMatchedByFilter({
topics: Hex[];
};
address?: Address | Address[];
- topics?: (Hex | Hex[] | null)[];
+ topics?: Topics;
}) {
if (address !== undefined && address.length > 0) {
if (Array.isArray(address)) {
From 7a9a2529a30bef2b939be9850d3e371502727f8b Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 12:36:39 -0400
Subject: [PATCH 10/44] cleanup
---
packages/core/src/config/abi.test.ts | 280 --------------------
packages/core/src/config/abi.ts | 76 ------
packages/core/src/config/contracts.ts | 53 ----
packages/core/src/indexing/contract.test.ts | 136 ----------
4 files changed, 545 deletions(-)
delete mode 100644 packages/core/src/config/abi.test.ts
delete mode 100644 packages/core/src/config/contracts.ts
delete mode 100644 packages/core/src/indexing/contract.test.ts
diff --git a/packages/core/src/config/abi.test.ts b/packages/core/src/config/abi.test.ts
deleted file mode 100644
index b4c4ad645..000000000
--- a/packages/core/src/config/abi.test.ts
+++ /dev/null
@@ -1,280 +0,0 @@
-// import { randomUUID } from "node:crypto";
-// import { mkdirSync, rmSync, writeFileSync } from "node:fs";
-// import { tmpdir } from "node:os";
-// import path from "node:path";
-// import { beforeEach, expect, test } from "vitest";
-
-// import { buildAbi, getEvents } from "./abi";
-
-// const abiSimple = [
-// {
-// inputs: [],
-// stateMutability: "nonpayable",
-// type: "constructor",
-// },
-// {
-// inputs: [
-// {
-// indexed: true,
-// type: "address",
-// },
-// {
-// indexed: true,
-// type: "address",
-// },
-// {
-// indexed: false,
-// type: "uint256",
-// },
-// ],
-// name: "Transfer",
-// type: "event",
-// },
-// {
-// inputs: [
-// {
-// indexed: true,
-// type: "address",
-// },
-// {
-// indexed: true,
-// type: "address",
-// },
-// {
-// indexed: false,
-// type: "uint256",
-// },
-// ],
-// name: "Approve",
-// type: "event",
-// },
-// ] as const;
-
-// const abiWithSameEvent = [
-// {
-// inputs: [],
-// stateMutability: "nonpayable",
-// type: "constructor",
-// },
-// {
-// inputs: [
-// {
-// indexed: true,
-// type: "address",
-// },
-// {
-// indexed: true,
-// type: "address",
-// },
-// {
-// indexed: false,
-// type: "uint256",
-// },
-// ],
-// name: "Approve",
-// type: "event",
-// },
-// ] as const;
-
-// let tmpDir: string;
-// let configFilePath: string;
-
-// beforeEach(() => {
-// tmpDir = path.join(tmpdir(), randomUUID());
-// configFilePath = path.join(tmpDir, "ponder.config.ts");
-
-// mkdirSync(tmpDir, { recursive: true });
-
-// return () => rmSync(tmpDir, { recursive: true, force: true });
-// });
-
-// test("buildAbi handles a single ABI passed as a file path", () => {
-// const abiSimplePath = path.join(tmpDir, "abiSimple.json");
-// writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
-
-// const { abi, filePaths } = buildAbi({
-// abiConfig: "./abiSimple.json",
-// configFilePath,
-// });
-
-// expect(abi).toMatchObject(abiSimple);
-// expect(filePaths).toMatchObject([abiSimplePath]);
-// });
-
-// test("buildAbi handles a single ABI passed as an object", () => {
-// const { abi, filePaths } = buildAbi({
-// abiConfig: abiSimple,
-// configFilePath,
-// });
-
-// expect(abi).toMatchObject(abiSimple);
-// expect(filePaths).toMatchObject([]);
-// });
-
-// test("buildAbi handles an array with a single ABI passed as a file path", () => {
-// const abiSimplePath = path.join(tmpDir, "abiSimple.json");
-// writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
-
-// const { abi, filePaths } = buildAbi({
-// abiConfig: ["./abiSimple.json"],
-// configFilePath,
-// });
-
-// expect(abi).toMatchObject(abiSimple.filter((x) => x.type !== "constructor"));
-// expect(filePaths).toMatchObject([abiSimplePath]);
-// });
-
-// test("buildAbi handles an array of ABIs passed as file paths", () => {
-// const abiSimplePath = path.join(tmpDir, "abiSimple.json");
-// writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
-// const abiWithSameEventPath = path.join(tmpDir, "abiWithSameEvent.json");
-// writeFileSync(abiWithSameEventPath, JSON.stringify(abiWithSameEvent));
-
-// const { abi, filePaths } = buildAbi({
-// abiConfig: ["./abiSimple.json", "./abiWithSameEvent.json"],
-// configFilePath,
-// });
-
-// expect(abi.filter((x) => x.type === "event")).toMatchObject(
-// abiSimple.filter((x) => x.type === "event")
-// );
-// expect(filePaths).toMatchObject([abiSimplePath, abiWithSameEventPath]);
-// });
-
-// test("buildAbi handles an array of ABIs with both file paths and objects", () => {
-// const abiSimplePath = path.join(tmpDir, "abiSimple.json");
-// writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
-
-// const { abi, filePaths } = buildAbi({
-// abiConfig: ["./abiSimple.json", abiWithSameEvent],
-// configFilePath,
-// });
-
-// expect(abi.filter((x) => x.type === "event")).toMatchObject(
-// abiSimple.filter((x) => x.type === "event")
-// );
-// expect(filePaths).toMatchObject([abiSimplePath]);
-// });
-
-// test("buildAbi handles an array of ABIs and removes duplicate abi items", () => {
-// const abiSimplePath = path.join(tmpDir, "abiSimple.json");
-// writeFileSync(abiSimplePath, JSON.stringify(abiSimple));
-
-// const { abi, filePaths } = buildAbi({
-// abiConfig: ["./abiSimple.json", abiWithSameEvent],
-// configFilePath,
-// });
-
-// expect(abi.filter((x) => x.type === "event")).toMatchObject(
-// abiSimple.filter((x) => x.type === "event")
-// );
-// expect(filePaths).toMatchObject([abiSimplePath]);
-// });
-
-// const abiWithOverloadedEvents = [
-// {
-// inputs: [],
-// stateMutability: "nonpayable",
-// type: "constructor",
-// },
-// {
-// inputs: [
-// {
-// indexed: true,
-// type: "address",
-// },
-// {
-// indexed: true,
-// type: "address",
-// },
-// {
-// indexed: false,
-// type: "uint256",
-// },
-// ],
-// name: "Transfer",
-// type: "event",
-// },
-// {
-// inputs: [
-// {
-// indexed: true,
-// type: "uint8",
-// },
-// {
-// indexed: true,
-// type: "uint256",
-// },
-// {
-// indexed: false,
-// type: "uint256",
-// },
-// {
-// indexed: false,
-// type: "address",
-// },
-// ],
-// name: "Transfer",
-// type: "event",
-// },
-// ] as const;
-
-// test("getEvents handles overloaded events", () => {
-// const events = getEvents({ abi: abiWithOverloadedEvents });
-
-// expect(events).toMatchObject({
-// "Transfer(address indexed, address indexed, uint256)": {
-// safeName: "Transfer(address indexed, address indexed, uint256)",
-// signature: "event Transfer(address indexed, address indexed, uint256)",
-// selector:
-// "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
-// abiItem: {
-// inputs: [
-// {
-// indexed: true,
-// type: "address",
-// },
-// {
-// indexed: true,
-// type: "address",
-// },
-// {
-// indexed: false,
-// type: "uint256",
-// },
-// ],
-// name: "Transfer",
-// type: "event",
-// },
-// },
-// "Transfer(uint8 indexed, uint256 indexed, uint256, address)": {
-// safeName: "Transfer(uint8 indexed, uint256 indexed, uint256, address)",
-// signature:
-// "event Transfer(uint8 indexed, uint256 indexed, uint256, address)",
-// selector:
-// "0x7d80b356169a1ce57762f79d1bc650835653d9798678ef3691964dfcde65cd76",
-// abiItem: {
-// inputs: [
-// {
-// indexed: true,
-// type: "uint8",
-// },
-// {
-// indexed: true,
-// type: "uint256",
-// },
-// {
-// indexed: false,
-// type: "uint256",
-// },
-// {
-// indexed: false,
-// type: "address",
-// },
-// ],
-// name: "Transfer",
-// type: "event",
-// },
-// },
-// });
-// });
diff --git a/packages/core/src/config/abi.ts b/packages/core/src/config/abi.ts
index 13e6d40cd..948893faf 100644
--- a/packages/core/src/config/abi.ts
+++ b/packages/core/src/config/abi.ts
@@ -3,82 +3,6 @@ import { type Hex, getEventSelector } from "viem";
import { getDuplicateElements } from "@/utils/duplicates";
-// export const buildAbi = ({
-// abiConfig,
-// configFilePath,
-// }: {
-// abiConfig: string | any[] | object | (string | any[] | object)[];
-// configFilePath: string;
-// }) => {
-// let resolvedAbi: Abi;
-// const filePaths: string[] = [];
-
-// if (
-// typeof abiConfig === "string" ||
-// (Array.isArray(abiConfig) &&
-// (abiConfig.length === 0 || typeof abiConfig[0] === "object"))
-// ) {
-// // If abiConfig is a string or an ABI itself, treat it as a single ABI.
-// const { abi, filePath } = buildSingleAbi({ abiConfig, configFilePath });
-// resolvedAbi = abi;
-// if (filePath) filePaths.push(filePath);
-// } else {
-// // Otherwise, handle as an array of of ABIs.
-// const results = (abiConfig as (object | any[])[]).map((a) =>
-// buildSingleAbi({ abiConfig: a, configFilePath })
-// );
-
-// const mergedAbi = results
-// .map(({ abi }) => abi.filter((item) => item.type !== "constructor"))
-// .flat()
-// .flat();
-// const mergedUniqueAbi = [
-// ...new Map(
-// mergedAbi.map((item) => [JSON.stringify(item), item])
-// ).values(),
-// ];
-
-// filePaths.push(
-// ...results.map((r) => r.filePath).filter((f): f is string => !!f)
-// );
-
-// resolvedAbi = mergedUniqueAbi;
-// }
-
-// return {
-// abi: resolvedAbi,
-// filePaths,
-// };
-// };
-
-// const buildSingleAbi = ({
-// abiConfig,
-// configFilePath,
-// }: {
-// abiConfig: string | any[] | object;
-// configFilePath: string;
-// }) => {
-// let filePath: string | undefined = undefined;
-// let abi: Abi;
-
-// if (typeof abiConfig === "string") {
-// // If a string, treat it as a file path.
-// filePath = path.isAbsolute(abiConfig)
-// ? abiConfig
-// : path.join(path.dirname(configFilePath), abiConfig);
-
-// const abiString = readFileSync(filePath, "utf-8");
-// abi = JSON.parse(abiString);
-// } else {
-// // Otherwise, treat as the ABI itself
-// abi = abiConfig as unknown as Abi;
-// }
-
-// // NOTE: Not currently using the filePath arg here, but eventually
-// // could use it to watch for changes and reload.
-// return { abi, filePath };
-// };
-
export type LogEventMetadata = {
// Event name (if no overloads) or full event signature (if name is overloaded).
// This is the event name used when registering indexing functions using `ponder.on("ContractName:EventName", ...)`
diff --git a/packages/core/src/config/contracts.ts b/packages/core/src/config/contracts.ts
deleted file mode 100644
index 90e753490..000000000
--- a/packages/core/src/config/contracts.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-// import type { Abi, Address } from "abitype";
-
-// import type { Options } from "@/config/options";
-// import type { ResolvedConfig } from "@/config/types";
-// import { toLowerCase } from "@/utils/lowercase";
-
-// import { buildAbi } from "./abi";
-// import type { Network } from "./networks";
-
-// export type Contract = {
-// name: string;
-// address: Address;
-// network: Network;
-// abi: Abi;
-// };
-
-// export function buildContracts({
-// config,
-// options,
-// networks,
-// }: {
-// config: ResolvedConfig;
-// options: Options;
-// networks: Network[];
-// }) {
-// const contracts = config.contracts ?? [];
-
-// return contracts
-// .filter(
-// (
-// contract
-// ): contract is (typeof contracts)[number] & { address: Address } =>
-// !!contract.address
-// )
-// .map((contract) => {
-// const address = toLowerCase(contract.address);
-
-// const { abi } = buildAbi({
-// abiConfig: contract.abi,
-// configFilePath: options.configFile,
-// });
-
-// // Get the contract network/provider.
-// const network = networks.find((n) => n.name === contract.network);
-// if (!network) {
-// throw new Error(
-// `Network [${contract.network}] not found for contract: ${contract.name}`
-// );
-// }
-
-// return { name: contract.name, address, network, abi } satisfies Contract;
-// });
-// }
diff --git a/packages/core/src/indexing/contract.test.ts b/packages/core/src/indexing/contract.test.ts
deleted file mode 100644
index e2f098d53..000000000
--- a/packages/core/src/indexing/contract.test.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-// import { getFunctionSelector, hexToBigInt } from "viem";
-// import { beforeEach, expect, test, vi } from "vitest";
-
-// import { usdcContractConfig } from "@/_test/constants";
-// import { setupEventStore } from "@/_test/setup";
-// import { publicClient } from "@/_test/utils";
-// import type { Contract } from "@/config/contracts";
-// import type { Network } from "@/config/networks";
-
-// import { buildReadOnlyContracts } from "./contract";
-
-// beforeEach((context) => setupEventStore(context));
-
-// const network: Network = {
-// name: "mainnet",
-// chainId: 1,
-// client: publicClient,
-// pollingInterval: 1_000,
-// defaultMaxBlockRange: 3,
-// finalityBlockCount: 10,
-// maxRpcRequestConcurrency: 10,
-// };
-
-// const contracts: Contract[] = [
-// {
-// name: "USDC",
-// address: usdcContractConfig.address,
-// abi: usdcContractConfig.abi,
-// network: network,
-// },
-// ];
-
-// // Test data generated from Alchemy Composer.
-// const usdcTotalSupply16375000 = 40921687992499550n;
-// const usdcTotalSupply16380000 = 40695630049769550n; // This is "latest" for our test setup.
-
-// test("getInjectedContract() returns data", async (context) => {
-// const { eventStore } = context;
-
-// const readOnlyContracts = buildReadOnlyContracts({
-// contracts,
-// getCurrentBlockNumber: () => 16375000n,
-// eventStore,
-// });
-// const contract = readOnlyContracts["USDC"];
-
-// const decimals = await contract.read.decimals();
-// expect(decimals).toBe(6);
-// });
-
-// test("getInjectedContract() uses current block number if no overrides are provided", async (context) => {
-// const { eventStore } = context;
-
-// const readOnlyContracts = buildReadOnlyContracts({
-// contracts,
-// getCurrentBlockNumber: () => 16375000n,
-// eventStore,
-// });
-// const contract = readOnlyContracts["USDC"];
-
-// const totalSupply = await contract.read.totalSupply();
-
-// expect(totalSupply).toBe(usdcTotalSupply16375000);
-// });
-
-// test("getInjectedContract() caches the read result if no overrides are provided", async (context) => {
-// const { eventStore } = context;
-
-// const callSpy = vi.spyOn(network.client, "call");
-
-// const readOnlyContracts = buildReadOnlyContracts({
-// contracts,
-// getCurrentBlockNumber: () => 16375000n,
-// eventStore,
-// });
-// const contract = readOnlyContracts["USDC"];
-
-// await contract.read.totalSupply();
-
-// expect(callSpy).toHaveBeenCalledTimes(1);
-
-// const cachedContractReadResult = await eventStore.getContractReadResult({
-// address: contract.address,
-// blockNumber: 16375000n,
-// chainId: 1,
-// data: getFunctionSelector("totalSupply()"),
-// });
-
-// expect(cachedContractReadResult).not.toBeNull();
-// expect(hexToBigInt(cachedContractReadResult!.result)).toBe(
-// usdcTotalSupply16375000
-// );
-
-// expect(callSpy).toHaveBeenCalledTimes(1);
-// });
-
-// test("getInjectedContract() uses blockTag override if provided", async (context) => {
-// const { eventStore } = context;
-
-// const readOnlyContracts = buildReadOnlyContracts({
-// contracts,
-// getCurrentBlockNumber: () => 16375000n,
-// eventStore,
-// });
-// const contract = readOnlyContracts["USDC"];
-
-// const totalSupply = await contract.read.totalSupply({
-// blockTag: "latest",
-// });
-
-// expect(totalSupply).toBe(usdcTotalSupply16380000);
-// });
-
-// test("getInjectedContract() does not cache data if blockTag override is provided", async (context) => {
-// const { eventStore } = context;
-
-// const readOnlyContracts = buildReadOnlyContracts({
-// contracts,
-// getCurrentBlockNumber: () => 16375000n,
-// eventStore,
-// });
-// const contract = readOnlyContracts["USDC"];
-
-// await contract.read.totalSupply({
-// blockTag: "latest",
-// });
-
-// const cachedContractReadResult = await eventStore.getContractReadResult({
-// address: contract.address,
-// blockNumber: 16375000n,
-// chainId: 1,
-// data: getFunctionSelector("totalSupply()"),
-// });
-
-// expect(cachedContractReadResult).toBeNull();
-// });
From d0583b3dc55ceb840b0ae7f186d5c8ac08d36e2d Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 13:07:56 -0400
Subject: [PATCH 11/44] end to end not working
---
packages/core/src/Ponder.ts | 2 +-
.../_test/art-gobblers/app/ponder.config.ts | 6 ++---
.../core/src/_test/ens/app/ponder.config.ts | 6 ++---
packages/core/src/build/config.ts | 2 +-
packages/core/src/config/config.test.ts | 23 +++++++++++++++++
.../core/src/config/{types.ts => config.ts} | 25 ++++++++++++-------
packages/core/src/config/database.ts | 2 +-
packages/core/src/config/networks.ts | 2 +-
packages/core/src/config/options.ts | 2 +-
packages/core/src/config/sources.ts | 2 +-
packages/core/src/index.ts | 2 +-
11 files changed, 52 insertions(+), 22 deletions(-)
create mode 100644 packages/core/src/config/config.test.ts
rename packages/core/src/config/{types.ts => config.ts} (86%)
diff --git a/packages/core/src/Ponder.ts b/packages/core/src/Ponder.ts
index 7da2da157..cd4977b4e 100644
--- a/packages/core/src/Ponder.ts
+++ b/packages/core/src/Ponder.ts
@@ -3,10 +3,10 @@ import process from "node:process";
import { BuildService } from "@/build/service";
import { CodegenService } from "@/codegen/service";
+import { type ResolvedConfig } from "@/config/config";
import { buildDatabase } from "@/config/database";
import { type Network, buildNetwork } from "@/config/networks";
import { type Options } from "@/config/options";
-import { type ResolvedConfig } from "@/config/types";
import { UserErrorService } from "@/errors/service";
import { EventAggregatorService } from "@/event-aggregator/service";
import { PostgresEventStore } from "@/event-store/postgres/store";
diff --git a/packages/core/src/_test/art-gobblers/app/ponder.config.ts b/packages/core/src/_test/art-gobblers/app/ponder.config.ts
index 7540d1e6a..f460890b3 100644
--- a/packages/core/src/_test/art-gobblers/app/ponder.config.ts
+++ b/packages/core/src/_test/art-gobblers/app/ponder.config.ts
@@ -1,9 +1,9 @@
import { http } from "viem";
-import type { Config } from "../../../../dist";
+import { createConfig } from "../../../../dist";
import { ArtGobblersAbi } from "./ArtGobblers.abi";
-export const config: Config = {
+export const config = createConfig({
networks: [
{ name: "mainnet", chainId: 1, transport: http("http://127.0.0.1:8545") },
],
@@ -17,4 +17,4 @@ export const config: Config = {
endBlock: 15870405, // 5 blocks
},
],
-};
+});
diff --git a/packages/core/src/_test/ens/app/ponder.config.ts b/packages/core/src/_test/ens/app/ponder.config.ts
index 0b733399f..c58f7909f 100644
--- a/packages/core/src/_test/ens/app/ponder.config.ts
+++ b/packages/core/src/_test/ens/app/ponder.config.ts
@@ -1,9 +1,9 @@
import { http } from "viem";
-import type { Config } from "../../../../dist";
+import { createConfig } from "../../../../dist";
import { BaseRegistrarImplementationAbi } from "./BaseRegistrarImplementation.abi";
-export const config: Config = {
+export const config = createConfig({
networks: [
{ name: "mainnet", chainId: 1, transport: http("http://127.0.0.1:8545") },
],
@@ -18,4 +18,4 @@ export const config: Config = {
maxBlockRange: 10,
},
],
-};
+});
diff --git a/packages/core/src/build/config.ts b/packages/core/src/build/config.ts
index 601ee0d8d..ae03efc90 100644
--- a/packages/core/src/build/config.ts
+++ b/packages/core/src/build/config.ts
@@ -2,7 +2,7 @@ import { build } from "esbuild";
import { existsSync, rmSync } from "node:fs";
import path from "node:path";
-import type { ResolvedConfig } from "@/config/types";
+import type { ResolvedConfig } from "@/config/config";
import { ensureDirExists } from "@/utils/exists";
export const buildConfig = async ({ configFile }: { configFile: string }) => {
diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts
new file mode 100644
index 000000000..9d66e85ca
--- /dev/null
+++ b/packages/core/src/config/config.test.ts
@@ -0,0 +1,23 @@
+import { http } from "viem";
+import { test } from "vitest";
+
+import { createConfig } from "./config";
+
+test("createConfig enforces matching network names", () => {
+ createConfig({
+ networks: [
+ { name: "mainnet", chainId: 1, transport: http("http://127.0.0.1:8545") },
+ ],
+ contracts: [
+ {
+ name: "BaseRegistrarImplementation",
+ network: [{ name: "mainnet" }],
+ abi: [],
+ address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
+ startBlock: 16370000,
+ endBlock: 16370020,
+ maxBlockRange: 10,
+ },
+ ],
+ });
+});
diff --git a/packages/core/src/config/types.ts b/packages/core/src/config/config.ts
similarity index 86%
rename from packages/core/src/config/types.ts
rename to packages/core/src/config/config.ts
index 5c3045375..cf3b48100 100644
--- a/packages/core/src/config/types.ts
+++ b/packages/core/src/config/config.ts
@@ -1,14 +1,16 @@
import type { Abi, AbiEvent } from "abitype";
import type { Transport } from "viem";
-type ContractRequired = {
+type ContractRequired<
+ TNetworkName extends string | unknown = string | unknown
+> = {
/** Contract name. Must be unique across `contracts` and `filters`. */
name: string;
/**
* Network that this contract is deployed to. Must match a network name in `networks`.
* Any filter information overrides the values in the higher level "contracts" property. Factories cannot override an address and vice versa.
*/
- network: ({ name: string } & Partial)[];
+ network: ({ name: TNetworkName } & Partial)[];
abi: Abi;
};
@@ -44,7 +46,9 @@ type ContractFilter = (
| AbiEvent[];
};
-export type ResolvedConfig = {
+export type ResolvedConfig<
+ TNetworkName extends string | unknown = string | unknown
+> = {
/** Database to use for storing blockchain & entity data. Default: `"postgres"` if `DATABASE_URL` env var is present, otherwise `"sqlite"`. */
database?:
| {
@@ -84,7 +88,7 @@ export type ResolvedConfig = {
maxRpcRequestConcurrency?: number;
}[];
/** List of contracts to sync & index events from. Contracts defined here will be present in `context.contracts`. */
- contracts?: (ContractRequired & ContractFilter)[];
+ contracts?: (ContractRequired & ContractFilter)[];
/** Configuration for Ponder internals. */
options?: {
/** Maximum number of seconds to wait for event processing to be complete before responding as healthy. If event processing exceeds this duration, the API may serve incomplete data. Default: `240` (4 minutes). */
@@ -92,8 +96,11 @@ export type ResolvedConfig = {
};
};
-export type Config =
- | ResolvedConfig
- | Promise
- | (() => ResolvedConfig)
- | (() => Promise);
+/**
+ * Identity function for type-level validation of config
+ */
+export const createConfig = <
+ const TConfig extends ResolvedConfig
+>(
+ config: TConfig
+) => config;
diff --git a/packages/core/src/config/database.ts b/packages/core/src/config/database.ts
index a4eaad923..2366bfbeb 100644
--- a/packages/core/src/config/database.ts
+++ b/packages/core/src/config/database.ts
@@ -2,8 +2,8 @@ import Sqlite from "better-sqlite3";
import path from "node:path";
import pg, { Client, DatabaseError, Pool } from "pg";
+import type { ResolvedConfig } from "@/config/config";
import type { Options } from "@/config/options";
-import type { ResolvedConfig } from "@/config/types";
import { PostgresError } from "@/errors/postgres";
import { SqliteError } from "@/errors/sqlite";
import { ensureDirExists } from "@/utils/exists";
diff --git a/packages/core/src/config/networks.ts b/packages/core/src/config/networks.ts
index 39b1a363f..8ecd9bd61 100644
--- a/packages/core/src/config/networks.ts
+++ b/packages/core/src/config/networks.ts
@@ -1,7 +1,7 @@
import { type Client, type PublicClient, createPublicClient } from "viem";
import * as chains from "viem/chains";
-import type { ResolvedConfig } from "@/config/types";
+import type { ResolvedConfig } from "@/config/config";
import type { Common } from "@/Ponder";
export type Network = {
diff --git a/packages/core/src/config/options.ts b/packages/core/src/config/options.ts
index 8382ecba2..d67114a91 100644
--- a/packages/core/src/config/options.ts
+++ b/packages/core/src/config/options.ts
@@ -3,7 +3,7 @@ import type { LevelWithSilent } from "pino";
import type { CliOptions } from "@/bin/ponder";
-import type { ResolvedConfig } from "./types";
+import type { ResolvedConfig } from "./config";
export type Options = {
configFile: string;
diff --git a/packages/core/src/config/sources.ts b/packages/core/src/config/sources.ts
index 50b74b519..7e59e39f6 100644
--- a/packages/core/src/config/sources.ts
+++ b/packages/core/src/config/sources.ts
@@ -3,9 +3,9 @@ import { Abi, Address, encodeEventTopics, Hex } from "viem";
import { toLowerCase } from "@/utils/lowercase";
import { AbiEvents, getEvents } from "./abi";
+import { ResolvedConfig } from "./config";
import { buildFactoryCriteria } from "./factories";
import { Options } from "./options";
-import { ResolvedConfig } from "./types";
/**
* There are up to 4 topics in an EVM event
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index d26b8e021..09dc15b00 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -1,5 +1,5 @@
export { PonderApp } from "@/build/functions";
-export type { Config, ResolvedConfig } from "@/config/types";
+export { createConfig } from "@/config/config";
export type { Block } from "@/types/block";
export type { ReadOnlyContract } from "@/types/contract";
export type { Log } from "@/types/log";
From 820c8793a7eb1954f930c18cda4617cf7aa90ce9 Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 13:14:37 -0400
Subject: [PATCH 12/44] fix end to end issue
---
packages/core/.gitignore | 3 ++-
packages/core/src/build/config.ts | 44 ++++++++++++++++++++++++++++++-
2 files changed, 45 insertions(+), 2 deletions(-)
diff --git a/packages/core/.gitignore b/packages/core/.gitignore
index 49dea9499..4c13059ce 100644
--- a/packages/core/.gitignore
+++ b/packages/core/.gitignore
@@ -1,4 +1,5 @@
dist/
.ponder/
generated/
-.env.local
\ No newline at end of file
+.env.local
+**/*.node
\ No newline at end of file
diff --git a/packages/core/src/build/config.ts b/packages/core/src/build/config.ts
index ae03efc90..7ffd50c1c 100644
--- a/packages/core/src/build/config.ts
+++ b/packages/core/src/build/config.ts
@@ -1,10 +1,51 @@
-import { build } from "esbuild";
+import { build, Plugin } from "esbuild";
import { existsSync, rmSync } from "node:fs";
import path from "node:path";
import type { ResolvedConfig } from "@/config/config";
import { ensureDirExists } from "@/utils/exists";
+/**
+ * Fixes issue with createConfig() being built
+ *
+ * {@link} https://github.com/evanw/esbuild/issues/1051
+ */
+const nativeNodeModulesPlugin: Plugin = {
+ name: "native-node-modules",
+ setup(build) {
+ // If a ".node" file is imported within a module in the "file" namespace, resolve
+ // it to an absolute path and put it into the "node-file" virtual namespace.
+ build.onResolve({ filter: /\.node$/, namespace: "file" }, (args) => ({
+ path: require.resolve(args.path, { paths: [args.resolveDir] }),
+ namespace: "node-file",
+ }));
+
+ // Files in the "node-file" virtual namespace call "require()" on the
+ // path from esbuild of the ".node" file in the output directory.
+ build.onLoad({ filter: /.*/, namespace: "node-file" }, (args) => ({
+ contents: `
+ import path from ${JSON.stringify(args.path)}
+ try { module.exports = require(path) }
+ catch {}
+ `,
+ }));
+
+ // If a ".node" file is imported within a module in the "node-file" namespace, put
+ // it in the "file" namespace where esbuild's default loading behavior will handle
+ // it. It is already an absolute path since we resolved it to one above.
+ build.onResolve({ filter: /\.node$/, namespace: "node-file" }, (args) => ({
+ path: args.path,
+ namespace: "file",
+ }));
+
+ // Tell esbuild's default loading behavior to use the "file" loader for
+ // these ".node" files.
+ const opts = build.initialOptions;
+ opts.loader = opts.loader || {};
+ opts.loader[".node"] = "file";
+ },
+};
+
export const buildConfig = async ({ configFile }: { configFile: string }) => {
if (!existsSync(configFile)) {
throw new Error(`Ponder config file not found, expected: ${configFile}`);
@@ -22,6 +63,7 @@ export const buildConfig = async ({ configFile }: { configFile: string }) => {
outfile: buildFile,
platform: "node",
format: "cjs",
+ plugins: [nativeNodeModulesPlugin],
// Note: Flipped to true in order to be able to import external files into ponder.config.ts
bundle: true,
logLevel: "silent",
From 9eb46883088fc063cc15017e4b7336870caff6dd Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 13:21:44 -0400
Subject: [PATCH 13/44] handle all types of config
---
packages/core/src/config/config.ts | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index cf3b48100..1946ccfa9 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -10,7 +10,7 @@ type ContractRequired<
* Network that this contract is deployed to. Must match a network name in `networks`.
* Any filter information overrides the values in the higher level "contracts" property. Factories cannot override an address and vice versa.
*/
- network: ({ name: TNetworkName } & Partial)[];
+ network: readonly ({ name: TNetworkName } & Partial)[];
abi: Abi;
};
@@ -62,7 +62,7 @@ export type ResolvedConfig<
connectionString?: string;
};
/** List of blockchain networks. */
- networks: {
+ networks: readonly {
/** Network name. Must be unique across all networks. */
name: string;
/** Chain ID of the network. */
@@ -88,7 +88,7 @@ export type ResolvedConfig<
maxRpcRequestConcurrency?: number;
}[];
/** List of contracts to sync & index events from. Contracts defined here will be present in `context.contracts`. */
- contracts?: (ContractRequired & ContractFilter)[];
+ contracts?: readonly (ContractRequired & ContractFilter)[];
/** Configuration for Ponder internals. */
options?: {
/** Maximum number of seconds to wait for event processing to be complete before responding as healthy. If event processing exceeds this duration, the API may serve incomplete data. Default: `240` (4 minutes). */
@@ -102,5 +102,9 @@ export type ResolvedConfig<
export const createConfig = <
const TConfig extends ResolvedConfig
>(
- config: TConfig
+ config:
+ | TConfig
+ | Promise
+ | (() => TConfig)
+ | (() => Promise)
) => config;
From dda40eeba7c7a41731da8005341377589bdb9d16 Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 13:31:39 -0400
Subject: [PATCH 14/44] update vitest and vite for const type assertions
---
packages/core/package.json | 4 +-
pnpm-lock.yaml | 662 +++++++++++++++++++++++++++++++------
2 files changed, 556 insertions(+), 110 deletions(-)
diff --git a/packages/core/package.json b/packages/core/package.json
index d33ca8aee..2ed844058 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -86,7 +86,7 @@
"rimraf": "^5.0.1",
"supertest": "^6.3.3",
"typescript": "^5.1.3",
- "vite": "^4.1.4",
- "vitest": "^0.29.2"
+ "vite": "^4.5.0",
+ "vitest": "^0.34.6"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 09e1d6cb9..322480909 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -57,7 +57,7 @@ importers:
devDependencies:
'@graphprotocol/graph-cli':
specifier: ^0.51.1
- version: 0.51.1(@types/node@18.16.18)(node-fetch@2.6.11)(typescript@5.1.3)
+ version: 0.51.1(@types/node@18.16.18)(node-fetch@3.3.2)(typescript@5.1.3)
'@graphprotocol/graph-ts':
specifier: ^0.31.0
version: 0.31.0
@@ -462,11 +462,11 @@ importers:
specifier: ^5.1.3
version: 5.1.3
vite:
- specifier: ^4.1.4
- version: 4.1.4(@types/node@18.16.18)
+ specifier: ^4.5.0
+ version: 4.5.0(@types/node@18.16.18)
vitest:
- specifier: ^0.29.2
- version: 0.29.2
+ specifier: ^0.34.6
+ version: 0.34.6
packages/create-ponder:
dependencies:
@@ -530,10 +530,10 @@ importers:
dependencies:
'@typescript-eslint/eslint-plugin':
specifier: ^6.3.0
- version: 6.3.0(@typescript-eslint/parser@6.3.0)(eslint@8.43.0)(typescript@5.1.3)
+ version: 6.3.0(@typescript-eslint/parser@6.3.0)(eslint@8.43.0)(typescript@5.2.2)
'@typescript-eslint/parser':
specifier: ^6.3.0
- version: 6.3.0(eslint@8.43.0)(typescript@5.1.3)
+ version: 6.3.0(eslint@8.43.0)(typescript@5.2.2)
eslint:
specifier: '>= 3'
version: 8.43.0
@@ -2029,8 +2029,8 @@ packages:
'@jridgewell/trace-mapping': 0.3.9
dev: true
- /@esbuild/android-arm64@0.16.17:
- resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==}
+ /@esbuild/android-arm64@0.18.20:
+ resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
@@ -2047,8 +2047,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-arm@0.16.17:
- resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==}
+ /@esbuild/android-arm@0.18.20:
+ resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
@@ -2065,8 +2065,8 @@ packages:
dev: true
optional: true
- /@esbuild/android-x64@0.16.17:
- resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==}
+ /@esbuild/android-x64@0.18.20:
+ resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
@@ -2083,8 +2083,8 @@ packages:
dev: true
optional: true
- /@esbuild/darwin-arm64@0.16.17:
- resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==}
+ /@esbuild/darwin-arm64@0.18.20:
+ resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
@@ -2101,8 +2101,8 @@ packages:
dev: true
optional: true
- /@esbuild/darwin-x64@0.16.17:
- resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==}
+ /@esbuild/darwin-x64@0.18.20:
+ resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
@@ -2119,8 +2119,8 @@ packages:
dev: true
optional: true
- /@esbuild/freebsd-arm64@0.16.17:
- resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==}
+ /@esbuild/freebsd-arm64@0.18.20:
+ resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
@@ -2137,8 +2137,8 @@ packages:
dev: true
optional: true
- /@esbuild/freebsd-x64@0.16.17:
- resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==}
+ /@esbuild/freebsd-x64@0.18.20:
+ resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
@@ -2155,8 +2155,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-arm64@0.16.17:
- resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==}
+ /@esbuild/linux-arm64@0.18.20:
+ resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
@@ -2173,8 +2173,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-arm@0.16.17:
- resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==}
+ /@esbuild/linux-arm@0.18.20:
+ resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
@@ -2191,8 +2191,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-ia32@0.16.17:
- resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==}
+ /@esbuild/linux-ia32@0.18.20:
+ resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
@@ -2218,8 +2218,8 @@ packages:
dev: false
optional: true
- /@esbuild/linux-loong64@0.16.17:
- resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==}
+ /@esbuild/linux-loong64@0.18.20:
+ resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
@@ -2236,8 +2236,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-mips64el@0.16.17:
- resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==}
+ /@esbuild/linux-mips64el@0.18.20:
+ resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
@@ -2254,8 +2254,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-ppc64@0.16.17:
- resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==}
+ /@esbuild/linux-ppc64@0.18.20:
+ resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
@@ -2272,8 +2272,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-riscv64@0.16.17:
- resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==}
+ /@esbuild/linux-riscv64@0.18.20:
+ resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
@@ -2290,8 +2290,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-s390x@0.16.17:
- resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==}
+ /@esbuild/linux-s390x@0.18.20:
+ resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
@@ -2308,8 +2308,8 @@ packages:
dev: true
optional: true
- /@esbuild/linux-x64@0.16.17:
- resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==}
+ /@esbuild/linux-x64@0.18.20:
+ resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
@@ -2326,8 +2326,8 @@ packages:
dev: true
optional: true
- /@esbuild/netbsd-x64@0.16.17:
- resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==}
+ /@esbuild/netbsd-x64@0.18.20:
+ resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
@@ -2344,8 +2344,8 @@ packages:
dev: true
optional: true
- /@esbuild/openbsd-x64@0.16.17:
- resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==}
+ /@esbuild/openbsd-x64@0.18.20:
+ resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
@@ -2362,8 +2362,8 @@ packages:
dev: true
optional: true
- /@esbuild/sunos-x64@0.16.17:
- resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==}
+ /@esbuild/sunos-x64@0.18.20:
+ resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
@@ -2380,8 +2380,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-arm64@0.16.17:
- resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==}
+ /@esbuild/win32-arm64@0.18.20:
+ resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
@@ -2398,8 +2398,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-ia32@0.16.17:
- resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==}
+ /@esbuild/win32-ia32@0.18.20:
+ resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
@@ -2416,8 +2416,8 @@ packages:
dev: true
optional: true
- /@esbuild/win32-x64@0.16.17:
- resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==}
+ /@esbuild/win32-x64@0.18.20:
+ resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
@@ -2636,7 +2636,7 @@ packages:
js-yaml: 4.1.0
dev: true
- /@graphprotocol/graph-cli@0.51.1(@types/node@18.16.18)(node-fetch@2.6.11)(typescript@5.1.3):
+ /@graphprotocol/graph-cli@0.51.1(@types/node@18.16.18)(node-fetch@3.3.2)(typescript@5.1.3):
resolution: {integrity: sha512-6W1aphU/0Uq1ju8e4Ya5plmXUcjCR2N30wjDrf5Cn6QjHsk9VUqMt4x3+D3DHGSVvXve2+ThiEU2s2xktP3PTw==}
engines: {node: '>=14'}
hasBin: true
@@ -2656,7 +2656,7 @@ packages:
gluegun: 5.1.2(debug@4.3.4)
graphql: 15.5.0
immutable: 4.2.1
- ipfs-http-client: 55.0.0(node-fetch@2.6.11)
+ ipfs-http-client: 55.0.0(node-fetch@3.3.2)
jayson: 4.0.0
js-yaml: 3.14.1
prettier: 1.19.1
@@ -2745,6 +2745,13 @@ packages:
wrap-ansi: 8.1.0
wrap-ansi-cjs: /wrap-ansi@7.0.0
+ /@jest/schemas@29.6.3:
+ resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dependencies:
+ '@sinclair/typebox': 0.27.8
+ dev: true
+
/@jridgewell/gen-mapping@0.1.1:
resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==}
engines: {node: '>=6.0.0'}
@@ -3266,6 +3273,10 @@ packages:
- encoding
dev: false
+ /@sinclair/typebox@0.27.8:
+ resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
+ dev: true
+
/@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.21.0):
resolution: {integrity: sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==}
engines: {node: '>=10'}
@@ -3500,10 +3511,20 @@ packages:
'@types/chai': 4.3.4
dev: true
+ /@types/chai-subset@1.3.4:
+ resolution: {integrity: sha512-CCWNXrJYSUIojZ1149ksLl3AN9cmZ5djf+yUoVVV+NuYrtydItQVlL2ZDqyC6M6O9LWRnVf8yYDxbXHO2TfQZg==}
+ dependencies:
+ '@types/chai': 4.3.9
+ dev: true
+
/@types/chai@4.3.4:
resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==}
dev: true
+ /@types/chai@4.3.9:
+ resolution: {integrity: sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==}
+ dev: true
+
/@types/cli-progress@3.11.0:
resolution: {integrity: sha512-XhXhBv1R/q2ahF3BM7qT5HLzJNlIL0wbcGyZVjqOTqAybAnsLisd7gy1UCyIqpL+5Iv6XhlSyzjLCnI2sIdbCg==}
dependencies:
@@ -3815,6 +3836,37 @@ packages:
typescript: 5.1.3
transitivePeerDependencies:
- supports-color
+ dev: true
+
+ /@typescript-eslint/eslint-plugin@6.3.0(@typescript-eslint/parser@6.3.0)(eslint@8.43.0)(typescript@5.2.2):
+ resolution: {integrity: sha512-IZYjYZ0ifGSLZbwMqIip/nOamFiWJ9AH+T/GYNZBWkVcyNQOFGtSMoWV7RvY4poYCMZ/4lHzNl796WOSNxmk8A==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
+ eslint: ^7.0.0 || ^8.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@eslint-community/regexpp': 4.5.1
+ '@typescript-eslint/parser': 6.3.0(eslint@8.43.0)(typescript@5.2.2)
+ '@typescript-eslint/scope-manager': 6.3.0
+ '@typescript-eslint/type-utils': 6.3.0(eslint@8.43.0)(typescript@5.2.2)
+ '@typescript-eslint/utils': 6.3.0(eslint@8.43.0)(typescript@5.2.2)
+ '@typescript-eslint/visitor-keys': 6.3.0
+ debug: 4.3.4(supports-color@8.1.1)
+ eslint: 8.43.0
+ graphemer: 1.4.0
+ ignore: 5.2.4
+ natural-compare: 1.4.0
+ natural-compare-lite: 1.4.0
+ semver: 7.5.4
+ ts-api-utils: 1.0.1(typescript@5.2.2)
+ typescript: 5.2.2
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
/@typescript-eslint/parser@5.59.11(eslint@8.43.0)(typescript@4.9.5):
resolution: {integrity: sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==}
@@ -3855,6 +3907,28 @@ packages:
typescript: 5.1.3
transitivePeerDependencies:
- supports-color
+ dev: true
+
+ /@typescript-eslint/parser@6.3.0(eslint@8.43.0)(typescript@5.2.2):
+ resolution: {integrity: sha512-ibP+y2Gr6p0qsUkhs7InMdXrwldjxZw66wpcQq9/PzAroM45wdwyu81T+7RibNCh8oc0AgrsyCwJByncY0Ongg==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ peerDependencies:
+ eslint: ^7.0.0 || ^8.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@typescript-eslint/scope-manager': 6.3.0
+ '@typescript-eslint/types': 6.3.0
+ '@typescript-eslint/typescript-estree': 6.3.0(typescript@5.2.2)
+ '@typescript-eslint/visitor-keys': 6.3.0
+ debug: 4.3.4(supports-color@8.1.1)
+ eslint: 8.43.0
+ typescript: 5.2.2
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
/@typescript-eslint/scope-manager@5.59.11:
resolution: {integrity: sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q==}
@@ -3889,6 +3963,27 @@ packages:
typescript: 5.1.3
transitivePeerDependencies:
- supports-color
+ dev: true
+
+ /@typescript-eslint/type-utils@6.3.0(eslint@8.43.0)(typescript@5.2.2):
+ resolution: {integrity: sha512-7Oj+1ox1T2Yc8PKpBvOKWhoI/4rWFd1j7FA/rPE0lbBPXTKjdbtC+7Ev0SeBjEKkIhKWVeZSP+mR7y1Db1CdfQ==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ peerDependencies:
+ eslint: ^7.0.0 || ^8.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@typescript-eslint/typescript-estree': 6.3.0(typescript@5.2.2)
+ '@typescript-eslint/utils': 6.3.0(eslint@8.43.0)(typescript@5.2.2)
+ debug: 4.3.4(supports-color@8.1.1)
+ eslint: 8.43.0
+ ts-api-utils: 1.0.1(typescript@5.2.2)
+ typescript: 5.2.2
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
/@typescript-eslint/types@5.59.11:
resolution: {integrity: sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA==}
@@ -3939,6 +4034,28 @@ packages:
typescript: 5.1.3
transitivePeerDependencies:
- supports-color
+ dev: true
+
+ /@typescript-eslint/typescript-estree@6.3.0(typescript@5.2.2):
+ resolution: {integrity: sha512-Xh4NVDaC4eYKY4O3QGPuQNp5NxBAlEvNQYOqJquR2MePNxO11E5K3t5x4M4Mx53IZvtpW+mBxIT0s274fLUocg==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@typescript-eslint/types': 6.3.0
+ '@typescript-eslint/visitor-keys': 6.3.0
+ debug: 4.3.4(supports-color@8.1.1)
+ globby: 11.1.0
+ is-glob: 4.0.3
+ semver: 7.5.4
+ ts-api-utils: 1.0.1(typescript@5.2.2)
+ typescript: 5.2.2
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
/@typescript-eslint/utils@6.3.0(eslint@8.43.0)(typescript@5.1.3):
resolution: {integrity: sha512-hLLg3BZE07XHnpzglNBG8P/IXq/ZVXraEbgY7FM0Cnc1ehM8RMdn9mat3LubJ3KBeYXXPxV1nugWbQPjGeJk6Q==}
@@ -3957,6 +4074,26 @@ packages:
transitivePeerDependencies:
- supports-color
- typescript
+ dev: true
+
+ /@typescript-eslint/utils@6.3.0(eslint@8.43.0)(typescript@5.2.2):
+ resolution: {integrity: sha512-hLLg3BZE07XHnpzglNBG8P/IXq/ZVXraEbgY7FM0Cnc1ehM8RMdn9mat3LubJ3KBeYXXPxV1nugWbQPjGeJk6Q==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ peerDependencies:
+ eslint: ^7.0.0 || ^8.0.0
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.43.0)
+ '@types/json-schema': 7.0.12
+ '@types/semver': 7.5.0
+ '@typescript-eslint/scope-manager': 6.3.0
+ '@typescript-eslint/types': 6.3.0
+ '@typescript-eslint/typescript-estree': 6.3.0(typescript@5.2.2)
+ eslint: 8.43.0
+ semver: 7.5.4
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+ dev: false
/@typescript-eslint/visitor-keys@5.59.11:
resolution: {integrity: sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA==}
@@ -3998,6 +4135,14 @@ packages:
chai: 4.3.7
dev: true
+ /@vitest/expect@0.34.6:
+ resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==}
+ dependencies:
+ '@vitest/spy': 0.34.6
+ '@vitest/utils': 0.34.6
+ chai: 4.3.10
+ dev: true
+
/@vitest/runner@0.29.2:
resolution: {integrity: sha512-A1P65f5+6ru36AyHWORhuQBJrOOcmDuhzl5RsaMNFe2jEkoj0faEszQS4CtPU/LxUYVIazlUtZTY0OEZmyZBnA==}
dependencies:
@@ -4006,12 +4151,34 @@ packages:
pathe: 1.1.0
dev: true
+ /@vitest/runner@0.34.6:
+ resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==}
+ dependencies:
+ '@vitest/utils': 0.34.6
+ p-limit: 4.0.0
+ pathe: 1.1.1
+ dev: true
+
+ /@vitest/snapshot@0.34.6:
+ resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==}
+ dependencies:
+ magic-string: 0.30.5
+ pathe: 1.1.1
+ pretty-format: 29.7.0
+ dev: true
+
/@vitest/spy@0.29.2:
resolution: {integrity: sha512-Hc44ft5kaAytlGL2PyFwdAsufjbdOvHklwjNy/gy/saRbg9Kfkxfh+PklLm1H2Ib/p586RkQeNFKYuJInUssyw==}
dependencies:
tinyspy: 1.1.1
dev: true
+ /@vitest/spy@0.34.6:
+ resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==}
+ dependencies:
+ tinyspy: 2.2.0
+ dev: true
+
/@vitest/utils@0.29.2:
resolution: {integrity: sha512-F14/Uc+vCdclStS2KEoXJlOLAEyqRhnw0gM27iXw9bMTcyKRPJrQ+rlC6XZ125GIPvvKYMPpVxNhiou6PsEeYQ==}
dependencies:
@@ -4022,6 +4189,14 @@ packages:
pretty-format: 27.5.1
dev: true
+ /@vitest/utils@0.34.6:
+ resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==}
+ dependencies:
+ diff-sequences: 29.6.3
+ loupe: 2.3.7
+ pretty-format: 29.7.0
+ dev: true
+
/@wagmi/chains@1.0.0(typescript@5.1.3):
resolution: {integrity: sha512-eNbqRWyHbivcMNq5tbXJks4NaOzVLHnNQauHPeE/EDT9AlpqzcrMc+v2T1/2Iw8zN4zgqB86NCsxeJHJs7+xng==}
peerDependencies:
@@ -4144,11 +4319,22 @@ packages:
engines: {node: '>=0.4.0'}
dev: true
+ /acorn-walk@8.3.0:
+ resolution: {integrity: sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==}
+ engines: {node: '>=0.4.0'}
+ dev: true
+
/acorn@8.10.0:
resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
engines: {node: '>=0.4.0'}
hasBin: true
+ /acorn@8.11.2:
+ resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+ dev: true
+
/aggregate-error@3.1.0:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
@@ -4846,6 +5032,19 @@ packages:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
dev: false
+ /chai@4.3.10:
+ resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==}
+ engines: {node: '>=4'}
+ dependencies:
+ assertion-error: 1.1.0
+ check-error: 1.0.3
+ deep-eql: 4.1.3
+ get-func-name: 2.0.2
+ loupe: 2.3.7
+ pathval: 1.1.1
+ type-detect: 4.0.8
+ dev: true
+
/chai@4.3.7:
resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==}
engines: {node: '>=4'}
@@ -4920,6 +5119,12 @@ packages:
resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==}
dev: true
+ /check-error@1.0.3:
+ resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
+ dependencies:
+ get-func-name: 2.0.2
+ dev: true
+
/chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
@@ -4932,7 +5137,7 @@ packages:
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
- fsevents: 2.3.2
+ fsevents: 2.3.3
/chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
@@ -5680,6 +5885,11 @@ packages:
engines: {node: '>= 6'}
dev: false
+ /data-uri-to-buffer@4.0.1:
+ resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
+ engines: {node: '>= 12'}
+ dev: true
+
/dataloader@1.4.0:
resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==}
dev: true
@@ -5859,6 +6069,11 @@ packages:
wrappy: 1.0.2
dev: true
+ /diff-sequences@29.6.3:
+ resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dev: true
+
/diff@4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
@@ -5874,11 +6089,11 @@ packages:
dependencies:
path-type: 4.0.0
- /dns-over-http-resolver@1.2.3(node-fetch@2.6.11):
+ /dns-over-http-resolver@1.2.3(node-fetch@3.3.2):
resolution: {integrity: sha512-miDiVSI6KSNbi4SVifzO/reD8rMnxgrlnkrlkugOLQpWQTe2qMdHsZp5DmfKjxNE+/T3VAAYLQUZMv9SMr6+AA==}
dependencies:
debug: 4.3.4(supports-color@8.1.1)
- native-fetch: 3.0.0(node-fetch@2.6.11)
+ native-fetch: 3.0.0(node-fetch@3.3.2)
receptacle: 1.3.2
transitivePeerDependencies:
- node-fetch
@@ -6384,34 +6599,34 @@ packages:
esbuild-windows-arm64: 0.15.4
dev: false
- /esbuild@0.16.17:
- resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==}
+ /esbuild@0.18.20:
+ resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
- '@esbuild/android-arm': 0.16.17
- '@esbuild/android-arm64': 0.16.17
- '@esbuild/android-x64': 0.16.17
- '@esbuild/darwin-arm64': 0.16.17
- '@esbuild/darwin-x64': 0.16.17
- '@esbuild/freebsd-arm64': 0.16.17
- '@esbuild/freebsd-x64': 0.16.17
- '@esbuild/linux-arm': 0.16.17
- '@esbuild/linux-arm64': 0.16.17
- '@esbuild/linux-ia32': 0.16.17
- '@esbuild/linux-loong64': 0.16.17
- '@esbuild/linux-mips64el': 0.16.17
- '@esbuild/linux-ppc64': 0.16.17
- '@esbuild/linux-riscv64': 0.16.17
- '@esbuild/linux-s390x': 0.16.17
- '@esbuild/linux-x64': 0.16.17
- '@esbuild/netbsd-x64': 0.16.17
- '@esbuild/openbsd-x64': 0.16.17
- '@esbuild/sunos-x64': 0.16.17
- '@esbuild/win32-arm64': 0.16.17
- '@esbuild/win32-ia32': 0.16.17
- '@esbuild/win32-x64': 0.16.17
+ '@esbuild/android-arm': 0.18.20
+ '@esbuild/android-arm64': 0.18.20
+ '@esbuild/android-x64': 0.18.20
+ '@esbuild/darwin-arm64': 0.18.20
+ '@esbuild/darwin-x64': 0.18.20
+ '@esbuild/freebsd-arm64': 0.18.20
+ '@esbuild/freebsd-x64': 0.18.20
+ '@esbuild/linux-arm': 0.18.20
+ '@esbuild/linux-arm64': 0.18.20
+ '@esbuild/linux-ia32': 0.18.20
+ '@esbuild/linux-loong64': 0.18.20
+ '@esbuild/linux-mips64el': 0.18.20
+ '@esbuild/linux-ppc64': 0.18.20
+ '@esbuild/linux-riscv64': 0.18.20
+ '@esbuild/linux-s390x': 0.18.20
+ '@esbuild/linux-x64': 0.18.20
+ '@esbuild/netbsd-x64': 0.18.20
+ '@esbuild/openbsd-x64': 0.18.20
+ '@esbuild/sunos-x64': 0.18.20
+ '@esbuild/win32-arm64': 0.18.20
+ '@esbuild/win32-ia32': 0.18.20
+ '@esbuild/win32-x64': 0.18.20
dev: true
/esbuild@0.18.4:
@@ -7074,6 +7289,14 @@ packages:
dependencies:
reusify: 1.0.4
+ /fetch-blob@3.2.0:
+ resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
+ engines: {node: ^12.20 || >= 14.13}
+ dependencies:
+ node-domexception: 1.0.0
+ web-streams-polyfill: 3.2.1
+ dev: true
+
/file-entry-cache@6.0.1:
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
engines: {node: ^10.12.0 || >=12.0.0}
@@ -7214,6 +7437,13 @@ packages:
mime-types: 2.1.35
dev: true
+ /formdata-polyfill@4.0.10:
+ resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
+ engines: {node: '>=12.20.0'}
+ dependencies:
+ fetch-blob: 3.2.0
+ dev: true
+
/formidable@2.1.1:
resolution: {integrity: sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==}
dependencies:
@@ -7281,8 +7511,8 @@ packages:
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
- /fsevents@2.3.2:
- resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ /fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
@@ -7318,6 +7548,10 @@ packages:
resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==}
dev: true
+ /get-func-name@2.0.2:
+ resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
+ dev: true
+
/get-intrinsic@1.2.1:
resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
dependencies:
@@ -8066,19 +8300,19 @@ packages:
engines: {node: '>= 0.10'}
dev: false
- /ipfs-core-types@0.9.0(node-fetch@2.6.11):
+ /ipfs-core-types@0.9.0(node-fetch@3.3.2):
resolution: {integrity: sha512-VJ8vJSHvI1Zm7/SxsZo03T+zzpsg8pkgiIi5hfwSJlsrJ1E2v68QPlnLshGHUSYw89Oxq0IbETYl2pGTFHTWfg==}
deprecated: js-IPFS has been deprecated in favour of Helia - please see https://github.com/ipfs/js-ipfs/issues/4336 for details
dependencies:
interface-datastore: 6.1.1
- multiaddr: 10.0.1(node-fetch@2.6.11)
+ multiaddr: 10.0.1(node-fetch@3.3.2)
multiformats: 9.9.0
transitivePeerDependencies:
- node-fetch
- supports-color
dev: true
- /ipfs-core-utils@0.13.0(node-fetch@2.6.11):
+ /ipfs-core-utils@0.13.0(node-fetch@3.3.2):
resolution: {integrity: sha512-HP5EafxU4/dLW3U13CFsgqVO5Ika8N4sRSIb/dTg16NjLOozMH31TXV0Grtu2ZWo1T10ahTzMvrfT5f4mhioXw==}
deprecated: js-IPFS has been deprecated in favour of Helia - please see https://github.com/ipfs/js-ipfs/issues/4336 for details
dependencies:
@@ -8087,7 +8321,7 @@ packages:
browser-readablestream-to-it: 1.0.3
debug: 4.3.4(supports-color@8.1.1)
err-code: 3.0.1
- ipfs-core-types: 0.9.0(node-fetch@2.6.11)
+ ipfs-core-types: 0.9.0(node-fetch@3.3.2)
ipfs-unixfs: 6.0.9
ipfs-utils: 9.0.14
it-all: 1.0.6
@@ -8095,8 +8329,8 @@ packages:
it-peekable: 1.0.3
it-to-stream: 1.0.0
merge-options: 3.0.4
- multiaddr: 10.0.1(node-fetch@2.6.11)
- multiaddr-to-uri: 8.0.0(node-fetch@2.6.11)
+ multiaddr: 10.0.1(node-fetch@3.3.2)
+ multiaddr-to-uri: 8.0.0(node-fetch@3.3.2)
multiformats: 9.9.0
nanoid: 3.3.6
parse-duration: 1.1.0
@@ -8108,7 +8342,7 @@ packages:
- supports-color
dev: true
- /ipfs-http-client@55.0.0(node-fetch@2.6.11):
+ /ipfs-http-client@55.0.0(node-fetch@3.3.2):
resolution: {integrity: sha512-GpvEs7C7WL9M6fN/kZbjeh4Y8YN7rY8b18tVWZnKxRsVwM25cIFrRI8CwNt3Ugin9yShieI3i9sPyzYGMrLNnQ==}
engines: {node: '>=14.0.0', npm: '>=3.0.0'}
dependencies:
@@ -8119,13 +8353,13 @@ packages:
any-signal: 2.1.2
debug: 4.3.4(supports-color@8.1.1)
err-code: 3.0.1
- ipfs-core-types: 0.9.0(node-fetch@2.6.11)
- ipfs-core-utils: 0.13.0(node-fetch@2.6.11)
+ ipfs-core-types: 0.9.0(node-fetch@3.3.2)
+ ipfs-core-utils: 0.13.0(node-fetch@3.3.2)
ipfs-utils: 9.0.14
it-first: 1.0.7
it-last: 1.0.6
merge-options: 3.0.4
- multiaddr: 10.0.1(node-fetch@2.6.11)
+ multiaddr: 10.0.1(node-fetch@3.3.2)
multiformats: 9.9.0
native-abort-controller: 1.0.4(abort-controller@3.0.0)
parse-duration: 1.1.0
@@ -8960,6 +9194,12 @@ packages:
get-func-name: 2.0.0
dev: true
+ /loupe@2.3.7:
+ resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
+ dependencies:
+ get-func-name: 2.0.2
+ dev: true
+
/lru-cache@4.1.5:
resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
dependencies:
@@ -8981,6 +9221,13 @@ packages:
resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==}
engines: {node: 14 || >=16.14}
+ /magic-string@0.30.5:
+ resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==}
+ engines: {node: '>=12'}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.4.15
+ dev: true
+
/make-error@1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
dev: true
@@ -9828,6 +10075,15 @@ packages:
ufo: 1.1.1
dev: true
+ /mlly@1.4.2:
+ resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==}
+ dependencies:
+ acorn: 8.11.2
+ pathe: 1.1.1
+ pkg-types: 1.0.3
+ ufo: 1.3.1
+ dev: true
+
/module-alias@2.2.2:
resolution: {integrity: sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==}
dev: true
@@ -9847,21 +10103,21 @@ packages:
/ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
- /multiaddr-to-uri@8.0.0(node-fetch@2.6.11):
+ /multiaddr-to-uri@8.0.0(node-fetch@3.3.2):
resolution: {integrity: sha512-dq4p/vsOOUdVEd1J1gl+R2GFrXJQH8yjLtz4hodqdVbieg39LvBOdMQRdQnfbg5LSM/q1BYNVf5CBbwZFFqBgA==}
deprecated: This module is deprecated, please upgrade to @multiformats/multiaddr-to-uri
dependencies:
- multiaddr: 10.0.1(node-fetch@2.6.11)
+ multiaddr: 10.0.1(node-fetch@3.3.2)
transitivePeerDependencies:
- node-fetch
- supports-color
dev: true
- /multiaddr@10.0.1(node-fetch@2.6.11):
+ /multiaddr@10.0.1(node-fetch@3.3.2):
resolution: {integrity: sha512-G5upNcGzEGuTHkzxezPrrD6CaIHR9uo+7MwqhNVcXTs33IInon4y7nMiGxl2CY5hG7chvYQUQhz5V52/Qe3cbg==}
deprecated: This module is deprecated, please upgrade to @multiformats/multiaddr
dependencies:
- dns-over-http-resolver: 1.2.3(node-fetch@2.6.11)
+ dns-over-http-resolver: 1.2.3(node-fetch@3.3.2)
err-code: 3.0.1
is-ip: 3.1.0
multiformats: 9.9.0
@@ -9913,6 +10169,14 @@ packages:
node-fetch: 2.6.11
dev: true
+ /native-fetch@3.0.0(node-fetch@3.3.2):
+ resolution: {integrity: sha512-G3Z7vx0IFb/FQ4JxvtqGABsOTIqRWvgQz6e+erkB+JJD6LrszQtMozEHI4EkmgZQvnGHrpLVzUWk7t4sJCIkVw==}
+ peerDependencies:
+ node-fetch: '*'
+ dependencies:
+ node-fetch: 3.3.2
+ dev: true
+
/natural-compare-lite@1.4.0:
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
@@ -10095,6 +10359,11 @@ packages:
resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==}
dev: true
+ /node-domexception@1.0.0:
+ resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
+ engines: {node: '>=10.5.0'}
+ dev: true
+
/node-fetch@2.6.11:
resolution: {integrity: sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==}
engines: {node: 4.x || >=6.0.0}
@@ -10106,6 +10375,15 @@ packages:
dependencies:
whatwg-url: 5.0.0
+ /node-fetch@3.3.2:
+ resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dependencies:
+ data-uri-to-buffer: 4.0.1
+ fetch-blob: 3.2.0
+ formdata-polyfill: 4.0.10
+ dev: true
+
/node-gyp-build@4.6.0:
resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
hasBin: true
@@ -10541,6 +10819,10 @@ packages:
resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==}
dev: true
+ /pathe@1.1.1:
+ resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==}
+ dev: true
+
/pathval@1.1.1:
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
dev: true
@@ -10687,6 +10969,14 @@ packages:
pathe: 1.1.0
dev: true
+ /pkg-types@1.0.3:
+ resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
+ dependencies:
+ jsonc-parser: 3.2.0
+ mlly: 1.4.2
+ pathe: 1.1.1
+ dev: true
+
/pkg-up@3.1.0:
resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==}
engines: {node: '>=8'}
@@ -10729,8 +11019,8 @@ packages:
source-map-js: 1.0.2
dev: false
- /postcss@8.4.21:
- resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==}
+ /postcss@8.4.31:
+ resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.6
@@ -10816,6 +11106,15 @@ packages:
react-is: 17.0.2
dev: true
+ /pretty-format@29.7.0:
+ resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dependencies:
+ '@jest/schemas': 29.6.3
+ ansi-styles: 5.2.0
+ react-is: 18.2.0
+ dev: true
+
/process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
dev: true
@@ -11027,6 +11326,10 @@ packages:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
dev: true
+ /react-is@18.2.0:
+ resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
+ dev: true
+
/react-native-fetch-api@3.0.0:
resolution: {integrity: sha512-g2rtqPjdroaboDKTsJCTlcmtw54E25OjyaunUP0anOZn4Fuo2IKs8BVfe02zVggA/UysbmfSnRJIqtNkAgggNA==}
dependencies:
@@ -11453,7 +11756,15 @@ packages:
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
optionalDependencies:
- fsevents: 2.3.2
+ fsevents: 2.3.3
+ dev: true
+
+ /rollup@3.29.4:
+ resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==}
+ engines: {node: '>=14.18.0', npm: '>=8.0.0'}
+ hasBin: true
+ optionalDependencies:
+ fsevents: 2.3.3
dev: true
/run-parallel@1.2.0:
@@ -11891,6 +12202,10 @@ packages:
resolution: {integrity: sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==}
dev: true
+ /std-env@3.4.3:
+ resolution: {integrity: sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==}
+ dev: true
+
/stream-to-it@0.2.4:
resolution: {integrity: sha512-4vEbkSs83OahpmBybNJXlJd7d6/RxzkkSdT3I0mnGt79Xd2Kk+e1JqbvAvsQfCeKj3aKb0QIWkyK3/n0j506vQ==}
dependencies:
@@ -12065,6 +12380,12 @@ packages:
acorn: 8.10.0
dev: true
+ /strip-literal@1.3.0:
+ resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==}
+ dependencies:
+ acorn: 8.11.2
+ dev: true
+
/style-to-object@0.4.1:
resolution: {integrity: sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==}
dependencies:
@@ -12346,16 +12667,30 @@ packages:
resolution: {integrity: sha512-iyziEiyFxX4kyxSp+MtY1oCH/lvjH3PxFN8PGCDeqcZWAJ/i+9y+nL85w99PxVzrIvew/GSkSbDYtiGVa85Afg==}
dev: true
+ /tinybench@2.5.1:
+ resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==}
+ dev: true
+
/tinypool@0.3.1:
resolution: {integrity: sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ==}
engines: {node: '>=14.0.0'}
dev: true
+ /tinypool@0.7.0:
+ resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
/tinyspy@1.1.1:
resolution: {integrity: sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==}
engines: {node: '>=14.0.0'}
dev: true
+ /tinyspy@2.2.0:
+ resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
/title@3.5.3:
resolution: {integrity: sha512-20JyowYglSEeCvZv3EZ0nZ046vLarO37prvV0mbtQV7C8DJPGgN967r8SJkqd3XK3K3lD3/Iyfp3avjfil8Q2Q==}
hasBin: true
@@ -12452,6 +12787,16 @@ packages:
typescript: '>=4.2.0'
dependencies:
typescript: 5.1.3
+ dev: true
+
+ /ts-api-utils@1.0.1(typescript@5.2.2):
+ resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==}
+ engines: {node: '>=16.13.0'}
+ peerDependencies:
+ typescript: '>=4.2.0'
+ dependencies:
+ typescript: 5.2.2
+ dev: false
/ts-dedent@2.2.0:
resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
@@ -12674,10 +13019,20 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
+ /typescript@5.2.2:
+ resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+ dev: false
+
/ufo@1.1.1:
resolution: {integrity: sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==}
dev: true
+ /ufo@1.3.1:
+ resolution: {integrity: sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==}
+ dev: true
+
/uint8arrays@3.1.1:
resolution: {integrity: sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==}
dependencies:
@@ -13045,10 +13400,11 @@ packages:
mlly: 1.1.1
pathe: 1.1.0
picocolors: 1.0.0
- vite: 4.1.4(@types/node@18.16.18)
+ vite: 4.5.0(@types/node@18.16.18)
transitivePeerDependencies:
- '@types/node'
- less
+ - lightningcss
- sass
- stylus
- sugarss
@@ -13056,13 +13412,36 @@ packages:
- terser
dev: true
- /vite@4.1.4(@types/node@18.16.18):
- resolution: {integrity: sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==}
+ /vite-node@0.34.6(@types/node@18.16.18):
+ resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
+ engines: {node: '>=v14.18.0'}
+ hasBin: true
+ dependencies:
+ cac: 6.7.14
+ debug: 4.3.4(supports-color@8.1.1)
+ mlly: 1.4.2
+ pathe: 1.1.1
+ picocolors: 1.0.0
+ vite: 4.5.0(@types/node@18.16.18)
+ transitivePeerDependencies:
+ - '@types/node'
+ - less
+ - lightningcss
+ - sass
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ dev: true
+
+ /vite@4.5.0(@types/node@18.16.18):
+ resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
'@types/node': '>= 14'
less: '*'
+ lightningcss: ^1.21.0
sass: '*'
stylus: '*'
sugarss: '*'
@@ -13072,6 +13451,8 @@ packages:
optional: true
less:
optional: true
+ lightningcss:
+ optional: true
sass:
optional: true
stylus:
@@ -13082,12 +13463,11 @@ packages:
optional: true
dependencies:
'@types/node': 18.16.18
- esbuild: 0.16.17
- postcss: 8.4.21
- resolve: 1.22.2
- rollup: 3.25.1
+ esbuild: 0.18.20
+ postcss: 8.4.31
+ rollup: 3.29.4
optionalDependencies:
- fsevents: 2.3.2
+ fsevents: 2.3.3
dev: true
/vitest@0.29.2:
@@ -13133,11 +13513,77 @@ packages:
tinybench: 2.4.0
tinypool: 0.3.1
tinyspy: 1.1.1
- vite: 4.1.4(@types/node@18.16.18)
+ vite: 4.5.0(@types/node@18.16.18)
vite-node: 0.29.2(@types/node@18.16.18)
why-is-node-running: 2.2.2
transitivePeerDependencies:
- less
+ - lightningcss
+ - sass
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ dev: true
+
+ /vitest@0.34.6:
+ resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==}
+ engines: {node: '>=v14.18.0'}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@vitest/browser': '*'
+ '@vitest/ui': '*'
+ happy-dom: '*'
+ jsdom: '*'
+ playwright: '*'
+ safaridriver: '*'
+ webdriverio: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+ playwright:
+ optional: true
+ safaridriver:
+ optional: true
+ webdriverio:
+ optional: true
+ dependencies:
+ '@types/chai': 4.3.9
+ '@types/chai-subset': 1.3.4
+ '@types/node': 18.16.18
+ '@vitest/expect': 0.34.6
+ '@vitest/runner': 0.34.6
+ '@vitest/snapshot': 0.34.6
+ '@vitest/spy': 0.34.6
+ '@vitest/utils': 0.34.6
+ acorn: 8.11.2
+ acorn-walk: 8.3.0
+ cac: 6.7.14
+ chai: 4.3.10
+ debug: 4.3.4(supports-color@8.1.1)
+ local-pkg: 0.4.3
+ magic-string: 0.30.5
+ pathe: 1.1.1
+ picocolors: 1.0.0
+ std-env: 3.4.3
+ strip-literal: 1.3.0
+ tinybench: 2.5.1
+ tinypool: 0.7.0
+ vite: 4.5.0(@types/node@18.16.18)
+ vite-node: 0.34.6(@types/node@18.16.18)
+ why-is-node-running: 2.2.2
+ transitivePeerDependencies:
+ - less
+ - lightningcss
- sass
- stylus
- sugarss
From 7c1ed741b227984ce8d826f3ebe9a21c09bd20b5 Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 13:50:33 -0400
Subject: [PATCH 15/44] update examples
---
examples/art-gobblers/abis/ArtGobblers.abi.ts | 947 ++++++++++++++
examples/art-gobblers/abis/ArtGobblers.json | 989 --------------
examples/art-gobblers/ponder.config.ts | 10 +-
examples/ethfs/abis/FileStore.abi.ts | 306 +++++
examples/ethfs/abis/FileStore.json | 322 -----
examples/ethfs/abis/FileStoreFrontend.abi.ts | 50 +
examples/ethfs/abis/FileStoreFrontend.json | 50 -
examples/ethfs/ponder.config.ts | 15 +-
examples/factory-llama/abis/LlamaCore.abi.ts | 1138 ++++++++++++++++
examples/factory-llama/abis/LlamaCore.json | 1160 -----------------
.../factory-llama/abis/LlamaPolicy.abi.ts | 899 +++++++++++++
examples/factory-llama/abis/LlamaPolicy.json | 911 -------------
examples/factory-llama/ponder.config.ts | 14 +-
.../friendtech/abis/FriendtechSharesV1.abi.ts | 232 ++++
.../friendtech/abis/FriendtechSharesV1.json | 238 ----
examples/friendtech/ponder.config.ts | 12 +-
.../token-erc20/abis/AdventureGold.abi.ts | 349 +++++
examples/token-erc20/abis/AdventureGold.json | 357 -----
examples/token-erc20/ponder.config.ts | 12 +-
examples/token-erc721/abis/SmolBrain.abi.ts | 597 +++++++++
examples/token-erc721/abis/SmolBrain.json | 633 ---------
examples/token-erc721/ponder.config.ts | 12 +-
.../token-reth/abis/RocketTokenRETH.abi.ts | 324 +++++
examples/token-reth/abis/RocketTokenRETH.json | 332 -----
examples/token-reth/ponder.config.ts | 12 +-
25 files changed, 4889 insertions(+), 5032 deletions(-)
create mode 100644 examples/art-gobblers/abis/ArtGobblers.abi.ts
delete mode 100644 examples/art-gobblers/abis/ArtGobblers.json
create mode 100644 examples/ethfs/abis/FileStore.abi.ts
delete mode 100644 examples/ethfs/abis/FileStore.json
create mode 100644 examples/ethfs/abis/FileStoreFrontend.abi.ts
delete mode 100644 examples/ethfs/abis/FileStoreFrontend.json
create mode 100644 examples/factory-llama/abis/LlamaCore.abi.ts
delete mode 100644 examples/factory-llama/abis/LlamaCore.json
create mode 100644 examples/factory-llama/abis/LlamaPolicy.abi.ts
delete mode 100644 examples/factory-llama/abis/LlamaPolicy.json
create mode 100644 examples/friendtech/abis/FriendtechSharesV1.abi.ts
delete mode 100644 examples/friendtech/abis/FriendtechSharesV1.json
create mode 100644 examples/token-erc20/abis/AdventureGold.abi.ts
delete mode 100644 examples/token-erc20/abis/AdventureGold.json
create mode 100644 examples/token-erc721/abis/SmolBrain.abi.ts
delete mode 100644 examples/token-erc721/abis/SmolBrain.json
create mode 100644 examples/token-reth/abis/RocketTokenRETH.abi.ts
delete mode 100644 examples/token-reth/abis/RocketTokenRETH.json
diff --git a/examples/art-gobblers/abis/ArtGobblers.abi.ts b/examples/art-gobblers/abis/ArtGobblers.abi.ts
new file mode 100644
index 000000000..e790b12dd
--- /dev/null
+++ b/examples/art-gobblers/abis/ArtGobblers.abi.ts
@@ -0,0 +1,947 @@
+export const ArtGobblersAbi = [
+ {
+ inputs: [
+ { internalType: "bytes32", name: "_merkleRoot", type: "bytes32" },
+ { internalType: "uint256", name: "_mintStart", type: "uint256" },
+ { internalType: "contract Goo", name: "_goo", type: "address" },
+ { internalType: "contract Pages", name: "_pages", type: "address" },
+ { internalType: "address", name: "_team", type: "address" },
+ { internalType: "address", name: "_community", type: "address" },
+ {
+ internalType: "contract RandProvider",
+ name: "_randProvider",
+ type: "address",
+ },
+ { internalType: "string", name: "_baseUri", type: "string" },
+ { internalType: "string", name: "_unrevealedUri", type: "string" },
+ {
+ internalType: "bytes32",
+ name: "_provenanceHash",
+ type: "bytes32",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "constructor",
+ },
+ { inputs: [], name: "AlreadyClaimed", type: "error" },
+ { inputs: [], name: "Cannibalism", type: "error" },
+ {
+ inputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ name: "CannotBurnLegendary",
+ type: "error",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "cost", type: "uint256" }],
+ name: "InsufficientGobblerAmount",
+ type: "error",
+ },
+ { inputs: [], name: "InvalidProof", type: "error" },
+ {
+ inputs: [
+ { internalType: "uint256", name: "gobblersLeft", type: "uint256" },
+ ],
+ name: "LegendaryAuctionNotStarted",
+ type: "error",
+ },
+ { inputs: [], name: "MintStartPending", type: "error" },
+ { inputs: [], name: "NoRemainingLegendaryGobblers", type: "error" },
+ {
+ inputs: [
+ {
+ internalType: "uint256",
+ name: "totalRemainingToBeRevealed",
+ type: "uint256",
+ },
+ ],
+ name: "NotEnoughRemainingToBeRevealed",
+ type: "error",
+ },
+ { inputs: [], name: "NotRandProvider", type: "error" },
+ {
+ inputs: [{ internalType: "address", name: "owner", type: "address" }],
+ name: "OwnerMismatch",
+ type: "error",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "currentPrice", type: "uint256" },
+ ],
+ name: "PriceExceededMax",
+ type: "error",
+ },
+ { inputs: [], name: "RequestTooEarly", type: "error" },
+ { inputs: [], name: "ReserveImbalance", type: "error" },
+ { inputs: [], name: "RevealsPending", type: "error" },
+ { inputs: [], name: "SeedPending", type: "error" },
+ {
+ inputs: [{ internalType: "address", name: "caller", type: "address" }],
+ name: "UnauthorizedCaller",
+ type: "error",
+ },
+ { inputs: [], name: "ZeroToBeRevealed", type: "error" },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "spender",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ ],
+ name: "Approval",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "operator",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bool",
+ name: "approved",
+ type: "bool",
+ },
+ ],
+ name: "ApprovalForAll",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "gobblerId",
+ type: "uint256",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "nft",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ ],
+ name: "ArtGobbled",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "gobblerId",
+ type: "uint256",
+ },
+ ],
+ name: "GobblerClaimed",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "gobblerId",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "price",
+ type: "uint256",
+ },
+ ],
+ name: "GobblerPurchased",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "numGobblers",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "lastRevealedId",
+ type: "uint256",
+ },
+ ],
+ name: "GobblersRevealed",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "newGooBalance",
+ type: "uint256",
+ },
+ ],
+ name: "GooBalanceUpdated",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "gobblerId",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256[]",
+ name: "burnedGobblerIds",
+ type: "uint256[]",
+ },
+ ],
+ name: "LegendaryGobblerMinted",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "newOwner",
+ type: "address",
+ },
+ ],
+ name: "OwnershipTransferred",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "contract RandProvider",
+ name: "newRandProvider",
+ type: "address",
+ },
+ ],
+ name: "RandProviderUpgraded",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "randomness",
+ type: "uint256",
+ },
+ ],
+ name: "RandomnessFulfilled",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "toBeRevealed",
+ type: "uint256",
+ },
+ ],
+ name: "RandomnessRequested",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "user",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "lastMintedGobblerId",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "numGobblersEach",
+ type: "uint256",
+ },
+ ],
+ name: "ReservedGobblersMinted",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "from",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "to",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ ],
+ name: "Transfer",
+ type: "event",
+ },
+ {
+ inputs: [],
+ name: "BASE_URI",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "FIRST_LEGENDARY_GOBBLER_ID",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "LEGENDARY_AUCTION_INTERVAL",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "LEGENDARY_GOBBLER_INITIAL_START_PRICE",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "LEGENDARY_SUPPLY",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "MAX_MINTABLE",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "MAX_SUPPLY",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "MINTLIST_SUPPLY",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "PROVENANCE_HASH",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "RESERVED_SUPPLY",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "UNREVEALED_URI",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "bytes32", name: "", type: "bytes32" },
+ { internalType: "uint256", name: "randomness", type: "uint256" },
+ ],
+ name: "acceptRandomSeed",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "gooAmount", type: "uint256" }],
+ name: "addGoo",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "spender", type: "address" },
+ { internalType: "uint256", name: "id", type: "uint256" },
+ ],
+ name: "approve",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "owner", type: "address" }],
+ name: "balanceOf",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "user", type: "address" },
+ { internalType: "uint256", name: "gooAmount", type: "uint256" },
+ ],
+ name: "burnGooForPages",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "bytes32[]", name: "proof", type: "bytes32[]" }],
+ name: "claimGobbler",
+ outputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "community",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "currentNonLegendaryId",
+ outputs: [{ internalType: "uint128", name: "", type: "uint128" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ name: "getApproved",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "", type: "uint256" },
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "uint256", name: "", type: "uint256" },
+ ],
+ name: "getCopiesOfArtGobbledByGobbler",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ name: "getGobblerData",
+ outputs: [
+ { internalType: "address", name: "owner", type: "address" },
+ { internalType: "uint64", name: "idx", type: "uint64" },
+ { internalType: "uint32", name: "emissionMultiple", type: "uint32" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ name: "getGobblerEmissionMultiple",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "int256", name: "sold", type: "int256" }],
+ name: "getTargetSaleTime",
+ outputs: [{ internalType: "int256", name: "", type: "int256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "", type: "address" }],
+ name: "getUserData",
+ outputs: [
+ { internalType: "uint32", name: "gobblersOwned", type: "uint32" },
+ {
+ internalType: "uint32",
+ name: "emissionMultiple",
+ type: "uint32",
+ },
+ { internalType: "uint128", name: "lastBalance", type: "uint128" },
+ { internalType: "uint64", name: "lastTimestamp", type: "uint64" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "user", type: "address" }],
+ name: "getUserEmissionMultiple",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "int256", name: "timeSinceStart", type: "int256" },
+ { internalType: "uint256", name: "sold", type: "uint256" },
+ ],
+ name: "getVRGDAPrice",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "gobblerId", type: "uint256" },
+ { internalType: "address", name: "nft", type: "address" },
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "bool", name: "isERC1155", type: "bool" },
+ ],
+ name: "gobble",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "gobblerPrice",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "gobblerRevealsData",
+ outputs: [
+ { internalType: "uint64", name: "randomSeed", type: "uint64" },
+ {
+ internalType: "uint64",
+ name: "nextRevealTimestamp",
+ type: "uint64",
+ },
+ { internalType: "uint64", name: "lastRevealedId", type: "uint64" },
+ { internalType: "uint56", name: "toBeRevealed", type: "uint56" },
+ { internalType: "bool", name: "waitingForSeed", type: "bool" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "goo",
+ outputs: [{ internalType: "contract Goo", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "user", type: "address" }],
+ name: "gooBalance",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "", type: "address" }],
+ name: "hasClaimedMintlistGobbler",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "address", name: "", type: "address" },
+ ],
+ name: "isApprovedForAll",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "legendaryGobblerAuctionData",
+ outputs: [
+ { internalType: "uint128", name: "startPrice", type: "uint128" },
+ { internalType: "uint128", name: "numSold", type: "uint128" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "legendaryGobblerPrice",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "merkleRoot",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "maxPrice", type: "uint256" },
+ { internalType: "bool", name: "useVirtualBalance", type: "bool" },
+ ],
+ name: "mintFromGoo",
+ outputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256[]", name: "gobblerIds", type: "uint256[]" },
+ ],
+ name: "mintLegendaryGobbler",
+ outputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "uint256",
+ name: "numGobblersEach",
+ type: "uint256",
+ },
+ ],
+ name: "mintReservedGobblers",
+ outputs: [
+ {
+ internalType: "uint256",
+ name: "lastMintedGobblerId",
+ type: "uint256",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "mintStart",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "name",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "numMintedForReserves",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "numMintedFromGoo",
+ outputs: [{ internalType: "uint128", name: "", type: "uint128" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "uint256[]", name: "", type: "uint256[]" },
+ { internalType: "uint256[]", name: "", type: "uint256[]" },
+ { internalType: "bytes", name: "", type: "bytes" },
+ ],
+ name: "onERC1155BatchReceived",
+ outputs: [{ internalType: "bytes4", name: "", type: "bytes4" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "uint256", name: "", type: "uint256" },
+ { internalType: "uint256", name: "", type: "uint256" },
+ { internalType: "bytes", name: "", type: "bytes" },
+ ],
+ name: "onERC1155Received",
+ outputs: [{ internalType: "bytes4", name: "", type: "bytes4" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "owner",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "id", type: "uint256" }],
+ name: "ownerOf",
+ outputs: [{ internalType: "address", name: "owner", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "pages",
+ outputs: [{ internalType: "contract Pages", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "randProvider",
+ outputs: [
+ { internalType: "contract RandProvider", name: "", type: "address" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "gooAmount", type: "uint256" }],
+ name: "removeGoo",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "requestRandomSeed",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "numGobblers", type: "uint256" }],
+ name: "revealGobblers",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "id", type: "uint256" },
+ ],
+ name: "safeTransferFrom",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ ],
+ name: "safeTransferFrom",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "operator", type: "address" },
+ { internalType: "bool", name: "approved", type: "bool" },
+ ],
+ name: "setApprovalForAll",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "bytes4", name: "interfaceId", type: "bytes4" }],
+ name: "supportsInterface",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "pure",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "symbol",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "targetPrice",
+ outputs: [{ internalType: "int256", name: "", type: "int256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "team",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "gobblerId", type: "uint256" }],
+ name: "tokenURI",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "id", type: "uint256" },
+ ],
+ name: "transferFrom",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "newOwner", type: "address" }],
+ name: "transferOwnership",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract RandProvider",
+ name: "newRandProvider",
+ type: "address",
+ },
+ ],
+ name: "upgradeRandProvider",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+] as const;
diff --git a/examples/art-gobblers/abis/ArtGobblers.json b/examples/art-gobblers/abis/ArtGobblers.json
deleted file mode 100644
index ce916cce2..000000000
--- a/examples/art-gobblers/abis/ArtGobblers.json
+++ /dev/null
@@ -1,989 +0,0 @@
-[
- {
- "inputs": [
- { "internalType": "bytes32", "name": "_merkleRoot", "type": "bytes32" },
- { "internalType": "uint256", "name": "_mintStart", "type": "uint256" },
- { "internalType": "contract Goo", "name": "_goo", "type": "address" },
- { "internalType": "contract Pages", "name": "_pages", "type": "address" },
- { "internalType": "address", "name": "_team", "type": "address" },
- { "internalType": "address", "name": "_community", "type": "address" },
- {
- "internalType": "contract RandProvider",
- "name": "_randProvider",
- "type": "address"
- },
- { "internalType": "string", "name": "_baseUri", "type": "string" },
- { "internalType": "string", "name": "_unrevealedUri", "type": "string" },
- {
- "internalType": "bytes32",
- "name": "_provenanceHash",
- "type": "bytes32"
- }
- ],
- "stateMutability": "nonpayable",
- "type": "constructor"
- },
- { "inputs": [], "name": "AlreadyClaimed", "type": "error" },
- { "inputs": [], "name": "Cannibalism", "type": "error" },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "name": "CannotBurnLegendary",
- "type": "error"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "cost", "type": "uint256" }
- ],
- "name": "InsufficientGobblerAmount",
- "type": "error"
- },
- { "inputs": [], "name": "InvalidProof", "type": "error" },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gobblersLeft", "type": "uint256" }
- ],
- "name": "LegendaryAuctionNotStarted",
- "type": "error"
- },
- { "inputs": [], "name": "MintStartPending", "type": "error" },
- { "inputs": [], "name": "NoRemainingLegendaryGobblers", "type": "error" },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "totalRemainingToBeRevealed",
- "type": "uint256"
- }
- ],
- "name": "NotEnoughRemainingToBeRevealed",
- "type": "error"
- },
- { "inputs": [], "name": "NotRandProvider", "type": "error" },
- {
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" }
- ],
- "name": "OwnerMismatch",
- "type": "error"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "currentPrice", "type": "uint256" }
- ],
- "name": "PriceExceededMax",
- "type": "error"
- },
- { "inputs": [], "name": "RequestTooEarly", "type": "error" },
- { "inputs": [], "name": "ReserveImbalance", "type": "error" },
- { "inputs": [], "name": "RevealsPending", "type": "error" },
- { "inputs": [], "name": "SeedPending", "type": "error" },
- {
- "inputs": [
- { "internalType": "address", "name": "caller", "type": "address" }
- ],
- "name": "UnauthorizedCaller",
- "type": "error"
- },
- { "inputs": [], "name": "ZeroToBeRevealed", "type": "error" },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "spender",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "operator",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "approved",
- "type": "bool"
- }
- ],
- "name": "ApprovalForAll",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "gobblerId",
- "type": "uint256"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "nft",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- }
- ],
- "name": "ArtGobbled",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "gobblerId",
- "type": "uint256"
- }
- ],
- "name": "GobblerClaimed",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "gobblerId",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "price",
- "type": "uint256"
- }
- ],
- "name": "GobblerPurchased",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "numGobblers",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "lastRevealedId",
- "type": "uint256"
- }
- ],
- "name": "GobblersRevealed",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "newGooBalance",
- "type": "uint256"
- }
- ],
- "name": "GooBalanceUpdated",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "gobblerId",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256[]",
- "name": "burnedGobblerIds",
- "type": "uint256[]"
- }
- ],
- "name": "LegendaryGobblerMinted",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "OwnershipTransferred",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "contract RandProvider",
- "name": "newRandProvider",
- "type": "address"
- }
- ],
- "name": "RandProviderUpgraded",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "randomness",
- "type": "uint256"
- }
- ],
- "name": "RandomnessFulfilled",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "toBeRevealed",
- "type": "uint256"
- }
- ],
- "name": "RandomnessRequested",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "user",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "lastMintedGobblerId",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "numGobblersEach",
- "type": "uint256"
- }
- ],
- "name": "ReservedGobblersMinted",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "inputs": [],
- "name": "BASE_URI",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "FIRST_LEGENDARY_GOBBLER_ID",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "LEGENDARY_AUCTION_INTERVAL",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "LEGENDARY_GOBBLER_INITIAL_START_PRICE",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "LEGENDARY_SUPPLY",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "MAX_MINTABLE",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "MAX_SUPPLY",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "MINTLIST_SUPPLY",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "PROVENANCE_HASH",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "RESERVED_SUPPLY",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "UNREVEALED_URI",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes32", "name": "", "type": "bytes32" },
- { "internalType": "uint256", "name": "randomness", "type": "uint256" }
- ],
- "name": "acceptRandomSeed",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gooAmount", "type": "uint256" }
- ],
- "name": "addGoo",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "spender", "type": "address" },
- { "internalType": "uint256", "name": "id", "type": "uint256" }
- ],
- "name": "approve",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" }
- ],
- "name": "balanceOf",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "user", "type": "address" },
- { "internalType": "uint256", "name": "gooAmount", "type": "uint256" }
- ],
- "name": "burnGooForPages",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes32[]", "name": "proof", "type": "bytes32[]" }
- ],
- "name": "claimGobbler",
- "outputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "community",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "currentNonLegendaryId",
- "outputs": [{ "internalType": "uint128", "name": "", "type": "uint128" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "name": "getApproved",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "", "type": "uint256" },
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "uint256", "name": "", "type": "uint256" }
- ],
- "name": "getCopiesOfArtGobbledByGobbler",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "name": "getGobblerData",
- "outputs": [
- { "internalType": "address", "name": "owner", "type": "address" },
- { "internalType": "uint64", "name": "idx", "type": "uint64" },
- { "internalType": "uint32", "name": "emissionMultiple", "type": "uint32" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "name": "getGobblerEmissionMultiple",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "int256", "name": "sold", "type": "int256" }],
- "name": "getTargetSaleTime",
- "outputs": [{ "internalType": "int256", "name": "", "type": "int256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "name": "getUserData",
- "outputs": [
- { "internalType": "uint32", "name": "gobblersOwned", "type": "uint32" },
- {
- "internalType": "uint32",
- "name": "emissionMultiple",
- "type": "uint32"
- },
- { "internalType": "uint128", "name": "lastBalance", "type": "uint128" },
- { "internalType": "uint64", "name": "lastTimestamp", "type": "uint64" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "user", "type": "address" }
- ],
- "name": "getUserEmissionMultiple",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "int256", "name": "timeSinceStart", "type": "int256" },
- { "internalType": "uint256", "name": "sold", "type": "uint256" }
- ],
- "name": "getVRGDAPrice",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" },
- { "internalType": "address", "name": "nft", "type": "address" },
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "bool", "name": "isERC1155", "type": "bool" }
- ],
- "name": "gobble",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "gobblerPrice",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "gobblerRevealsData",
- "outputs": [
- { "internalType": "uint64", "name": "randomSeed", "type": "uint64" },
- {
- "internalType": "uint64",
- "name": "nextRevealTimestamp",
- "type": "uint64"
- },
- { "internalType": "uint64", "name": "lastRevealedId", "type": "uint64" },
- { "internalType": "uint56", "name": "toBeRevealed", "type": "uint56" },
- { "internalType": "bool", "name": "waitingForSeed", "type": "bool" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "goo",
- "outputs": [
- { "internalType": "contract Goo", "name": "", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "user", "type": "address" }
- ],
- "name": "gooBalance",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "name": "hasClaimedMintlistGobbler",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "address", "name": "", "type": "address" }
- ],
- "name": "isApprovedForAll",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "legendaryGobblerAuctionData",
- "outputs": [
- { "internalType": "uint128", "name": "startPrice", "type": "uint128" },
- { "internalType": "uint128", "name": "numSold", "type": "uint128" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "legendaryGobblerPrice",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "merkleRoot",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "maxPrice", "type": "uint256" },
- { "internalType": "bool", "name": "useVirtualBalance", "type": "bool" }
- ],
- "name": "mintFromGoo",
- "outputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256[]", "name": "gobblerIds", "type": "uint256[]" }
- ],
- "name": "mintLegendaryGobbler",
- "outputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "numGobblersEach",
- "type": "uint256"
- }
- ],
- "name": "mintReservedGobblers",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "lastMintedGobblerId",
- "type": "uint256"
- }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "mintStart",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "name",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "numMintedForReserves",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "numMintedFromGoo",
- "outputs": [{ "internalType": "uint128", "name": "", "type": "uint128" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "uint256[]", "name": "", "type": "uint256[]" },
- { "internalType": "uint256[]", "name": "", "type": "uint256[]" },
- { "internalType": "bytes", "name": "", "type": "bytes" }
- ],
- "name": "onERC1155BatchReceived",
- "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "uint256", "name": "", "type": "uint256" },
- { "internalType": "uint256", "name": "", "type": "uint256" },
- { "internalType": "bytes", "name": "", "type": "bytes" }
- ],
- "name": "onERC1155Received",
- "outputs": [{ "internalType": "bytes4", "name": "", "type": "bytes4" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "owner",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "id", "type": "uint256" }],
- "name": "ownerOf",
- "outputs": [
- { "internalType": "address", "name": "owner", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "pages",
- "outputs": [
- { "internalType": "contract Pages", "name": "", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "randProvider",
- "outputs": [
- { "internalType": "contract RandProvider", "name": "", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gooAmount", "type": "uint256" }
- ],
- "name": "removeGoo",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "requestRandomSeed",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "numGobblers", "type": "uint256" }
- ],
- "name": "revealGobblers",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "id", "type": "uint256" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "operator", "type": "address" },
- { "internalType": "bool", "name": "approved", "type": "bool" }
- ],
- "name": "setApprovalForAll",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }
- ],
- "name": "supportsInterface",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "pure",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "symbol",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "targetPrice",
- "outputs": [{ "internalType": "int256", "name": "", "type": "int256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "team",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "gobblerId", "type": "uint256" }
- ],
- "name": "tokenURI",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "id", "type": "uint256" }
- ],
- "name": "transferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "newOwner", "type": "address" }
- ],
- "name": "transferOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract RandProvider",
- "name": "newRandProvider",
- "type": "address"
- }
- ],
- "name": "upgradeRandProvider",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- }
-]
diff --git a/examples/art-gobblers/ponder.config.ts b/examples/art-gobblers/ponder.config.ts
index 2da8595ce..d5fd61dbf 100644
--- a/examples/art-gobblers/ponder.config.ts
+++ b/examples/art-gobblers/ponder.config.ts
@@ -1,9 +1,9 @@
-import type { Config } from "@ponder/core";
+import { createConfig } from "@ponder/core";
import { http } from "viem";
-import ArtGobblersAbi from "./abis/ArtGobblers.json";
+import { ArtGobblersAbi } from "./ArtGobblers.abi";
-export const config: Config = {
+export const config = createConfig({
networks: [
{
name: "mainnet",
@@ -14,10 +14,10 @@ export const config: Config = {
contracts: [
{
name: "ArtGobblers",
- network: "mainnet",
+ network: [{ name: "mainnet" }],
abi: ArtGobblersAbi,
address: "0x60bb1e2aa1c9acafb4d34f71585d7e959f387769",
startBlock: 15863321,
},
],
-};
+});
diff --git a/examples/ethfs/abis/FileStore.abi.ts b/examples/ethfs/abis/FileStore.abi.ts
new file mode 100644
index 000000000..8863a59cc
--- /dev/null
+++ b/examples/ethfs/abis/FileStore.abi.ts
@@ -0,0 +1,306 @@
+export const FileStoreAbi = [
+ {
+ inputs: [
+ {
+ internalType: "contract IContentStore",
+ name: "_contentStore",
+ type: "address",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "constructor",
+ },
+ { inputs: [], name: "EmptyFile", type: "error" },
+ {
+ inputs: [{ internalType: "string", name: "filename", type: "string" }],
+ name: "FileNotFound",
+ type: "error",
+ },
+ {
+ inputs: [{ internalType: "string", name: "filename", type: "string" }],
+ name: "FilenameExists",
+ type: "error",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "string",
+ name: "indexedFilename",
+ type: "string",
+ },
+ {
+ indexed: true,
+ internalType: "bytes32",
+ name: "checksum",
+ type: "bytes32",
+ },
+ {
+ indexed: false,
+ internalType: "string",
+ name: "filename",
+ type: "string",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "size",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "bytes",
+ name: "metadata",
+ type: "bytes",
+ },
+ ],
+ name: "FileCreated",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "string",
+ name: "indexedFilename",
+ type: "string",
+ },
+ {
+ indexed: true,
+ internalType: "bytes32",
+ name: "checksum",
+ type: "bytes32",
+ },
+ {
+ indexed: false,
+ internalType: "string",
+ name: "filename",
+ type: "string",
+ },
+ ],
+ name: "FileDeleted",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "previousOwner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "newOwner",
+ type: "address",
+ },
+ ],
+ name: "OwnershipTransferStarted",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "previousOwner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "newOwner",
+ type: "address",
+ },
+ ],
+ name: "OwnershipTransferred",
+ type: "event",
+ },
+ {
+ inputs: [],
+ name: "acceptOwnership",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "contentStore",
+ outputs: [
+ {
+ internalType: "contract IContentStore",
+ name: "",
+ type: "address",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "string", name: "filename", type: "string" },
+ { internalType: "bytes32[]", name: "checksums", type: "bytes32[]" },
+ ],
+ name: "createFile",
+ outputs: [
+ {
+ components: [
+ { internalType: "uint256", name: "size", type: "uint256" },
+ {
+ components: [
+ {
+ internalType: "bytes32",
+ name: "checksum",
+ type: "bytes32",
+ },
+ {
+ internalType: "address",
+ name: "pointer",
+ type: "address",
+ },
+ ],
+ internalType: "struct Content[]",
+ name: "contents",
+ type: "tuple[]",
+ },
+ ],
+ internalType: "struct File",
+ name: "file",
+ type: "tuple",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "string", name: "filename", type: "string" },
+ { internalType: "bytes32[]", name: "checksums", type: "bytes32[]" },
+ { internalType: "bytes", name: "extraData", type: "bytes" },
+ ],
+ name: "createFile",
+ outputs: [
+ {
+ components: [
+ { internalType: "uint256", name: "size", type: "uint256" },
+ {
+ components: [
+ {
+ internalType: "bytes32",
+ name: "checksum",
+ type: "bytes32",
+ },
+ {
+ internalType: "address",
+ name: "pointer",
+ type: "address",
+ },
+ ],
+ internalType: "struct Content[]",
+ name: "contents",
+ type: "tuple[]",
+ },
+ ],
+ internalType: "struct File",
+ name: "file",
+ type: "tuple",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "string", name: "filename", type: "string" }],
+ name: "deleteFile",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "string", name: "filename", type: "string" }],
+ name: "fileExists",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "string", name: "", type: "string" }],
+ name: "files",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "string", name: "filename", type: "string" }],
+ name: "getChecksum",
+ outputs: [{ internalType: "bytes32", name: "checksum", type: "bytes32" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "string", name: "filename", type: "string" }],
+ name: "getFile",
+ outputs: [
+ {
+ components: [
+ { internalType: "uint256", name: "size", type: "uint256" },
+ {
+ components: [
+ {
+ internalType: "bytes32",
+ name: "checksum",
+ type: "bytes32",
+ },
+ {
+ internalType: "address",
+ name: "pointer",
+ type: "address",
+ },
+ ],
+ internalType: "struct Content[]",
+ name: "contents",
+ type: "tuple[]",
+ },
+ ],
+ internalType: "struct File",
+ name: "file",
+ type: "tuple",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "owner",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "pendingOwner",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "renounceOwnership",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "newOwner", type: "address" }],
+ name: "transferOwnership",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+] as const;
diff --git a/examples/ethfs/abis/FileStore.json b/examples/ethfs/abis/FileStore.json
deleted file mode 100644
index 9cd326622..000000000
--- a/examples/ethfs/abis/FileStore.json
+++ /dev/null
@@ -1,322 +0,0 @@
-[
- {
- "inputs": [
- {
- "internalType": "contract IContentStore",
- "name": "_contentStore",
- "type": "address"
- }
- ],
- "stateMutability": "nonpayable",
- "type": "constructor"
- },
- { "inputs": [], "name": "EmptyFile", "type": "error" },
- {
- "inputs": [
- { "internalType": "string", "name": "filename", "type": "string" }
- ],
- "name": "FileNotFound",
- "type": "error"
- },
- {
- "inputs": [
- { "internalType": "string", "name": "filename", "type": "string" }
- ],
- "name": "FilenameExists",
- "type": "error"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "string",
- "name": "indexedFilename",
- "type": "string"
- },
- {
- "indexed": true,
- "internalType": "bytes32",
- "name": "checksum",
- "type": "bytes32"
- },
- {
- "indexed": false,
- "internalType": "string",
- "name": "filename",
- "type": "string"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "size",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "bytes",
- "name": "metadata",
- "type": "bytes"
- }
- ],
- "name": "FileCreated",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "string",
- "name": "indexedFilename",
- "type": "string"
- },
- {
- "indexed": true,
- "internalType": "bytes32",
- "name": "checksum",
- "type": "bytes32"
- },
- {
- "indexed": false,
- "internalType": "string",
- "name": "filename",
- "type": "string"
- }
- ],
- "name": "FileDeleted",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "previousOwner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "OwnershipTransferStarted",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "previousOwner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "OwnershipTransferred",
- "type": "event"
- },
- {
- "inputs": [],
- "name": "acceptOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "contentStore",
- "outputs": [
- {
- "internalType": "contract IContentStore",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "string", "name": "filename", "type": "string" },
- { "internalType": "bytes32[]", "name": "checksums", "type": "bytes32[]" }
- ],
- "name": "createFile",
- "outputs": [
- {
- "components": [
- { "internalType": "uint256", "name": "size", "type": "uint256" },
- {
- "components": [
- {
- "internalType": "bytes32",
- "name": "checksum",
- "type": "bytes32"
- },
- {
- "internalType": "address",
- "name": "pointer",
- "type": "address"
- }
- ],
- "internalType": "struct Content[]",
- "name": "contents",
- "type": "tuple[]"
- }
- ],
- "internalType": "struct File",
- "name": "file",
- "type": "tuple"
- }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "string", "name": "filename", "type": "string" },
- { "internalType": "bytes32[]", "name": "checksums", "type": "bytes32[]" },
- { "internalType": "bytes", "name": "extraData", "type": "bytes" }
- ],
- "name": "createFile",
- "outputs": [
- {
- "components": [
- { "internalType": "uint256", "name": "size", "type": "uint256" },
- {
- "components": [
- {
- "internalType": "bytes32",
- "name": "checksum",
- "type": "bytes32"
- },
- {
- "internalType": "address",
- "name": "pointer",
- "type": "address"
- }
- ],
- "internalType": "struct Content[]",
- "name": "contents",
- "type": "tuple[]"
- }
- ],
- "internalType": "struct File",
- "name": "file",
- "type": "tuple"
- }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "string", "name": "filename", "type": "string" }
- ],
- "name": "deleteFile",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "string", "name": "filename", "type": "string" }
- ],
- "name": "fileExists",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "name": "files",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "string", "name": "filename", "type": "string" }
- ],
- "name": "getChecksum",
- "outputs": [
- { "internalType": "bytes32", "name": "checksum", "type": "bytes32" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "string", "name": "filename", "type": "string" }
- ],
- "name": "getFile",
- "outputs": [
- {
- "components": [
- { "internalType": "uint256", "name": "size", "type": "uint256" },
- {
- "components": [
- {
- "internalType": "bytes32",
- "name": "checksum",
- "type": "bytes32"
- },
- {
- "internalType": "address",
- "name": "pointer",
- "type": "address"
- }
- ],
- "internalType": "struct Content[]",
- "name": "contents",
- "type": "tuple[]"
- }
- ],
- "internalType": "struct File",
- "name": "file",
- "type": "tuple"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "owner",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "pendingOwner",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "renounceOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "newOwner", "type": "address" }
- ],
- "name": "transferOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- }
-]
diff --git a/examples/ethfs/abis/FileStoreFrontend.abi.ts b/examples/ethfs/abis/FileStoreFrontend.abi.ts
new file mode 100644
index 000000000..9a9f300f6
--- /dev/null
+++ b/examples/ethfs/abis/FileStoreFrontend.abi.ts
@@ -0,0 +1,50 @@
+export const FileStoreFrontendAbi = [
+ {
+ inputs: [
+ {
+ internalType: "contract IContentStore",
+ name: "contentStore",
+ type: "address",
+ },
+ {
+ internalType: "bytes32",
+ name: "checksum",
+ type: "bytes32",
+ },
+ ],
+ name: "getContent",
+ outputs: [
+ {
+ internalType: "bytes",
+ name: "content",
+ type: "bytes",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract IFileStore",
+ name: "fileStore",
+ type: "address",
+ },
+ {
+ internalType: "string",
+ name: "filename",
+ type: "string",
+ },
+ ],
+ name: "readFile",
+ outputs: [
+ {
+ internalType: "string",
+ name: "contents",
+ type: "string",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+] as const;
diff --git a/examples/ethfs/abis/FileStoreFrontend.json b/examples/ethfs/abis/FileStoreFrontend.json
deleted file mode 100644
index 2da532d6c..000000000
--- a/examples/ethfs/abis/FileStoreFrontend.json
+++ /dev/null
@@ -1,50 +0,0 @@
-[
- {
- "inputs": [
- {
- "internalType": "contract IContentStore",
- "name": "contentStore",
- "type": "address"
- },
- {
- "internalType": "bytes32",
- "name": "checksum",
- "type": "bytes32"
- }
- ],
- "name": "getContent",
- "outputs": [
- {
- "internalType": "bytes",
- "name": "content",
- "type": "bytes"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract IFileStore",
- "name": "fileStore",
- "type": "address"
- },
- {
- "internalType": "string",
- "name": "filename",
- "type": "string"
- }
- ],
- "name": "readFile",
- "outputs": [
- {
- "internalType": "string",
- "name": "contents",
- "type": "string"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- }
-]
diff --git a/examples/ethfs/ponder.config.ts b/examples/ethfs/ponder.config.ts
index 9b96c3a79..9a06009d7 100644
--- a/examples/ethfs/ponder.config.ts
+++ b/examples/ethfs/ponder.config.ts
@@ -1,10 +1,10 @@
-import type { Config } from "@ponder/core";
+import { createConfig } from "@ponder/core";
import { http } from "viem";
-import FileStoreAbi from "./abis/FileStore.json";
-import FileStoreFrontendAbi from "./abis/FileStoreFrontend.json";
+import { FileStoreAbi } from "./abis/FileStore.abi";
+import { FileStoreFrontendAbi } from "./abis/FileStoreFrontend.abi";
-export const config: Config = {
+export const config = createConfig({
networks: [
{
name: "mainnet",
@@ -15,17 +15,16 @@ export const config: Config = {
contracts: [
{
name: "FileStore",
- network: "mainnet",
+ network: [{ name: "mainnet" }],
abi: FileStoreAbi,
address: "0x9746fD0A77829E12F8A9DBe70D7a322412325B91",
startBlock: 15963553,
},
{
name: "FileStoreFrontend",
- network: "mainnet",
+ network: [{ name: "mainnet" }],
address: "0xBc66C61BCF49Cc3fe4E321aeCEa307F61EC57C0b",
abi: FileStoreFrontendAbi,
- isLogEventSource: false,
},
],
-};
+});
diff --git a/examples/factory-llama/abis/LlamaCore.abi.ts b/examples/factory-llama/abis/LlamaCore.abi.ts
new file mode 100644
index 000000000..4580536fb
--- /dev/null
+++ b/examples/factory-llama/abis/LlamaCore.abi.ts
@@ -0,0 +1,1138 @@
+export const LlamaCoreAbi = [
+ { inputs: [], stateMutability: "nonpayable", type: "constructor" },
+ { inputs: [], name: "BootstrapStrategyNotAuthorized", type: "error" },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ ],
+ name: "CannotCastWithZeroQuantity",
+ type: "error",
+ },
+ {
+ inputs: [],
+ name: "CannotDisapproveAfterMinExecutionTime",
+ type: "error",
+ },
+ { inputs: [], name: "CannotSetExecutorAsTarget", type: "error" },
+ { inputs: [], name: "DuplicateCast", type: "error" },
+ {
+ inputs: [{ internalType: "bytes", name: "reason", type: "bytes" }],
+ name: "FailedActionExecution",
+ type: "error",
+ },
+ { inputs: [], name: "IncorrectMsgValue", type: "error" },
+ { inputs: [], name: "InfoHashMismatch", type: "error" },
+ {
+ inputs: [
+ { internalType: "enum ActionState", name: "current", type: "uint8" },
+ ],
+ name: "InvalidActionState",
+ type: "error",
+ },
+ { inputs: [], name: "InvalidPolicyholder", type: "error" },
+ { inputs: [], name: "InvalidSignature", type: "error" },
+ {
+ inputs: [],
+ name: "MinExecutionTimeCannotBeInThePast",
+ type: "error",
+ },
+ { inputs: [], name: "MinExecutionTimeNotReached", type: "error" },
+ { inputs: [], name: "NonExistentStrategy", type: "error" },
+ { inputs: [], name: "OnlyLlama", type: "error" },
+ {
+ inputs: [],
+ name: "PolicyholderDoesNotHavePermission",
+ type: "error",
+ },
+ { inputs: [], name: "RestrictedAddress", type: "error" },
+ { inputs: [], name: "UnauthorizedAccountLogic", type: "error" },
+ { inputs: [], name: "UnauthorizedStrategy", type: "error" },
+ { inputs: [], name: "UnauthorizedStrategyLogic", type: "error" },
+ {
+ inputs: [{ internalType: "uint256", name: "n", type: "uint256" }],
+ name: "UnsafeCast",
+ type: "error",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "contract ILlamaAccount",
+ name: "account",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "contract ILlamaAccount",
+ name: "accountLogic",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bytes",
+ name: "initializationData",
+ type: "bytes",
+ },
+ ],
+ name: "AccountCreated",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "contract ILlamaAccount",
+ name: "accountLogic",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bool",
+ name: "authorized",
+ type: "bool",
+ },
+ ],
+ name: "AccountLogicAuthorizationSet",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "caller",
+ type: "address",
+ },
+ ],
+ name: "ActionCanceled",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "creator",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint8",
+ name: "role",
+ type: "uint8",
+ },
+ {
+ indexed: true,
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "target",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "value",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "bytes",
+ name: "data",
+ type: "bytes",
+ },
+ {
+ indexed: false,
+ internalType: "string",
+ name: "description",
+ type: "string",
+ },
+ ],
+ name: "ActionCreated",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "caller",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "creator",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bytes",
+ name: "result",
+ type: "bytes",
+ },
+ ],
+ name: "ActionExecuted",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "target",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "bytes4",
+ name: "selector",
+ type: "bytes4",
+ },
+ {
+ indexed: false,
+ internalType: "contract ILlamaActionGuard",
+ name: "actionGuard",
+ type: "address",
+ },
+ ],
+ name: "ActionGuardSet",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "caller",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "creator",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "minExecutionTime",
+ type: "uint256",
+ },
+ ],
+ name: "ActionQueued",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "policyholder",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint8",
+ name: "role",
+ type: "uint8",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "quantity",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "string",
+ name: "reason",
+ type: "string",
+ },
+ ],
+ name: "ApprovalCast",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "policyholder",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint8",
+ name: "role",
+ type: "uint8",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "quantity",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "string",
+ name: "reason",
+ type: "string",
+ },
+ ],
+ name: "DisapprovalCast",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint8",
+ name: "version",
+ type: "uint8",
+ },
+ ],
+ name: "Initialized",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "script",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bool",
+ name: "authorized",
+ type: "bool",
+ },
+ ],
+ name: "ScriptAuthorizationSet",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bool",
+ name: "authorized",
+ type: "bool",
+ },
+ ],
+ name: "StrategyAuthorizationSet",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "contract ILlamaStrategy",
+ name: "strategyLogic",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bytes",
+ name: "initializationData",
+ type: "bytes",
+ },
+ ],
+ name: "StrategyCreated",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "contract ILlamaStrategy",
+ name: "strategyLogic",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bool",
+ name: "authorized",
+ type: "bool",
+ },
+ ],
+ name: "StrategyLogicAuthorizationSet",
+ type: "event",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "bytes4", name: "selector", type: "bytes4" },
+ ],
+ name: "actionGuard",
+ outputs: [
+ {
+ internalType: "contract ILlamaActionGuard",
+ name: "guard",
+ type: "address",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "actionsCount",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "actionId", type: "uint256" },
+ { internalType: "address", name: "policyholder", type: "address" },
+ ],
+ name: "approvals",
+ outputs: [{ internalType: "bool", name: "hasApproved", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract ILlamaAccount",
+ name: "accountLogic",
+ type: "address",
+ },
+ ],
+ name: "authorizedAccountLogics",
+ outputs: [{ internalType: "bool", name: "isAuthorized", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "script", type: "address" }],
+ name: "authorizedScripts",
+ outputs: [{ internalType: "bool", name: "isAuthorized", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategyLogic",
+ type: "address",
+ },
+ ],
+ name: "authorizedStrategyLogics",
+ outputs: [{ internalType: "bool", name: "isAuthorized", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ components: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "creator", type: "address" },
+ { internalType: "uint8", name: "creatorRole", type: "uint8" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "uint256", name: "value", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ ],
+ internalType: "struct ActionInfo",
+ name: "actionInfo",
+ type: "tuple",
+ },
+ ],
+ name: "cancelAction",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ {
+ components: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "creator", type: "address" },
+ { internalType: "uint8", name: "creatorRole", type: "uint8" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "uint256", name: "value", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ ],
+ internalType: "struct ActionInfo",
+ name: "actionInfo",
+ type: "tuple",
+ },
+ { internalType: "uint8", name: "v", type: "uint8" },
+ { internalType: "bytes32", name: "r", type: "bytes32" },
+ { internalType: "bytes32", name: "s", type: "bytes32" },
+ ],
+ name: "cancelActionBySig",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ components: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "creator", type: "address" },
+ { internalType: "uint8", name: "creatorRole", type: "uint8" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "uint256", name: "value", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ ],
+ internalType: "struct ActionInfo",
+ name: "actionInfo",
+ type: "tuple",
+ },
+ { internalType: "string", name: "reason", type: "string" },
+ ],
+ name: "castApproval",
+ outputs: [{ internalType: "uint96", name: "", type: "uint96" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ components: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "creator", type: "address" },
+ { internalType: "uint8", name: "creatorRole", type: "uint8" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "uint256", name: "value", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ ],
+ internalType: "struct ActionInfo",
+ name: "actionInfo",
+ type: "tuple",
+ },
+ { internalType: "string", name: "reason", type: "string" },
+ { internalType: "uint8", name: "v", type: "uint8" },
+ { internalType: "bytes32", name: "r", type: "bytes32" },
+ { internalType: "bytes32", name: "s", type: "bytes32" },
+ ],
+ name: "castApprovalBySig",
+ outputs: [{ internalType: "uint96", name: "", type: "uint96" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ components: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "creator", type: "address" },
+ { internalType: "uint8", name: "creatorRole", type: "uint8" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "uint256", name: "value", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ ],
+ internalType: "struct ActionInfo",
+ name: "actionInfo",
+ type: "tuple",
+ },
+ { internalType: "string", name: "reason", type: "string" },
+ ],
+ name: "castDisapproval",
+ outputs: [{ internalType: "uint96", name: "", type: "uint96" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ components: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "creator", type: "address" },
+ { internalType: "uint8", name: "creatorRole", type: "uint8" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "uint256", name: "value", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ ],
+ internalType: "struct ActionInfo",
+ name: "actionInfo",
+ type: "tuple",
+ },
+ { internalType: "string", name: "reason", type: "string" },
+ { internalType: "uint8", name: "v", type: "uint8" },
+ { internalType: "bytes32", name: "r", type: "bytes32" },
+ { internalType: "bytes32", name: "s", type: "bytes32" },
+ ],
+ name: "castDisapprovalBySig",
+ outputs: [{ internalType: "uint96", name: "", type: "uint96" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract ILlamaAccount",
+ name: "llamaAccountLogic",
+ type: "address",
+ },
+ { internalType: "bytes[]", name: "accountConfigs", type: "bytes[]" },
+ ],
+ name: "createAccounts",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "uint256", name: "value", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ { internalType: "string", name: "description", type: "string" },
+ ],
+ name: "createAction",
+ outputs: [{ internalType: "uint256", name: "actionId", type: "uint256" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "uint256", name: "value", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ { internalType: "string", name: "description", type: "string" },
+ { internalType: "uint8", name: "v", type: "uint8" },
+ { internalType: "bytes32", name: "r", type: "bytes32" },
+ { internalType: "bytes32", name: "s", type: "bytes32" },
+ ],
+ name: "createActionBySig",
+ outputs: [{ internalType: "uint256", name: "actionId", type: "uint256" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "llamaStrategyLogic",
+ type: "address",
+ },
+ {
+ internalType: "bytes[]",
+ name: "strategyConfigs",
+ type: "bytes[]",
+ },
+ ],
+ name: "createStrategies",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "actionId", type: "uint256" },
+ { internalType: "address", name: "policyholder", type: "address" },
+ ],
+ name: "disapprovals",
+ outputs: [{ internalType: "bool", name: "hasDisapproved", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ components: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "creator", type: "address" },
+ { internalType: "uint8", name: "creatorRole", type: "uint8" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "uint256", name: "value", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ ],
+ internalType: "struct ActionInfo",
+ name: "actionInfo",
+ type: "tuple",
+ },
+ ],
+ name: "executeAction",
+ outputs: [],
+ stateMutability: "payable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "executor",
+ outputs: [
+ {
+ internalType: "contract LlamaExecutor",
+ name: "",
+ type: "address",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "actionId", type: "uint256" }],
+ name: "getAction",
+ outputs: [
+ {
+ components: [
+ { internalType: "bytes32", name: "infoHash", type: "bytes32" },
+ { internalType: "bool", name: "executed", type: "bool" },
+ { internalType: "bool", name: "canceled", type: "bool" },
+ { internalType: "bool", name: "isScript", type: "bool" },
+ {
+ internalType: "contract ILlamaActionGuard",
+ name: "guard",
+ type: "address",
+ },
+ {
+ internalType: "uint64",
+ name: "creationTime",
+ type: "uint64",
+ },
+ {
+ internalType: "uint64",
+ name: "minExecutionTime",
+ type: "uint64",
+ },
+ {
+ internalType: "uint96",
+ name: "totalApprovals",
+ type: "uint96",
+ },
+ {
+ internalType: "uint96",
+ name: "totalDisapprovals",
+ type: "uint96",
+ },
+ ],
+ internalType: "struct Action",
+ name: "",
+ type: "tuple",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ components: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "creator", type: "address" },
+ { internalType: "uint8", name: "creatorRole", type: "uint8" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "uint256", name: "value", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ ],
+ internalType: "struct ActionInfo",
+ name: "actionInfo",
+ type: "tuple",
+ },
+ ],
+ name: "getActionState",
+ outputs: [{ internalType: "enum ActionState", name: "", type: "uint8" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "bytes4", name: "selector", type: "bytes4" }],
+ name: "incrementNonce",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ components: [
+ { internalType: "string", name: "name", type: "string" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategyLogic",
+ type: "address",
+ },
+ {
+ internalType: "contract ILlamaAccount",
+ name: "accountLogic",
+ type: "address",
+ },
+ {
+ internalType: "bytes[]",
+ name: "initialStrategies",
+ type: "bytes[]",
+ },
+ {
+ internalType: "bytes[]",
+ name: "initialAccounts",
+ type: "bytes[]",
+ },
+ {
+ components: [
+ {
+ internalType: "RoleDescription[]",
+ name: "roleDescriptions",
+ type: "bytes32[]",
+ },
+ {
+ components: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ internalType: "address",
+ name: "policyholder",
+ type: "address",
+ },
+ {
+ internalType: "uint96",
+ name: "quantity",
+ type: "uint96",
+ },
+ {
+ internalType: "uint64",
+ name: "expiration",
+ type: "uint64",
+ },
+ ],
+ internalType: "struct RoleHolderData[]",
+ name: "roleHolders",
+ type: "tuple[]",
+ },
+ {
+ components: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ components: [
+ {
+ internalType: "address",
+ name: "target",
+ type: "address",
+ },
+ {
+ internalType: "bytes4",
+ name: "selector",
+ type: "bytes4",
+ },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ ],
+ internalType: "struct PermissionData",
+ name: "permissionData",
+ type: "tuple",
+ },
+ {
+ internalType: "bool",
+ name: "hasPermission",
+ type: "bool",
+ },
+ ],
+ internalType: "struct RolePermissionData[]",
+ name: "rolePermissions",
+ type: "tuple[]",
+ },
+ { internalType: "string", name: "color", type: "string" },
+ { internalType: "string", name: "logo", type: "string" },
+ ],
+ internalType: "struct LlamaPolicyConfig",
+ name: "policyConfig",
+ type: "tuple",
+ },
+ ],
+ internalType: "struct LlamaInstanceConfig",
+ name: "config",
+ type: "tuple",
+ },
+ {
+ internalType: "contract LlamaPolicy",
+ name: "policyLogic",
+ type: "address",
+ },
+ {
+ internalType: "contract ILlamaPolicyMetadata",
+ name: "policyMetadataLogic",
+ type: "address",
+ },
+ ],
+ name: "initialize",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "name",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "bytes4", name: "selector", type: "bytes4" },
+ ],
+ name: "nonces",
+ outputs: [
+ { internalType: "uint256", name: "currentNonce", type: "uint256" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "policy",
+ outputs: [
+ { internalType: "contract LlamaPolicy", name: "", type: "address" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ components: [
+ { internalType: "uint256", name: "id", type: "uint256" },
+ { internalType: "address", name: "creator", type: "address" },
+ { internalType: "uint8", name: "creatorRole", type: "uint8" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "uint256", name: "value", type: "uint256" },
+ { internalType: "bytes", name: "data", type: "bytes" },
+ ],
+ internalType: "struct ActionInfo",
+ name: "actionInfo",
+ type: "tuple",
+ },
+ ],
+ name: "queueAction",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract ILlamaAccount",
+ name: "accountLogic",
+ type: "address",
+ },
+ { internalType: "bool", name: "authorized", type: "bool" },
+ ],
+ name: "setAccountLogicAuthorization",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "bytes4", name: "selector", type: "bytes4" },
+ {
+ internalType: "contract ILlamaActionGuard",
+ name: "guard",
+ type: "address",
+ },
+ ],
+ name: "setGuard",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "script", type: "address" },
+ { internalType: "bool", name: "authorized", type: "bool" },
+ ],
+ name: "setScriptAuthorization",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ { internalType: "bool", name: "authorized", type: "bool" },
+ ],
+ name: "setStrategyAuthorization",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategyLogic",
+ type: "address",
+ },
+ { internalType: "bool", name: "authorized", type: "bool" },
+ ],
+ name: "setStrategyLogicAuthorization",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ ],
+ name: "strategies",
+ outputs: [
+ { internalType: "bool", name: "deployed", type: "bool" },
+ { internalType: "bool", name: "authorized", type: "bool" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+] as const;
diff --git a/examples/factory-llama/abis/LlamaCore.json b/examples/factory-llama/abis/LlamaCore.json
deleted file mode 100644
index b786a17c9..000000000
--- a/examples/factory-llama/abis/LlamaCore.json
+++ /dev/null
@@ -1,1160 +0,0 @@
-[
- { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" },
- { "inputs": [], "name": "BootstrapStrategyNotAuthorized", "type": "error" },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" }
- ],
- "name": "CannotCastWithZeroQuantity",
- "type": "error"
- },
- {
- "inputs": [],
- "name": "CannotDisapproveAfterMinExecutionTime",
- "type": "error"
- },
- { "inputs": [], "name": "CannotSetExecutorAsTarget", "type": "error" },
- { "inputs": [], "name": "DuplicateCast", "type": "error" },
- {
- "inputs": [{ "internalType": "bytes", "name": "reason", "type": "bytes" }],
- "name": "FailedActionExecution",
- "type": "error"
- },
- { "inputs": [], "name": "IncorrectMsgValue", "type": "error" },
- { "inputs": [], "name": "InfoHashMismatch", "type": "error" },
- {
- "inputs": [
- { "internalType": "enum ActionState", "name": "current", "type": "uint8" }
- ],
- "name": "InvalidActionState",
- "type": "error"
- },
- { "inputs": [], "name": "InvalidPolicyholder", "type": "error" },
- { "inputs": [], "name": "InvalidSignature", "type": "error" },
- {
- "inputs": [],
- "name": "MinExecutionTimeCannotBeInThePast",
- "type": "error"
- },
- { "inputs": [], "name": "MinExecutionTimeNotReached", "type": "error" },
- { "inputs": [], "name": "NonExistentStrategy", "type": "error" },
- { "inputs": [], "name": "OnlyLlama", "type": "error" },
- {
- "inputs": [],
- "name": "PolicyholderDoesNotHavePermission",
- "type": "error"
- },
- { "inputs": [], "name": "RestrictedAddress", "type": "error" },
- { "inputs": [], "name": "UnauthorizedAccountLogic", "type": "error" },
- { "inputs": [], "name": "UnauthorizedStrategy", "type": "error" },
- { "inputs": [], "name": "UnauthorizedStrategyLogic", "type": "error" },
- {
- "inputs": [{ "internalType": "uint256", "name": "n", "type": "uint256" }],
- "name": "UnsafeCast",
- "type": "error"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "contract ILlamaAccount",
- "name": "account",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "contract ILlamaAccount",
- "name": "accountLogic",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bytes",
- "name": "initializationData",
- "type": "bytes"
- }
- ],
- "name": "AccountCreated",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "contract ILlamaAccount",
- "name": "accountLogic",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "authorized",
- "type": "bool"
- }
- ],
- "name": "AccountLogicAuthorizationSet",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "caller",
- "type": "address"
- }
- ],
- "name": "ActionCanceled",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "creator",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint8",
- "name": "role",
- "type": "uint8"
- },
- {
- "indexed": true,
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "target",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "value",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "bytes",
- "name": "data",
- "type": "bytes"
- },
- {
- "indexed": false,
- "internalType": "string",
- "name": "description",
- "type": "string"
- }
- ],
- "name": "ActionCreated",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "caller",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "creator",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bytes",
- "name": "result",
- "type": "bytes"
- }
- ],
- "name": "ActionExecuted",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "target",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "bytes4",
- "name": "selector",
- "type": "bytes4"
- },
- {
- "indexed": false,
- "internalType": "contract ILlamaActionGuard",
- "name": "actionGuard",
- "type": "address"
- }
- ],
- "name": "ActionGuardSet",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "caller",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "creator",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "minExecutionTime",
- "type": "uint256"
- }
- ],
- "name": "ActionQueued",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "policyholder",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint8",
- "name": "role",
- "type": "uint8"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "quantity",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "string",
- "name": "reason",
- "type": "string"
- }
- ],
- "name": "ApprovalCast",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "policyholder",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint8",
- "name": "role",
- "type": "uint8"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "quantity",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "string",
- "name": "reason",
- "type": "string"
- }
- ],
- "name": "DisapprovalCast",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint8",
- "name": "version",
- "type": "uint8"
- }
- ],
- "name": "Initialized",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "script",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "authorized",
- "type": "bool"
- }
- ],
- "name": "ScriptAuthorizationSet",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "authorized",
- "type": "bool"
- }
- ],
- "name": "StrategyAuthorizationSet",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "contract ILlamaStrategy",
- "name": "strategyLogic",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bytes",
- "name": "initializationData",
- "type": "bytes"
- }
- ],
- "name": "StrategyCreated",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "contract ILlamaStrategy",
- "name": "strategyLogic",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "authorized",
- "type": "bool"
- }
- ],
- "name": "StrategyLogicAuthorizationSet",
- "type": "event"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "bytes4", "name": "selector", "type": "bytes4" }
- ],
- "name": "actionGuard",
- "outputs": [
- {
- "internalType": "contract ILlamaActionGuard",
- "name": "guard",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "actionsCount",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "actionId", "type": "uint256" },
- { "internalType": "address", "name": "policyholder", "type": "address" }
- ],
- "name": "approvals",
- "outputs": [
- { "internalType": "bool", "name": "hasApproved", "type": "bool" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract ILlamaAccount",
- "name": "accountLogic",
- "type": "address"
- }
- ],
- "name": "authorizedAccountLogics",
- "outputs": [
- { "internalType": "bool", "name": "isAuthorized", "type": "bool" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "script", "type": "address" }
- ],
- "name": "authorizedScripts",
- "outputs": [
- { "internalType": "bool", "name": "isAuthorized", "type": "bool" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategyLogic",
- "type": "address"
- }
- ],
- "name": "authorizedStrategyLogics",
- "outputs": [
- { "internalType": "bool", "name": "isAuthorized", "type": "bool" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "components": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "creator", "type": "address" },
- { "internalType": "uint8", "name": "creatorRole", "type": "uint8" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "uint256", "name": "value", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "internalType": "struct ActionInfo",
- "name": "actionInfo",
- "type": "tuple"
- }
- ],
- "name": "cancelAction",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- {
- "components": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "creator", "type": "address" },
- { "internalType": "uint8", "name": "creatorRole", "type": "uint8" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "uint256", "name": "value", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "internalType": "struct ActionInfo",
- "name": "actionInfo",
- "type": "tuple"
- },
- { "internalType": "uint8", "name": "v", "type": "uint8" },
- { "internalType": "bytes32", "name": "r", "type": "bytes32" },
- { "internalType": "bytes32", "name": "s", "type": "bytes32" }
- ],
- "name": "cancelActionBySig",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "components": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "creator", "type": "address" },
- { "internalType": "uint8", "name": "creatorRole", "type": "uint8" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "uint256", "name": "value", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "internalType": "struct ActionInfo",
- "name": "actionInfo",
- "type": "tuple"
- },
- { "internalType": "string", "name": "reason", "type": "string" }
- ],
- "name": "castApproval",
- "outputs": [{ "internalType": "uint96", "name": "", "type": "uint96" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "components": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "creator", "type": "address" },
- { "internalType": "uint8", "name": "creatorRole", "type": "uint8" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "uint256", "name": "value", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "internalType": "struct ActionInfo",
- "name": "actionInfo",
- "type": "tuple"
- },
- { "internalType": "string", "name": "reason", "type": "string" },
- { "internalType": "uint8", "name": "v", "type": "uint8" },
- { "internalType": "bytes32", "name": "r", "type": "bytes32" },
- { "internalType": "bytes32", "name": "s", "type": "bytes32" }
- ],
- "name": "castApprovalBySig",
- "outputs": [{ "internalType": "uint96", "name": "", "type": "uint96" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "components": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "creator", "type": "address" },
- { "internalType": "uint8", "name": "creatorRole", "type": "uint8" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "uint256", "name": "value", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "internalType": "struct ActionInfo",
- "name": "actionInfo",
- "type": "tuple"
- },
- { "internalType": "string", "name": "reason", "type": "string" }
- ],
- "name": "castDisapproval",
- "outputs": [{ "internalType": "uint96", "name": "", "type": "uint96" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "components": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "creator", "type": "address" },
- { "internalType": "uint8", "name": "creatorRole", "type": "uint8" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "uint256", "name": "value", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "internalType": "struct ActionInfo",
- "name": "actionInfo",
- "type": "tuple"
- },
- { "internalType": "string", "name": "reason", "type": "string" },
- { "internalType": "uint8", "name": "v", "type": "uint8" },
- { "internalType": "bytes32", "name": "r", "type": "bytes32" },
- { "internalType": "bytes32", "name": "s", "type": "bytes32" }
- ],
- "name": "castDisapprovalBySig",
- "outputs": [{ "internalType": "uint96", "name": "", "type": "uint96" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract ILlamaAccount",
- "name": "llamaAccountLogic",
- "type": "address"
- },
- { "internalType": "bytes[]", "name": "accountConfigs", "type": "bytes[]" }
- ],
- "name": "createAccounts",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "uint256", "name": "value", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" },
- { "internalType": "string", "name": "description", "type": "string" }
- ],
- "name": "createAction",
- "outputs": [
- { "internalType": "uint256", "name": "actionId", "type": "uint256" }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "uint256", "name": "value", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" },
- { "internalType": "string", "name": "description", "type": "string" },
- { "internalType": "uint8", "name": "v", "type": "uint8" },
- { "internalType": "bytes32", "name": "r", "type": "bytes32" },
- { "internalType": "bytes32", "name": "s", "type": "bytes32" }
- ],
- "name": "createActionBySig",
- "outputs": [
- { "internalType": "uint256", "name": "actionId", "type": "uint256" }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract ILlamaStrategy",
- "name": "llamaStrategyLogic",
- "type": "address"
- },
- {
- "internalType": "bytes[]",
- "name": "strategyConfigs",
- "type": "bytes[]"
- }
- ],
- "name": "createStrategies",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "actionId", "type": "uint256" },
- { "internalType": "address", "name": "policyholder", "type": "address" }
- ],
- "name": "disapprovals",
- "outputs": [
- { "internalType": "bool", "name": "hasDisapproved", "type": "bool" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "components": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "creator", "type": "address" },
- { "internalType": "uint8", "name": "creatorRole", "type": "uint8" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "uint256", "name": "value", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "internalType": "struct ActionInfo",
- "name": "actionInfo",
- "type": "tuple"
- }
- ],
- "name": "executeAction",
- "outputs": [],
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "executor",
- "outputs": [
- {
- "internalType": "contract LlamaExecutor",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "actionId", "type": "uint256" }
- ],
- "name": "getAction",
- "outputs": [
- {
- "components": [
- { "internalType": "bytes32", "name": "infoHash", "type": "bytes32" },
- { "internalType": "bool", "name": "executed", "type": "bool" },
- { "internalType": "bool", "name": "canceled", "type": "bool" },
- { "internalType": "bool", "name": "isScript", "type": "bool" },
- {
- "internalType": "contract ILlamaActionGuard",
- "name": "guard",
- "type": "address"
- },
- {
- "internalType": "uint64",
- "name": "creationTime",
- "type": "uint64"
- },
- {
- "internalType": "uint64",
- "name": "minExecutionTime",
- "type": "uint64"
- },
- {
- "internalType": "uint96",
- "name": "totalApprovals",
- "type": "uint96"
- },
- {
- "internalType": "uint96",
- "name": "totalDisapprovals",
- "type": "uint96"
- }
- ],
- "internalType": "struct Action",
- "name": "",
- "type": "tuple"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "components": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "creator", "type": "address" },
- { "internalType": "uint8", "name": "creatorRole", "type": "uint8" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "uint256", "name": "value", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "internalType": "struct ActionInfo",
- "name": "actionInfo",
- "type": "tuple"
- }
- ],
- "name": "getActionState",
- "outputs": [
- { "internalType": "enum ActionState", "name": "", "type": "uint8" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes4", "name": "selector", "type": "bytes4" }
- ],
- "name": "incrementNonce",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "components": [
- { "internalType": "string", "name": "name", "type": "string" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategyLogic",
- "type": "address"
- },
- {
- "internalType": "contract ILlamaAccount",
- "name": "accountLogic",
- "type": "address"
- },
- {
- "internalType": "bytes[]",
- "name": "initialStrategies",
- "type": "bytes[]"
- },
- {
- "internalType": "bytes[]",
- "name": "initialAccounts",
- "type": "bytes[]"
- },
- {
- "components": [
- {
- "internalType": "RoleDescription[]",
- "name": "roleDescriptions",
- "type": "bytes32[]"
- },
- {
- "components": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "internalType": "address",
- "name": "policyholder",
- "type": "address"
- },
- {
- "internalType": "uint96",
- "name": "quantity",
- "type": "uint96"
- },
- {
- "internalType": "uint64",
- "name": "expiration",
- "type": "uint64"
- }
- ],
- "internalType": "struct RoleHolderData[]",
- "name": "roleHolders",
- "type": "tuple[]"
- },
- {
- "components": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "components": [
- {
- "internalType": "address",
- "name": "target",
- "type": "address"
- },
- {
- "internalType": "bytes4",
- "name": "selector",
- "type": "bytes4"
- },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- }
- ],
- "internalType": "struct PermissionData",
- "name": "permissionData",
- "type": "tuple"
- },
- {
- "internalType": "bool",
- "name": "hasPermission",
- "type": "bool"
- }
- ],
- "internalType": "struct RolePermissionData[]",
- "name": "rolePermissions",
- "type": "tuple[]"
- },
- { "internalType": "string", "name": "color", "type": "string" },
- { "internalType": "string", "name": "logo", "type": "string" }
- ],
- "internalType": "struct LlamaPolicyConfig",
- "name": "policyConfig",
- "type": "tuple"
- }
- ],
- "internalType": "struct LlamaInstanceConfig",
- "name": "config",
- "type": "tuple"
- },
- {
- "internalType": "contract LlamaPolicy",
- "name": "policyLogic",
- "type": "address"
- },
- {
- "internalType": "contract ILlamaPolicyMetadata",
- "name": "policyMetadataLogic",
- "type": "address"
- }
- ],
- "name": "initialize",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "name",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "bytes4", "name": "selector", "type": "bytes4" }
- ],
- "name": "nonces",
- "outputs": [
- { "internalType": "uint256", "name": "currentNonce", "type": "uint256" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "policy",
- "outputs": [
- { "internalType": "contract LlamaPolicy", "name": "", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "components": [
- { "internalType": "uint256", "name": "id", "type": "uint256" },
- { "internalType": "address", "name": "creator", "type": "address" },
- { "internalType": "uint8", "name": "creatorRole", "type": "uint8" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "uint256", "name": "value", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "internalType": "struct ActionInfo",
- "name": "actionInfo",
- "type": "tuple"
- }
- ],
- "name": "queueAction",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract ILlamaAccount",
- "name": "accountLogic",
- "type": "address"
- },
- { "internalType": "bool", "name": "authorized", "type": "bool" }
- ],
- "name": "setAccountLogicAuthorization",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "bytes4", "name": "selector", "type": "bytes4" },
- {
- "internalType": "contract ILlamaActionGuard",
- "name": "guard",
- "type": "address"
- }
- ],
- "name": "setGuard",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "script", "type": "address" },
- { "internalType": "bool", "name": "authorized", "type": "bool" }
- ],
- "name": "setScriptAuthorization",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- },
- { "internalType": "bool", "name": "authorized", "type": "bool" }
- ],
- "name": "setStrategyAuthorization",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategyLogic",
- "type": "address"
- },
- { "internalType": "bool", "name": "authorized", "type": "bool" }
- ],
- "name": "setStrategyLogicAuthorization",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- }
- ],
- "name": "strategies",
- "outputs": [
- { "internalType": "bool", "name": "deployed", "type": "bool" },
- { "internalType": "bool", "name": "authorized", "type": "bool" }
- ],
- "stateMutability": "view",
- "type": "function"
- }
-]
diff --git a/examples/factory-llama/abis/LlamaPolicy.abi.ts b/examples/factory-llama/abis/LlamaPolicy.abi.ts
new file mode 100644
index 000000000..e00afdcb8
--- /dev/null
+++ b/examples/factory-llama/abis/LlamaPolicy.abi.ts
@@ -0,0 +1,899 @@
+export const LlamaPolicyAbi = [
+ { inputs: [], stateMutability: "nonpayable", type: "constructor" },
+ { inputs: [], name: "ActionCreationAtSameTimestamp", type: "error" },
+ {
+ inputs: [{ internalType: "address", name: "userAddress", type: "address" }],
+ name: "AddressDoesNotHoldPolicy",
+ type: "error",
+ },
+ { inputs: [], name: "AllHoldersRole", type: "error" },
+ { inputs: [], name: "AlreadyInitialized", type: "error" },
+ { inputs: [], name: "InvalidIndices", type: "error" },
+ { inputs: [], name: "InvalidRoleHolderInput", type: "error" },
+ { inputs: [], name: "NonTransferableToken", type: "error" },
+ { inputs: [], name: "OnlyLlama", type: "error" },
+ { inputs: [], name: "OnlyLlamaFactory", type: "error" },
+ {
+ inputs: [{ internalType: "uint8", name: "role", type: "uint8" }],
+ name: "RoleNotInitialized",
+ type: "error",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "n", type: "uint256" }],
+ name: "UnsafeCast",
+ type: "error",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "spender",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ ],
+ name: "Approval",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "operator",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bool",
+ name: "approved",
+ type: "bool",
+ },
+ ],
+ name: "ApprovalForAll",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "caller",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "policyholder",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint8",
+ name: "role",
+ type: "uint8",
+ },
+ ],
+ name: "ExpiredRoleRevoked",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint8",
+ name: "version",
+ type: "uint8",
+ },
+ ],
+ name: "Initialized",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "contract ILlamaPolicyMetadata",
+ name: "policyMetadata",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "contract ILlamaPolicyMetadata",
+ name: "policyMetadataLogic",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bytes",
+ name: "initializationData",
+ type: "bytes",
+ },
+ ],
+ name: "PolicyMetadataSet",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "policyholder",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint8",
+ name: "role",
+ type: "uint8",
+ },
+ {
+ indexed: false,
+ internalType: "uint64",
+ name: "expiration",
+ type: "uint64",
+ },
+ {
+ indexed: false,
+ internalType: "uint96",
+ name: "quantity",
+ type: "uint96",
+ },
+ ],
+ name: "RoleAssigned",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "uint8",
+ name: "role",
+ type: "uint8",
+ },
+ {
+ indexed: false,
+ internalType: "RoleDescription",
+ name: "description",
+ type: "bytes32",
+ },
+ ],
+ name: "RoleInitialized",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "uint8",
+ name: "role",
+ type: "uint8",
+ },
+ {
+ indexed: true,
+ internalType: "bytes32",
+ name: "permissionId",
+ type: "bytes32",
+ },
+ {
+ components: [
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "bytes4", name: "selector", type: "bytes4" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ ],
+ indexed: false,
+ internalType: "struct PermissionData",
+ name: "permissionData",
+ type: "tuple",
+ },
+ {
+ indexed: false,
+ internalType: "bool",
+ name: "hasPermission",
+ type: "bool",
+ },
+ ],
+ name: "RolePermissionAssigned",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "from",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "to",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "id",
+ type: "uint256",
+ },
+ ],
+ name: "Transfer",
+ type: "event",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "uint256", name: "", type: "uint256" },
+ ],
+ name: "approve",
+ outputs: [],
+ stateMutability: "pure",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "owner", type: "address" }],
+ name: "balanceOf",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ { internalType: "bytes32", name: "permissionId", type: "bytes32" },
+ ],
+ name: "canCreateAction",
+ outputs: [{ internalType: "bool", name: "hasPermission", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "contractURI",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ name: "getApproved",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ { internalType: "uint256", name: "timestamp", type: "uint256" },
+ ],
+ name: "getPastQuantity",
+ outputs: [{ internalType: "uint96", name: "", type: "uint96" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ { internalType: "uint256", name: "timestamp", type: "uint256" },
+ ],
+ name: "getPastRoleSupplyAsNumberOfHolders",
+ outputs: [
+ { internalType: "uint96", name: "numberOfHolders", type: "uint96" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ { internalType: "uint256", name: "timestamp", type: "uint256" },
+ ],
+ name: "getPastRoleSupplyAsQuantitySum",
+ outputs: [
+ { internalType: "uint96", name: "totalQuantity", type: "uint96" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ ],
+ name: "getQuantity",
+ outputs: [{ internalType: "uint96", name: "", type: "uint96" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint8", name: "role", type: "uint8" }],
+ name: "getRoleSupplyAsNumberOfHolders",
+ outputs: [
+ { internalType: "uint96", name: "numberOfHolders", type: "uint96" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint8", name: "role", type: "uint8" }],
+ name: "getRoleSupplyAsQuantitySum",
+ outputs: [
+ { internalType: "uint96", name: "totalQuantity", type: "uint96" },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ { internalType: "bytes32", name: "permissionId", type: "bytes32" },
+ ],
+ name: "hasPermissionId",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ ],
+ name: "hasRole",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ { internalType: "uint256", name: "timestamp", type: "uint256" },
+ ],
+ name: "hasRole",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "string", name: "_name", type: "string" },
+ {
+ components: [
+ {
+ internalType: "RoleDescription[]",
+ name: "roleDescriptions",
+ type: "bytes32[]",
+ },
+ {
+ components: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ internalType: "address",
+ name: "policyholder",
+ type: "address",
+ },
+ {
+ internalType: "uint96",
+ name: "quantity",
+ type: "uint96",
+ },
+ {
+ internalType: "uint64",
+ name: "expiration",
+ type: "uint64",
+ },
+ ],
+ internalType: "struct RoleHolderData[]",
+ name: "roleHolders",
+ type: "tuple[]",
+ },
+ {
+ components: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ components: [
+ {
+ internalType: "address",
+ name: "target",
+ type: "address",
+ },
+ {
+ internalType: "bytes4",
+ name: "selector",
+ type: "bytes4",
+ },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ ],
+ internalType: "struct PermissionData",
+ name: "permissionData",
+ type: "tuple",
+ },
+ {
+ internalType: "bool",
+ name: "hasPermission",
+ type: "bool",
+ },
+ ],
+ internalType: "struct RolePermissionData[]",
+ name: "rolePermissions",
+ type: "tuple[]",
+ },
+ { internalType: "string", name: "color", type: "string" },
+ { internalType: "string", name: "logo", type: "string" },
+ ],
+ internalType: "struct LlamaPolicyConfig",
+ name: "config",
+ type: "tuple",
+ },
+ {
+ internalType: "contract ILlamaPolicyMetadata",
+ name: "policyMetadataLogic",
+ type: "address",
+ },
+ { internalType: "address", name: "executor", type: "address" },
+ {
+ components: [
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "bytes4", name: "selector", type: "bytes4" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ ],
+ internalType: "struct PermissionData",
+ name: "bootstrapPermissionData",
+ type: "tuple",
+ },
+ ],
+ name: "initialize",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "RoleDescription",
+ name: "description",
+ type: "bytes32",
+ },
+ ],
+ name: "initializeRole",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "address", name: "", type: "address" },
+ ],
+ name: "isApprovedForAll",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ ],
+ name: "isRoleExpired",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "llamaExecutor",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "llamaPolicyMetadata",
+ outputs: [
+ {
+ internalType: "contract ILlamaPolicyMetadata",
+ name: "",
+ type: "address",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "name",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "numRoles",
+ outputs: [{ internalType: "uint8", name: "", type: "uint8" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "id", type: "uint256" }],
+ name: "ownerOf",
+ outputs: [{ internalType: "address", name: "owner", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ { internalType: "address", name: "policyholder", type: "address" },
+ ],
+ name: "revokeExpiredRole",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ ],
+ name: "revokePolicy",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ { internalType: "uint256", name: "start", type: "uint256" },
+ { internalType: "uint256", name: "end", type: "uint256" },
+ ],
+ name: "roleBalanceCheckpoints",
+ outputs: [
+ {
+ components: [
+ {
+ components: [
+ {
+ internalType: "uint64",
+ name: "timestamp",
+ type: "uint64",
+ },
+ {
+ internalType: "uint64",
+ name: "expiration",
+ type: "uint64",
+ },
+ { internalType: "uint96", name: "quantity", type: "uint96" },
+ ],
+ internalType: "struct PolicyholderCheckpoints.Checkpoint[]",
+ name: "_checkpoints",
+ type: "tuple[]",
+ },
+ ],
+ internalType: "struct PolicyholderCheckpoints.History",
+ name: "",
+ type: "tuple",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ ],
+ name: "roleBalanceCheckpoints",
+ outputs: [
+ {
+ components: [
+ {
+ components: [
+ {
+ internalType: "uint64",
+ name: "timestamp",
+ type: "uint64",
+ },
+ {
+ internalType: "uint64",
+ name: "expiration",
+ type: "uint64",
+ },
+ { internalType: "uint96", name: "quantity", type: "uint96" },
+ ],
+ internalType: "struct PolicyholderCheckpoints.Checkpoint[]",
+ name: "_checkpoints",
+ type: "tuple[]",
+ },
+ ],
+ internalType: "struct PolicyholderCheckpoints.History",
+ name: "",
+ type: "tuple",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ ],
+ name: "roleBalanceCheckpointsLength",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint8", name: "role", type: "uint8" },
+ ],
+ name: "roleExpiration",
+ outputs: [{ internalType: "uint64", name: "", type: "uint64" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ { internalType: "uint256", name: "start", type: "uint256" },
+ { internalType: "uint256", name: "end", type: "uint256" },
+ ],
+ name: "roleSupplyCheckpoints",
+ outputs: [
+ {
+ components: [
+ {
+ components: [
+ {
+ internalType: "uint64",
+ name: "timestamp",
+ type: "uint64",
+ },
+ {
+ internalType: "uint96",
+ name: "numberOfHolders",
+ type: "uint96",
+ },
+ {
+ internalType: "uint96",
+ name: "totalQuantity",
+ type: "uint96",
+ },
+ ],
+ internalType: "struct SupplyCheckpoints.Checkpoint[]",
+ name: "_checkpoints",
+ type: "tuple[]",
+ },
+ ],
+ internalType: "struct SupplyCheckpoints.History",
+ name: "",
+ type: "tuple",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint8", name: "role", type: "uint8" }],
+ name: "roleSupplyCheckpoints",
+ outputs: [
+ {
+ components: [
+ {
+ components: [
+ {
+ internalType: "uint64",
+ name: "timestamp",
+ type: "uint64",
+ },
+ {
+ internalType: "uint96",
+ name: "numberOfHolders",
+ type: "uint96",
+ },
+ {
+ internalType: "uint96",
+ name: "totalQuantity",
+ type: "uint96",
+ },
+ ],
+ internalType: "struct SupplyCheckpoints.Checkpoint[]",
+ name: "_checkpoints",
+ type: "tuple[]",
+ },
+ ],
+ internalType: "struct SupplyCheckpoints.History",
+ name: "",
+ type: "tuple",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint8", name: "role", type: "uint8" }],
+ name: "roleSupplyCheckpointsLength",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "uint256", name: "", type: "uint256" },
+ ],
+ name: "safeTransferFrom",
+ outputs: [],
+ stateMutability: "pure",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "uint256", name: "", type: "uint256" },
+ { internalType: "bytes", name: "", type: "bytes" },
+ ],
+ name: "safeTransferFrom",
+ outputs: [],
+ stateMutability: "pure",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "contract ILlamaPolicyMetadata",
+ name: "llamaPolicyMetadataLogic",
+ type: "address",
+ },
+ { internalType: "bytes", name: "config", type: "bytes" },
+ ],
+ name: "setAndInitializePolicyMetadata",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "bool", name: "", type: "bool" },
+ ],
+ name: "setApprovalForAll",
+ outputs: [],
+ stateMutability: "pure",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ { internalType: "address", name: "policyholder", type: "address" },
+ { internalType: "uint96", name: "quantity", type: "uint96" },
+ { internalType: "uint64", name: "expiration", type: "uint64" },
+ ],
+ name: "setRoleHolder",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ components: [
+ { internalType: "address", name: "target", type: "address" },
+ { internalType: "bytes4", name: "selector", type: "bytes4" },
+ {
+ internalType: "contract ILlamaStrategy",
+ name: "strategy",
+ type: "address",
+ },
+ ],
+ internalType: "struct PermissionData",
+ name: "permissionData",
+ type: "tuple",
+ },
+ { internalType: "bool", name: "hasPermission", type: "bool" },
+ ],
+ name: "setRolePermission",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "bytes4", name: "interfaceId", type: "bytes4" }],
+ name: "supportsInterface",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "symbol",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }],
+ name: "tokenURI",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "totalSupply",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "uint256", name: "", type: "uint256" },
+ ],
+ name: "transferFrom",
+ outputs: [],
+ stateMutability: "pure",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint8", name: "role", type: "uint8" },
+ {
+ internalType: "RoleDescription",
+ name: "description",
+ type: "bytes32",
+ },
+ ],
+ name: "updateRoleDescription",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+] as const;
diff --git a/examples/factory-llama/abis/LlamaPolicy.json b/examples/factory-llama/abis/LlamaPolicy.json
deleted file mode 100644
index 15dcee5e5..000000000
--- a/examples/factory-llama/abis/LlamaPolicy.json
+++ /dev/null
@@ -1,911 +0,0 @@
-[
- { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" },
- { "inputs": [], "name": "ActionCreationAtSameTimestamp", "type": "error" },
- {
- "inputs": [
- { "internalType": "address", "name": "userAddress", "type": "address" }
- ],
- "name": "AddressDoesNotHoldPolicy",
- "type": "error"
- },
- { "inputs": [], "name": "AllHoldersRole", "type": "error" },
- { "inputs": [], "name": "AlreadyInitialized", "type": "error" },
- { "inputs": [], "name": "InvalidIndices", "type": "error" },
- { "inputs": [], "name": "InvalidRoleHolderInput", "type": "error" },
- { "inputs": [], "name": "NonTransferableToken", "type": "error" },
- { "inputs": [], "name": "OnlyLlama", "type": "error" },
- { "inputs": [], "name": "OnlyLlamaFactory", "type": "error" },
- {
- "inputs": [{ "internalType": "uint8", "name": "role", "type": "uint8" }],
- "name": "RoleNotInitialized",
- "type": "error"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "n", "type": "uint256" }],
- "name": "UnsafeCast",
- "type": "error"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "spender",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "operator",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "approved",
- "type": "bool"
- }
- ],
- "name": "ApprovalForAll",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "caller",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "policyholder",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint8",
- "name": "role",
- "type": "uint8"
- }
- ],
- "name": "ExpiredRoleRevoked",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint8",
- "name": "version",
- "type": "uint8"
- }
- ],
- "name": "Initialized",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "contract ILlamaPolicyMetadata",
- "name": "policyMetadata",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "contract ILlamaPolicyMetadata",
- "name": "policyMetadataLogic",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bytes",
- "name": "initializationData",
- "type": "bytes"
- }
- ],
- "name": "PolicyMetadataSet",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "policyholder",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint8",
- "name": "role",
- "type": "uint8"
- },
- {
- "indexed": false,
- "internalType": "uint64",
- "name": "expiration",
- "type": "uint64"
- },
- {
- "indexed": false,
- "internalType": "uint96",
- "name": "quantity",
- "type": "uint96"
- }
- ],
- "name": "RoleAssigned",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "uint8",
- "name": "role",
- "type": "uint8"
- },
- {
- "indexed": false,
- "internalType": "RoleDescription",
- "name": "description",
- "type": "bytes32"
- }
- ],
- "name": "RoleInitialized",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "uint8",
- "name": "role",
- "type": "uint8"
- },
- {
- "indexed": true,
- "internalType": "bytes32",
- "name": "permissionId",
- "type": "bytes32"
- },
- {
- "components": [
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "bytes4", "name": "selector", "type": "bytes4" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- }
- ],
- "indexed": false,
- "internalType": "struct PermissionData",
- "name": "permissionData",
- "type": "tuple"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "hasPermission",
- "type": "bool"
- }
- ],
- "name": "RolePermissionAssigned",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "id",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "uint256", "name": "", "type": "uint256" }
- ],
- "name": "approve",
- "outputs": [],
- "stateMutability": "pure",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" }
- ],
- "name": "balanceOf",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- { "internalType": "bytes32", "name": "permissionId", "type": "bytes32" }
- ],
- "name": "canCreateAction",
- "outputs": [
- { "internalType": "bool", "name": "hasPermission", "type": "bool" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "contractURI",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "name": "getApproved",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- { "internalType": "uint256", "name": "timestamp", "type": "uint256" }
- ],
- "name": "getPastQuantity",
- "outputs": [{ "internalType": "uint96", "name": "", "type": "uint96" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- { "internalType": "uint256", "name": "timestamp", "type": "uint256" }
- ],
- "name": "getPastRoleSupplyAsNumberOfHolders",
- "outputs": [
- { "internalType": "uint96", "name": "numberOfHolders", "type": "uint96" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- { "internalType": "uint256", "name": "timestamp", "type": "uint256" }
- ],
- "name": "getPastRoleSupplyAsQuantitySum",
- "outputs": [
- { "internalType": "uint96", "name": "totalQuantity", "type": "uint96" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" }
- ],
- "name": "getQuantity",
- "outputs": [{ "internalType": "uint96", "name": "", "type": "uint96" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint8", "name": "role", "type": "uint8" }],
- "name": "getRoleSupplyAsNumberOfHolders",
- "outputs": [
- { "internalType": "uint96", "name": "numberOfHolders", "type": "uint96" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint8", "name": "role", "type": "uint8" }],
- "name": "getRoleSupplyAsQuantitySum",
- "outputs": [
- { "internalType": "uint96", "name": "totalQuantity", "type": "uint96" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- { "internalType": "bytes32", "name": "permissionId", "type": "bytes32" }
- ],
- "name": "hasPermissionId",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" }
- ],
- "name": "hasRole",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- { "internalType": "uint256", "name": "timestamp", "type": "uint256" }
- ],
- "name": "hasRole",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "string", "name": "_name", "type": "string" },
- {
- "components": [
- {
- "internalType": "RoleDescription[]",
- "name": "roleDescriptions",
- "type": "bytes32[]"
- },
- {
- "components": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "internalType": "address",
- "name": "policyholder",
- "type": "address"
- },
- {
- "internalType": "uint96",
- "name": "quantity",
- "type": "uint96"
- },
- {
- "internalType": "uint64",
- "name": "expiration",
- "type": "uint64"
- }
- ],
- "internalType": "struct RoleHolderData[]",
- "name": "roleHolders",
- "type": "tuple[]"
- },
- {
- "components": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "components": [
- {
- "internalType": "address",
- "name": "target",
- "type": "address"
- },
- {
- "internalType": "bytes4",
- "name": "selector",
- "type": "bytes4"
- },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- }
- ],
- "internalType": "struct PermissionData",
- "name": "permissionData",
- "type": "tuple"
- },
- {
- "internalType": "bool",
- "name": "hasPermission",
- "type": "bool"
- }
- ],
- "internalType": "struct RolePermissionData[]",
- "name": "rolePermissions",
- "type": "tuple[]"
- },
- { "internalType": "string", "name": "color", "type": "string" },
- { "internalType": "string", "name": "logo", "type": "string" }
- ],
- "internalType": "struct LlamaPolicyConfig",
- "name": "config",
- "type": "tuple"
- },
- {
- "internalType": "contract ILlamaPolicyMetadata",
- "name": "policyMetadataLogic",
- "type": "address"
- },
- { "internalType": "address", "name": "executor", "type": "address" },
- {
- "components": [
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "bytes4", "name": "selector", "type": "bytes4" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- }
- ],
- "internalType": "struct PermissionData",
- "name": "bootstrapPermissionData",
- "type": "tuple"
- }
- ],
- "name": "initialize",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "RoleDescription",
- "name": "description",
- "type": "bytes32"
- }
- ],
- "name": "initializeRole",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "address", "name": "", "type": "address" }
- ],
- "name": "isApprovedForAll",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" }
- ],
- "name": "isRoleExpired",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "llamaExecutor",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "llamaPolicyMetadata",
- "outputs": [
- {
- "internalType": "contract ILlamaPolicyMetadata",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "name",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "numRoles",
- "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "id", "type": "uint256" }],
- "name": "ownerOf",
- "outputs": [
- { "internalType": "address", "name": "owner", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- { "internalType": "address", "name": "policyholder", "type": "address" }
- ],
- "name": "revokeExpiredRole",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" }
- ],
- "name": "revokePolicy",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- { "internalType": "uint256", "name": "start", "type": "uint256" },
- { "internalType": "uint256", "name": "end", "type": "uint256" }
- ],
- "name": "roleBalanceCheckpoints",
- "outputs": [
- {
- "components": [
- {
- "components": [
- {
- "internalType": "uint64",
- "name": "timestamp",
- "type": "uint64"
- },
- {
- "internalType": "uint64",
- "name": "expiration",
- "type": "uint64"
- },
- { "internalType": "uint96", "name": "quantity", "type": "uint96" }
- ],
- "internalType": "struct PolicyholderCheckpoints.Checkpoint[]",
- "name": "_checkpoints",
- "type": "tuple[]"
- }
- ],
- "internalType": "struct PolicyholderCheckpoints.History",
- "name": "",
- "type": "tuple"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" }
- ],
- "name": "roleBalanceCheckpoints",
- "outputs": [
- {
- "components": [
- {
- "components": [
- {
- "internalType": "uint64",
- "name": "timestamp",
- "type": "uint64"
- },
- {
- "internalType": "uint64",
- "name": "expiration",
- "type": "uint64"
- },
- { "internalType": "uint96", "name": "quantity", "type": "uint96" }
- ],
- "internalType": "struct PolicyholderCheckpoints.Checkpoint[]",
- "name": "_checkpoints",
- "type": "tuple[]"
- }
- ],
- "internalType": "struct PolicyholderCheckpoints.History",
- "name": "",
- "type": "tuple"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" }
- ],
- "name": "roleBalanceCheckpointsLength",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint8", "name": "role", "type": "uint8" }
- ],
- "name": "roleExpiration",
- "outputs": [{ "internalType": "uint64", "name": "", "type": "uint64" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- { "internalType": "uint256", "name": "start", "type": "uint256" },
- { "internalType": "uint256", "name": "end", "type": "uint256" }
- ],
- "name": "roleSupplyCheckpoints",
- "outputs": [
- {
- "components": [
- {
- "components": [
- {
- "internalType": "uint64",
- "name": "timestamp",
- "type": "uint64"
- },
- {
- "internalType": "uint96",
- "name": "numberOfHolders",
- "type": "uint96"
- },
- {
- "internalType": "uint96",
- "name": "totalQuantity",
- "type": "uint96"
- }
- ],
- "internalType": "struct SupplyCheckpoints.Checkpoint[]",
- "name": "_checkpoints",
- "type": "tuple[]"
- }
- ],
- "internalType": "struct SupplyCheckpoints.History",
- "name": "",
- "type": "tuple"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint8", "name": "role", "type": "uint8" }],
- "name": "roleSupplyCheckpoints",
- "outputs": [
- {
- "components": [
- {
- "components": [
- {
- "internalType": "uint64",
- "name": "timestamp",
- "type": "uint64"
- },
- {
- "internalType": "uint96",
- "name": "numberOfHolders",
- "type": "uint96"
- },
- {
- "internalType": "uint96",
- "name": "totalQuantity",
- "type": "uint96"
- }
- ],
- "internalType": "struct SupplyCheckpoints.Checkpoint[]",
- "name": "_checkpoints",
- "type": "tuple[]"
- }
- ],
- "internalType": "struct SupplyCheckpoints.History",
- "name": "",
- "type": "tuple"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint8", "name": "role", "type": "uint8" }],
- "name": "roleSupplyCheckpointsLength",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "uint256", "name": "", "type": "uint256" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "pure",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "uint256", "name": "", "type": "uint256" },
- { "internalType": "bytes", "name": "", "type": "bytes" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "pure",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "contract ILlamaPolicyMetadata",
- "name": "llamaPolicyMetadataLogic",
- "type": "address"
- },
- { "internalType": "bytes", "name": "config", "type": "bytes" }
- ],
- "name": "setAndInitializePolicyMetadata",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "bool", "name": "", "type": "bool" }
- ],
- "name": "setApprovalForAll",
- "outputs": [],
- "stateMutability": "pure",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- { "internalType": "address", "name": "policyholder", "type": "address" },
- { "internalType": "uint96", "name": "quantity", "type": "uint96" },
- { "internalType": "uint64", "name": "expiration", "type": "uint64" }
- ],
- "name": "setRoleHolder",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "components": [
- { "internalType": "address", "name": "target", "type": "address" },
- { "internalType": "bytes4", "name": "selector", "type": "bytes4" },
- {
- "internalType": "contract ILlamaStrategy",
- "name": "strategy",
- "type": "address"
- }
- ],
- "internalType": "struct PermissionData",
- "name": "permissionData",
- "type": "tuple"
- },
- { "internalType": "bool", "name": "hasPermission", "type": "bool" }
- ],
- "name": "setRolePermission",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }
- ],
- "name": "supportsInterface",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "symbol",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "tokenURI",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "totalSupply",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "uint256", "name": "", "type": "uint256" }
- ],
- "name": "transferFrom",
- "outputs": [],
- "stateMutability": "pure",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint8", "name": "role", "type": "uint8" },
- {
- "internalType": "RoleDescription",
- "name": "description",
- "type": "bytes32"
- }
- ],
- "name": "updateRoleDescription",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- }
-]
diff --git a/examples/factory-llama/ponder.config.ts b/examples/factory-llama/ponder.config.ts
index 437d1a4ef..8026a1048 100644
--- a/examples/factory-llama/ponder.config.ts
+++ b/examples/factory-llama/ponder.config.ts
@@ -1,15 +1,15 @@
-import type { Config } from "@ponder/core";
+import { createConfig } from "@ponder/core";
import { parseAbiItem } from "abitype";
import { http } from "viem";
-import LlamaCoreAbi from "./abis/LlamaCore.json";
-import LlamaPolicyAbi from "./abis/LlamaPolicy.json";
+import { LlamaCoreAbi } from "./abis/LlamaCore.abi";
+import { LlamaPolicyAbi } from "./abis/LlamaPolicy.abi";
const llamaFactoryEvent = parseAbiItem(
"event LlamaInstanceCreated(address indexed deployer, string indexed name, address llamaCore, address llamaExecutor, address llamaPolicy, uint256 chainId)"
);
-export const config: Config = {
+export const config = createConfig({
networks: [
{
name: "sepolia",
@@ -20,7 +20,7 @@ export const config: Config = {
contracts: [
{
name: "LlamaCore",
- network: "sepolia",
+ network: [{ name: "sepolia" }],
abi: LlamaCoreAbi,
factory: {
address: "0xFf5d4E226D9A3496EECE31083a8F493edd79AbEB",
@@ -31,7 +31,7 @@ export const config: Config = {
},
{
name: "LlamaPolicy",
- network: "sepolia",
+ network: [{ name: "sepolia" }],
abi: LlamaPolicyAbi,
factory: {
address: "0xFf5d4E226D9A3496EECE31083a8F493edd79AbEB",
@@ -41,4 +41,4 @@ export const config: Config = {
startBlock: 4121269,
},
],
-};
+});
diff --git a/examples/friendtech/abis/FriendtechSharesV1.abi.ts b/examples/friendtech/abis/FriendtechSharesV1.abi.ts
new file mode 100644
index 000000000..2f0a3a310
--- /dev/null
+++ b/examples/friendtech/abis/FriendtechSharesV1.abi.ts
@@ -0,0 +1,232 @@
+export const FriendtechSharesV1Abi = [
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "previousOwner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "newOwner",
+ type: "address",
+ },
+ ],
+ name: "OwnershipTransferred",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "address",
+ name: "trader",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "address",
+ name: "subject",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bool",
+ name: "isBuy",
+ type: "bool",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "shareAmount",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "ethAmount",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "protocolEthAmount",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "subjectEthAmount",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "supply",
+ type: "uint256",
+ },
+ ],
+ name: "Trade",
+ type: "event",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "sharesSubject", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "buyShares",
+ outputs: [],
+ stateMutability: "payable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "sharesSubject", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "getBuyPrice",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "sharesSubject", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "getBuyPriceAfterFee",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "supply", type: "uint256" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "getPrice",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "pure",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "sharesSubject", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "getSellPrice",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "sharesSubject", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "getSellPriceAfterFee",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "owner",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "protocolFeeDestination",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "protocolFeePercent",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "renounceOwnership",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "sharesSubject", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "sellShares",
+ outputs: [],
+ stateMutability: "payable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "address",
+ name: "_feeDestination",
+ type: "address",
+ },
+ ],
+ name: "setFeeDestination",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "_feePercent", type: "uint256" }],
+ name: "setProtocolFeePercent",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "_feePercent", type: "uint256" }],
+ name: "setSubjectFeePercent",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "", type: "address" },
+ { internalType: "address", name: "", type: "address" },
+ ],
+ name: "sharesBalance",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "", type: "address" }],
+ name: "sharesSupply",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "subjectFeePercent",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "newOwner", type: "address" }],
+ name: "transferOwnership",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+] as const;
diff --git a/examples/friendtech/abis/FriendtechSharesV1.json b/examples/friendtech/abis/FriendtechSharesV1.json
deleted file mode 100644
index e2058a4df..000000000
--- a/examples/friendtech/abis/FriendtechSharesV1.json
+++ /dev/null
@@ -1,238 +0,0 @@
-[
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "previousOwner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "OwnershipTransferred",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "address",
- "name": "trader",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "address",
- "name": "subject",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "isBuy",
- "type": "bool"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "shareAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "ethAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "protocolEthAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "subjectEthAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "supply",
- "type": "uint256"
- }
- ],
- "name": "Trade",
- "type": "event"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "sharesSubject", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "buyShares",
- "outputs": [],
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "sharesSubject", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "getBuyPrice",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "sharesSubject", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "getBuyPriceAfterFee",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "supply", "type": "uint256" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "getPrice",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "pure",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "sharesSubject", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "getSellPrice",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "sharesSubject", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "getSellPriceAfterFee",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "owner",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "protocolFeeDestination",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "protocolFeePercent",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "renounceOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "sharesSubject", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "sellShares",
- "outputs": [],
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "_feeDestination",
- "type": "address"
- }
- ],
- "name": "setFeeDestination",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_feePercent", "type": "uint256" }
- ],
- "name": "setProtocolFeePercent",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_feePercent", "type": "uint256" }
- ],
- "name": "setSubjectFeePercent",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "", "type": "address" },
- { "internalType": "address", "name": "", "type": "address" }
- ],
- "name": "sharesBalance",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "name": "sharesSupply",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "subjectFeePercent",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "newOwner", "type": "address" }
- ],
- "name": "transferOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- }
-]
diff --git a/examples/friendtech/ponder.config.ts b/examples/friendtech/ponder.config.ts
index a5dee2d96..aa910fca3 100644
--- a/examples/friendtech/ponder.config.ts
+++ b/examples/friendtech/ponder.config.ts
@@ -1,7 +1,9 @@
-import type { Config } from "@ponder/core";
+import { createConfig } from "@ponder/core";
import { http } from "viem";
-export const config: Config = {
+import { FriendtechSharesV1Abi } from "./abis/FriendtechSharesV1.abi";
+
+export const config = createConfig({
networks: [
{
name: "base",
@@ -12,11 +14,11 @@ export const config: Config = {
contracts: [
{
name: "FriendtechSharesV1",
- network: "base",
- abi: "./abis/FriendtechSharesV1.json",
+ network: [{ name: "base" }],
+ abi: FriendtechSharesV1Abi,
address: "0xcf205808ed36593aa40a44f10c7f7c2f67d4a4d4",
startBlock: 2430440,
maxBlockRange: 100,
},
],
-};
+});
diff --git a/examples/token-erc20/abis/AdventureGold.abi.ts b/examples/token-erc20/abis/AdventureGold.abi.ts
new file mode 100644
index 000000000..4d21f2474
--- /dev/null
+++ b/examples/token-erc20/abis/AdventureGold.abi.ts
@@ -0,0 +1,349 @@
+export const AdventureGoldAbi = [
+ { inputs: [], stateMutability: "nonpayable", type: "constructor" },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "spender",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "value",
+ type: "uint256",
+ },
+ ],
+ name: "Approval",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "previousOwner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "newOwner",
+ type: "address",
+ },
+ ],
+ name: "OwnershipTransferred",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "from",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "to",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "value",
+ type: "uint256",
+ },
+ ],
+ name: "Transfer",
+ type: "event",
+ },
+ {
+ inputs: [],
+ name: "adventureGoldPerTokenId",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "owner", type: "address" },
+ { internalType: "address", name: "spender", type: "address" },
+ ],
+ name: "allowance",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "spender", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "approve",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "account", type: "address" }],
+ name: "balanceOf",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "claimAllForOwner",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }],
+ name: "claimById",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "uint256",
+ name: "ownerIndexStart",
+ type: "uint256",
+ },
+ { internalType: "uint256", name: "ownerIndexEnd", type: "uint256" },
+ ],
+ name: "claimRangeForOwner",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "uint256",
+ name: "amountDisplayValue",
+ type: "uint256",
+ },
+ ],
+ name: "daoMint",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "uint256",
+ name: "adventureGoldDisplayValue",
+ type: "uint256",
+ },
+ ],
+ name: "daoSetAdventureGoldPerTokenId",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ {
+ internalType: "address",
+ name: "lootContractAddress_",
+ type: "address",
+ },
+ ],
+ name: "daoSetLootContractAddress",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "season_", type: "uint256" }],
+ name: "daoSetSeason",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "season_", type: "uint256" },
+ {
+ internalType: "uint256",
+ name: "adventureGoldDisplayValue",
+ type: "uint256",
+ },
+ ],
+ name: "daoSetSeasonAndAdventureGoldPerTokenID",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "tokenIdStart_", type: "uint256" },
+ { internalType: "uint256", name: "tokenIdEnd_", type: "uint256" },
+ ],
+ name: "daoSetTokenIdRange",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "decimals",
+ outputs: [{ internalType: "uint8", name: "", type: "uint8" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "spender", type: "address" },
+ {
+ internalType: "uint256",
+ name: "subtractedValue",
+ type: "uint256",
+ },
+ ],
+ name: "decreaseAllowance",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "spender", type: "address" },
+ { internalType: "uint256", name: "addedValue", type: "uint256" },
+ ],
+ name: "increaseAllowance",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "lootContract",
+ outputs: [
+ {
+ internalType: "contract IERC721Enumerable",
+ name: "",
+ type: "address",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "lootContractAddress",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "name",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "owner",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "renounceOwnership",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "season",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "", type: "uint256" },
+ { internalType: "uint256", name: "", type: "uint256" },
+ ],
+ name: "seasonClaimedByTokenId",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "symbol",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "tokenIdEnd",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "tokenIdStart",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "totalSupply",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "recipient", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "transfer",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "sender", type: "address" },
+ { internalType: "address", name: "recipient", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "transferFrom",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "newOwner", type: "address" }],
+ name: "transferOwnership",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+] as const;
diff --git a/examples/token-erc20/abis/AdventureGold.json b/examples/token-erc20/abis/AdventureGold.json
deleted file mode 100644
index 95231479d..000000000
--- a/examples/token-erc20/abis/AdventureGold.json
+++ /dev/null
@@ -1,357 +0,0 @@
-[
- { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "spender",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "value",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "previousOwner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "OwnershipTransferred",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "value",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "inputs": [],
- "name": "adventureGoldPerTokenId",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" },
- { "internalType": "address", "name": "spender", "type": "address" }
- ],
- "name": "allowance",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "spender", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "approve",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "account", "type": "address" }
- ],
- "name": "balanceOf",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "claimAllForOwner",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "claimById",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "ownerIndexStart",
- "type": "uint256"
- },
- { "internalType": "uint256", "name": "ownerIndexEnd", "type": "uint256" }
- ],
- "name": "claimRangeForOwner",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "amountDisplayValue",
- "type": "uint256"
- }
- ],
- "name": "daoMint",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "adventureGoldDisplayValue",
- "type": "uint256"
- }
- ],
- "name": "daoSetAdventureGoldPerTokenId",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "lootContractAddress_",
- "type": "address"
- }
- ],
- "name": "daoSetLootContractAddress",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "season_", "type": "uint256" }
- ],
- "name": "daoSetSeason",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "season_", "type": "uint256" },
- {
- "internalType": "uint256",
- "name": "adventureGoldDisplayValue",
- "type": "uint256"
- }
- ],
- "name": "daoSetSeasonAndAdventureGoldPerTokenID",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "tokenIdStart_", "type": "uint256" },
- { "internalType": "uint256", "name": "tokenIdEnd_", "type": "uint256" }
- ],
- "name": "daoSetTokenIdRange",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "decimals",
- "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "spender", "type": "address" },
- {
- "internalType": "uint256",
- "name": "subtractedValue",
- "type": "uint256"
- }
- ],
- "name": "decreaseAllowance",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "spender", "type": "address" },
- { "internalType": "uint256", "name": "addedValue", "type": "uint256" }
- ],
- "name": "increaseAllowance",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "lootContract",
- "outputs": [
- {
- "internalType": "contract IERC721Enumerable",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "lootContractAddress",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "name",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "owner",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "renounceOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "season",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "", "type": "uint256" },
- { "internalType": "uint256", "name": "", "type": "uint256" }
- ],
- "name": "seasonClaimedByTokenId",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "symbol",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "tokenIdEnd",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "tokenIdStart",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "totalSupply",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "recipient", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "transfer",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "sender", "type": "address" },
- { "internalType": "address", "name": "recipient", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "transferFrom",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "newOwner", "type": "address" }
- ],
- "name": "transferOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- }
-]
diff --git a/examples/token-erc20/ponder.config.ts b/examples/token-erc20/ponder.config.ts
index a3609e439..a15524575 100644
--- a/examples/token-erc20/ponder.config.ts
+++ b/examples/token-erc20/ponder.config.ts
@@ -1,7 +1,9 @@
-import type { Config } from "@ponder/core";
+import { createConfig } from "@ponder/core";
import { http } from "viem";
-export const config: Config = {
+import { AdventureGoldAbi } from "./abis/AdventureGold.abi";
+
+export const config = createConfig({
networks: [
{
name: "mainnet",
@@ -12,11 +14,11 @@ export const config: Config = {
contracts: [
{
name: "AdventureGold",
- network: "mainnet",
- abi: "./abis/AdventureGold.json",
+ network: [{ name: "mainnet" }],
+ abi: AdventureGoldAbi,
address: "0x32353A6C91143bfd6C7d363B546e62a9A2489A20",
startBlock: 13142655,
endBlock: 13150000,
},
],
-};
+});
diff --git a/examples/token-erc721/abis/SmolBrain.abi.ts b/examples/token-erc721/abis/SmolBrain.abi.ts
new file mode 100644
index 000000000..4d4c8cc62
--- /dev/null
+++ b/examples/token-erc721/abis/SmolBrain.abi.ts
@@ -0,0 +1,597 @@
+export const SmolBrainAbi = [
+ {
+ inputs: [
+ { internalType: "address", name: "_luckyWinner", type: "address" },
+ ],
+ stateMutability: "nonpayable",
+ type: "constructor",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "approved",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "tokenId",
+ type: "uint256",
+ },
+ ],
+ name: "Approval",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "operator",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "bool",
+ name: "approved",
+ type: "bool",
+ },
+ ],
+ name: "ApprovalForAll",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "brainMaxLevel",
+ type: "uint256",
+ },
+ ],
+ name: "LandMaxLevel",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "address",
+ name: "land",
+ type: "address",
+ },
+ ],
+ name: "LandSet",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "levelIQCost",
+ type: "uint256",
+ },
+ ],
+ name: "LevelIQCost",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "bytes32",
+ name: "role",
+ type: "bytes32",
+ },
+ {
+ indexed: true,
+ internalType: "bytes32",
+ name: "previousAdminRole",
+ type: "bytes32",
+ },
+ {
+ indexed: true,
+ internalType: "bytes32",
+ name: "newAdminRole",
+ type: "bytes32",
+ },
+ ],
+ name: "RoleAdminChanged",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "bytes32",
+ name: "role",
+ type: "bytes32",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "account",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "sender",
+ type: "address",
+ },
+ ],
+ name: "RoleGranted",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "bytes32",
+ name: "role",
+ type: "bytes32",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "account",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "sender",
+ type: "address",
+ },
+ ],
+ name: "RoleRevoked",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "address",
+ name: "school",
+ type: "address",
+ },
+ ],
+ name: "SchoolSet",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: "address",
+ name: "to",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "tokenId",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "enum SmolBrain.Gender",
+ name: "gender",
+ type: "uint8",
+ },
+ ],
+ name: "SmolBrainMint",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "from",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "to",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "uint256",
+ name: "tokenId",
+ type: "uint256",
+ },
+ ],
+ name: "Transfer",
+ type: "event",
+ },
+ {
+ inputs: [],
+ name: "DEFAULT_ADMIN_ROLE",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "SMOLBRAIN_MINTER_ROLE",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "SMOLBRAIN_OWNER_ROLE",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "tokenId", type: "uint256" },
+ ],
+ name: "approve",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "averageIQ",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "owner", type: "address" }],
+ name: "balanceOf",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "baseURI",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "brainMaxLevel",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ name: "brainz",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }],
+ name: "getApproved",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "_tokenId", type: "uint256" }],
+ name: "getGender",
+ outputs: [
+ { internalType: "enum SmolBrain.Gender", name: "", type: "uint8" },
+ ],
+ stateMutability: "pure",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "bytes32", name: "role", type: "bytes32" }],
+ name: "getRoleAdmin",
+ outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "_minter", type: "address" }],
+ name: "grantMinter",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "_owner", type: "address" }],
+ name: "grantOwner",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "bytes32", name: "role", type: "bytes32" },
+ { internalType: "address", name: "account", type: "address" },
+ ],
+ name: "grantRole",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "bytes32", name: "role", type: "bytes32" },
+ { internalType: "address", name: "account", type: "address" },
+ ],
+ name: "hasRole",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "owner", type: "address" },
+ { internalType: "address", name: "operator", type: "address" },
+ ],
+ name: "isApprovedForAll",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "_minter", type: "address" }],
+ name: "isMinter",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "_owner", type: "address" }],
+ name: "isOwner",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "land",
+ outputs: [{ internalType: "contract Land", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "levelIQCost",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "_to", type: "address" }],
+ name: "mintFemale",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "_to", type: "address" }],
+ name: "mintMale",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "name",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "tokenId", type: "uint256" }],
+ name: "ownerOf",
+ outputs: [{ internalType: "address", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "bytes32", name: "role", type: "bytes32" },
+ { internalType: "address", name: "account", type: "address" },
+ ],
+ name: "renounceRole",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "bytes32", name: "role", type: "bytes32" },
+ { internalType: "address", name: "account", type: "address" },
+ ],
+ name: "revokeRole",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "tokenId", type: "uint256" },
+ ],
+ name: "safeTransferFrom",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "tokenId", type: "uint256" },
+ { internalType: "bytes", name: "_data", type: "bytes" },
+ ],
+ name: "safeTransferFrom",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "_tokenId", type: "uint256" }],
+ name: "scanBrain",
+ outputs: [{ internalType: "uint256", name: "IQ", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "school",
+ outputs: [{ internalType: "contract School", name: "", type: "address" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "_tokenId", type: "uint256" },
+ { internalType: "uint256", name: "_iqEarned", type: "uint256" },
+ ],
+ name: "schoolDrop",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "operator", type: "address" },
+ { internalType: "bool", name: "approved", type: "bool" },
+ ],
+ name: "setApprovalForAll",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "string", name: "_baseURItoSet", type: "string" }],
+ name: "setBaseURI",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "_land", type: "address" }],
+ name: "setLand",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "_levelIQCost", type: "uint256" },
+ ],
+ name: "setLevelIQCost",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "_brainMaxLevel", type: "uint256" },
+ ],
+ name: "setMaxLevel",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "_school", type: "address" }],
+ name: "setSchool",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "bytes4", name: "interfaceId", type: "bytes4" }],
+ name: "supportsInterface",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "symbol",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "index", type: "uint256" }],
+ name: "tokenByIndex",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "owner", type: "address" },
+ { internalType: "uint256", name: "index", type: "uint256" },
+ ],
+ name: "tokenOfOwnerByIndex",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "_tokenId", type: "uint256" }],
+ name: "tokenURI",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "totalSupply",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "from", type: "address" },
+ { internalType: "address", name: "to", type: "address" },
+ { internalType: "uint256", name: "tokenId", type: "uint256" },
+ ],
+ name: "transferFrom",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+] as const;
diff --git a/examples/token-erc721/abis/SmolBrain.json b/examples/token-erc721/abis/SmolBrain.json
deleted file mode 100644
index 7524d4896..000000000
--- a/examples/token-erc721/abis/SmolBrain.json
+++ /dev/null
@@ -1,633 +0,0 @@
-[
- {
- "inputs": [
- { "internalType": "address", "name": "_luckyWinner", "type": "address" }
- ],
- "stateMutability": "nonpayable",
- "type": "constructor"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "approved",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "operator",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "bool",
- "name": "approved",
- "type": "bool"
- }
- ],
- "name": "ApprovalForAll",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "brainMaxLevel",
- "type": "uint256"
- }
- ],
- "name": "LandMaxLevel",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "address",
- "name": "land",
- "type": "address"
- }
- ],
- "name": "LandSet",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "levelIQCost",
- "type": "uint256"
- }
- ],
- "name": "LevelIQCost",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "bytes32",
- "name": "role",
- "type": "bytes32"
- },
- {
- "indexed": true,
- "internalType": "bytes32",
- "name": "previousAdminRole",
- "type": "bytes32"
- },
- {
- "indexed": true,
- "internalType": "bytes32",
- "name": "newAdminRole",
- "type": "bytes32"
- }
- ],
- "name": "RoleAdminChanged",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "bytes32",
- "name": "role",
- "type": "bytes32"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "account",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "sender",
- "type": "address"
- }
- ],
- "name": "RoleGranted",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "bytes32",
- "name": "role",
- "type": "bytes32"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "account",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "sender",
- "type": "address"
- }
- ],
- "name": "RoleRevoked",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "address",
- "name": "school",
- "type": "address"
- }
- ],
- "name": "SchoolSet",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": false,
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "enum SmolBrain.Gender",
- "name": "gender",
- "type": "uint8"
- }
- ],
- "name": "SmolBrainMint",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "tokenId",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "inputs": [],
- "name": "DEFAULT_ADMIN_ROLE",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "SMOLBRAIN_MINTER_ROLE",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "SMOLBRAIN_OWNER_ROLE",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "approve",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "averageIQ",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" }
- ],
- "name": "balanceOf",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "baseURI",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "brainMaxLevel",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "name": "brainz",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "getApproved",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_tokenId", "type": "uint256" }
- ],
- "name": "getGender",
- "outputs": [
- { "internalType": "enum SmolBrain.Gender", "name": "", "type": "uint8" }
- ],
- "stateMutability": "pure",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes32", "name": "role", "type": "bytes32" }
- ],
- "name": "getRoleAdmin",
- "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "_minter", "type": "address" }
- ],
- "name": "grantMinter",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "_owner", "type": "address" }
- ],
- "name": "grantOwner",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes32", "name": "role", "type": "bytes32" },
- { "internalType": "address", "name": "account", "type": "address" }
- ],
- "name": "grantRole",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes32", "name": "role", "type": "bytes32" },
- { "internalType": "address", "name": "account", "type": "address" }
- ],
- "name": "hasRole",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" },
- { "internalType": "address", "name": "operator", "type": "address" }
- ],
- "name": "isApprovedForAll",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "_minter", "type": "address" }
- ],
- "name": "isMinter",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "_owner", "type": "address" }
- ],
- "name": "isOwner",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "land",
- "outputs": [
- { "internalType": "contract Land", "name": "", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "levelIQCost",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "_to", "type": "address" }],
- "name": "mintFemale",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "_to", "type": "address" }],
- "name": "mintMale",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "name",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "ownerOf",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes32", "name": "role", "type": "bytes32" },
- { "internalType": "address", "name": "account", "type": "address" }
- ],
- "name": "renounceRole",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes32", "name": "role", "type": "bytes32" },
- { "internalType": "address", "name": "account", "type": "address" }
- ],
- "name": "revokeRole",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
- { "internalType": "bytes", "name": "_data", "type": "bytes" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_tokenId", "type": "uint256" }
- ],
- "name": "scanBrain",
- "outputs": [{ "internalType": "uint256", "name": "IQ", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "school",
- "outputs": [
- { "internalType": "contract School", "name": "", "type": "address" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_tokenId", "type": "uint256" },
- { "internalType": "uint256", "name": "_iqEarned", "type": "uint256" }
- ],
- "name": "schoolDrop",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "operator", "type": "address" },
- { "internalType": "bool", "name": "approved", "type": "bool" }
- ],
- "name": "setApprovalForAll",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "string", "name": "_baseURItoSet", "type": "string" }
- ],
- "name": "setBaseURI",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "_land", "type": "address" }
- ],
- "name": "setLand",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_levelIQCost", "type": "uint256" }
- ],
- "name": "setLevelIQCost",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_brainMaxLevel", "type": "uint256" }
- ],
- "name": "setMaxLevel",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "_school", "type": "address" }
- ],
- "name": "setSchool",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }
- ],
- "name": "supportsInterface",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "symbol",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "index", "type": "uint256" }
- ],
- "name": "tokenByIndex",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" },
- { "internalType": "uint256", "name": "index", "type": "uint256" }
- ],
- "name": "tokenOfOwnerByIndex",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_tokenId", "type": "uint256" }
- ],
- "name": "tokenURI",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "totalSupply",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "transferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- }
-]
diff --git a/examples/token-erc721/ponder.config.ts b/examples/token-erc721/ponder.config.ts
index 4c35e9de2..bcfaa1392 100644
--- a/examples/token-erc721/ponder.config.ts
+++ b/examples/token-erc721/ponder.config.ts
@@ -1,7 +1,9 @@
-import type { Config } from "@ponder/core";
+import { createConfig } from "@ponder/core";
import { http } from "viem";
-export const config: Config = {
+import { SmolBrainAbi } from "./abis/SmolBrain.abi";
+
+export const config = createConfig({
networks: [
{
name: "arbitrum",
@@ -12,10 +14,10 @@ export const config: Config = {
contracts: [
{
name: "SmolBrain",
- network: "arbitrum",
- abi: "./abis/SmolBrain.json",
+ network: [{ name: "arbitrum" }],
+ abi: SmolBrainAbi,
address: "0x6325439389E0797Ab35752B4F43a14C004f22A9c",
startBlock: 3163146,
},
],
-};
+});
diff --git a/examples/token-reth/abis/RocketTokenRETH.abi.ts b/examples/token-reth/abis/RocketTokenRETH.abi.ts
new file mode 100644
index 000000000..b3c75d22e
--- /dev/null
+++ b/examples/token-reth/abis/RocketTokenRETH.abi.ts
@@ -0,0 +1,324 @@
+export const RocketTokenRETHAbi = [
+ {
+ inputs: [
+ {
+ internalType: "contract RocketStorageInterface",
+ name: "_rocketStorageAddress",
+ type: "address",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "constructor",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "spender",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "value",
+ type: "uint256",
+ },
+ ],
+ name: "Approval",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "from",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "amount",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "time",
+ type: "uint256",
+ },
+ ],
+ name: "EtherDeposited",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "from",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "amount",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "ethAmount",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "time",
+ type: "uint256",
+ },
+ ],
+ name: "TokensBurned",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "to",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "amount",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "ethAmount",
+ type: "uint256",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "time",
+ type: "uint256",
+ },
+ ],
+ name: "TokensMinted",
+ type: "event",
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: "address",
+ name: "from",
+ type: "address",
+ },
+ {
+ indexed: true,
+ internalType: "address",
+ name: "to",
+ type: "address",
+ },
+ {
+ indexed: false,
+ internalType: "uint256",
+ name: "value",
+ type: "uint256",
+ },
+ ],
+ name: "Transfer",
+ type: "event",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "owner", type: "address" },
+ { internalType: "address", name: "spender", type: "address" },
+ ],
+ name: "allowance",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "spender", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "approve",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "address", name: "account", type: "address" }],
+ name: "balanceOf",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "_rethAmount", type: "uint256" }],
+ name: "burn",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "decimals",
+ outputs: [{ internalType: "uint8", name: "", type: "uint8" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "spender", type: "address" },
+ {
+ internalType: "uint256",
+ name: "subtractedValue",
+ type: "uint256",
+ },
+ ],
+ name: "decreaseAllowance",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "depositExcess",
+ outputs: [],
+ stateMutability: "payable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "depositExcessCollateral",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "getCollateralRate",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "_rethAmount", type: "uint256" }],
+ name: "getEthValue",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "getExchangeRate",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [{ internalType: "uint256", name: "_ethAmount", type: "uint256" }],
+ name: "getRethValue",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "getTotalCollateral",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "spender", type: "address" },
+ { internalType: "uint256", name: "addedValue", type: "uint256" },
+ ],
+ name: "increaseAllowance",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "uint256", name: "_ethAmount", type: "uint256" },
+ { internalType: "address", name: "_to", type: "address" },
+ ],
+ name: "mint",
+ outputs: [],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "name",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "symbol",
+ outputs: [{ internalType: "string", name: "", type: "string" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "totalSupply",
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "recipient", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "transfer",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [
+ { internalType: "address", name: "sender", type: "address" },
+ { internalType: "address", name: "recipient", type: "address" },
+ { internalType: "uint256", name: "amount", type: "uint256" },
+ ],
+ name: "transferFrom",
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
+ stateMutability: "nonpayable",
+ type: "function",
+ },
+ {
+ inputs: [],
+ name: "version",
+ outputs: [{ internalType: "uint8", name: "", type: "uint8" }],
+ stateMutability: "view",
+ type: "function",
+ },
+ { stateMutability: "payable", type: "receive" },
+] as const;
diff --git a/examples/token-reth/abis/RocketTokenRETH.json b/examples/token-reth/abis/RocketTokenRETH.json
deleted file mode 100644
index c2094f2c2..000000000
--- a/examples/token-reth/abis/RocketTokenRETH.json
+++ /dev/null
@@ -1,332 +0,0 @@
-[
- {
- "inputs": [
- {
- "internalType": "contract RocketStorageInterface",
- "name": "_rocketStorageAddress",
- "type": "address"
- }
- ],
- "stateMutability": "nonpayable",
- "type": "constructor"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "spender",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "value",
- "type": "uint256"
- }
- ],
- "name": "Approval",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "amount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "time",
- "type": "uint256"
- }
- ],
- "name": "EtherDeposited",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "amount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "ethAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "time",
- "type": "uint256"
- }
- ],
- "name": "TokensBurned",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "amount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "ethAmount",
- "type": "uint256"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "time",
- "type": "uint256"
- }
- ],
- "name": "TokensMinted",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "from",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "value",
- "type": "uint256"
- }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" },
- { "internalType": "address", "name": "spender", "type": "address" }
- ],
- "name": "allowance",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "spender", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "approve",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "account", "type": "address" }
- ],
- "name": "balanceOf",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_rethAmount", "type": "uint256" }
- ],
- "name": "burn",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "decimals",
- "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "spender", "type": "address" },
- {
- "internalType": "uint256",
- "name": "subtractedValue",
- "type": "uint256"
- }
- ],
- "name": "decreaseAllowance",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "depositExcess",
- "outputs": [],
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "depositExcessCollateral",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "getCollateralRate",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_rethAmount", "type": "uint256" }
- ],
- "name": "getEthValue",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "getExchangeRate",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_ethAmount", "type": "uint256" }
- ],
- "name": "getRethValue",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "getTotalCollateral",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "spender", "type": "address" },
- { "internalType": "uint256", "name": "addedValue", "type": "uint256" }
- ],
- "name": "increaseAllowance",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "_ethAmount", "type": "uint256" },
- { "internalType": "address", "name": "_to", "type": "address" }
- ],
- "name": "mint",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "name",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "symbol",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "totalSupply",
- "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "recipient", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "transfer",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "sender", "type": "address" },
- { "internalType": "address", "name": "recipient", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "transferFrom",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "version",
- "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
- "stateMutability": "view",
- "type": "function"
- },
- { "stateMutability": "payable", "type": "receive" }
-]
diff --git a/examples/token-reth/ponder.config.ts b/examples/token-reth/ponder.config.ts
index d5b306780..6459fc25e 100644
--- a/examples/token-reth/ponder.config.ts
+++ b/examples/token-reth/ponder.config.ts
@@ -1,7 +1,9 @@
-import type { Config } from "@ponder/core";
+import { createConfig } from "@ponder/core";
import { http } from "viem";
-export const config: Config = {
+import { RocketTokenRETHAbi } from "./abis/RocketTokenRETH.abi";
+
+export const config = createConfig({
networks: [
{
name: "mainnet",
@@ -13,10 +15,10 @@ export const config: Config = {
contracts: [
{
name: "RocketTokenRETH",
- network: "mainnet",
- abi: "./abis/RocketTokenRETH.json",
+ network: [{ name: "mainnet" }],
+ abi: RocketTokenRETHAbi,
address: "0xae78736cd615f374d3085123a210448e74fc6393",
startBlock: 13325304,
},
],
-};
+});
From 99e62d18c403c9cad816b413fefcf28e365ef1c1 Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 13:51:20 -0400
Subject: [PATCH 16/44] forgot one
---
examples/art-gobblers/ponder.config.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/examples/art-gobblers/ponder.config.ts b/examples/art-gobblers/ponder.config.ts
index d5fd61dbf..07f747069 100644
--- a/examples/art-gobblers/ponder.config.ts
+++ b/examples/art-gobblers/ponder.config.ts
@@ -1,7 +1,7 @@
import { createConfig } from "@ponder/core";
import { http } from "viem";
-import { ArtGobblersAbi } from "./ArtGobblers.abi";
+import { ArtGobblersAbi } from "./abis/ArtGobblers.abi";
export const config = createConfig({
networks: [
From 3d7aa6d7218c9545588116c26402b88c91bce672 Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 16:38:42 -0400
Subject: [PATCH 17/44] config types
---
packages/core/src/config/config.test-d.ts | 111 ++++++++++++++++++++++
packages/core/src/config/config.ts | 45 ++++++++-
2 files changed, 155 insertions(+), 1 deletion(-)
create mode 100644 packages/core/src/config/config.test-d.ts
diff --git a/packages/core/src/config/config.test-d.ts b/packages/core/src/config/config.test-d.ts
new file mode 100644
index 000000000..f39ba7d01
--- /dev/null
+++ b/packages/core/src/config/config.test-d.ts
@@ -0,0 +1,111 @@
+import { assertType, test } from "vitest";
+
+import { FilterElement, FilterEvents, SafeEventNames } from "./config";
+
+const abiSimple = [
+ {
+ inputs: [],
+ stateMutability: "nonpayable",
+ type: "constructor",
+ },
+ {
+ inputs: [
+ {
+ indexed: true,
+ type: "address",
+ },
+ {
+ indexed: true,
+ type: "address",
+ },
+ {
+ indexed: false,
+ type: "uint256",
+ },
+ ],
+ name: "Approve",
+ type: "event",
+ },
+ {
+ inputs: [
+ {
+ indexed: true,
+ type: "address",
+ },
+ {
+ indexed: true,
+ type: "address",
+ },
+ {
+ indexed: false,
+ type: "uint256",
+ },
+ ],
+ name: "Transfer",
+ type: "event",
+ },
+] as const;
+
+const abiWithSameEvent = [
+ ...abiSimple,
+ {
+ inputs: [],
+ stateMutability: "nonpayable",
+ type: "constructor",
+ },
+ {
+ inputs: [
+ {
+ indexed: true,
+ type: "address",
+ },
+ {
+ indexed: true,
+ type: "bytes32",
+ },
+ {
+ indexed: false,
+ type: "uint256",
+ },
+ ],
+ name: "Approve",
+ type: "event",
+ },
+] as const;
+
+test("filter events", () => {
+ type t = FilterEvents;
+ // ^?
+
+ assertType([
+ abiWithSameEvent[1],
+ abiWithSameEvent[2],
+ abiWithSameEvent[4],
+ ] as const);
+});
+
+test("filter elements", () => {
+ type a = FilterElement<"a", readonly ["a", "b", "c"]>;
+ // ^?
+ assertType(["b", "c"] as const);
+});
+
+test("safe event names", () => {
+ type a = SafeEventNames<
+ // ^?
+ FilterEvents,
+ FilterEvents
+ >;
+ assertType(["Approve", "Transfer"] as const);
+
+ type b = SafeEventNames<
+ // ^?
+ FilterEvents,
+ FilterEvents
+ >;
+ assertType([
+ "Approve(address indexed, address indexed, uint256)",
+ "Transfer",
+ "Approve(address indexed, bytes32 indexed, uint256)",
+ ]);
+});
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index 1946ccfa9..bd0bc9bad 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -1,6 +1,49 @@
-import type { Abi, AbiEvent } from "abitype";
+import type { Abi, AbiEvent, FormatAbiItem } from "abitype";
import type { Transport } from "viem";
+/**
+ * Keep only AbiEvents from an Abi
+ */
+export type FilterEvents = T extends readonly [
+ infer First,
+ ...infer Rest extends Abi
+]
+ ? First extends AbiEvent
+ ? readonly [First, ...FilterEvents]
+ : FilterEvents
+ : [];
+
+/**
+ * Remove TElement from TArr
+ */
+export type FilterElement<
+ TElement,
+ TArr extends readonly unknown[]
+> = TArr extends readonly [infer First, ...infer Rest]
+ ? TElement extends First
+ ? FilterElement
+ : readonly [First, ...FilterElement]
+ : [];
+
+/**
+ * Return an array of safe event names that handle multiple events with the same name
+ */
+export type SafeEventNames<
+ TAbi extends readonly AbiEvent[],
+ TArr extends readonly AbiEvent[]
+> = TAbi extends readonly [
+ infer First extends AbiEvent,
+ ...infer Rest extends readonly AbiEvent[]
+]
+ ? First["name"] extends FilterElement[number]["name"]
+ ? // Name collisions exist, format long name
+ FormatAbiItem extends `event ${infer LongEvent extends string}`
+ ? readonly [LongEvent, ...SafeEventNames]
+ : never
+ : // Short name
+ readonly [First["name"], ...SafeEventNames]
+ : [];
+
type ContractRequired<
TNetworkName extends string | unknown = string | unknown
> = {
From 2468e9bb056d6002d0be40cada0af4960f05ef4d Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 17:46:45 -0400
Subject: [PATCH 18/44] barely workingish
---
packages/core/src/config/config.test-d.ts | 18 ++-
packages/core/src/config/config.test.ts | 21 ++++
packages/core/src/config/config.ts | 140 +++++++++++++---------
3 files changed, 120 insertions(+), 59 deletions(-)
diff --git a/packages/core/src/config/config.test-d.ts b/packages/core/src/config/config.test-d.ts
index f39ba7d01..b9b91536d 100644
--- a/packages/core/src/config/config.test-d.ts
+++ b/packages/core/src/config/config.test-d.ts
@@ -1,8 +1,13 @@
import { assertType, test } from "vitest";
-import { FilterElement, FilterEvents, SafeEventNames } from "./config";
+import {
+ ContractFilter,
+ FilterElement,
+ FilterEvents,
+ SafeEventNames,
+} from "./config";
-const abiSimple = [
+export const abiSimple = [
{
inputs: [],
stateMutability: "nonpayable",
@@ -46,7 +51,7 @@ const abiSimple = [
},
] as const;
-const abiWithSameEvent = [
+export const abiWithSameEvent = [
...abiSimple,
{
inputs: [],
@@ -109,3 +114,10 @@ test("safe event names", () => {
"Approve(address indexed, bytes32 indexed, uint256)",
]);
});
+
+test("infer event names from abi", () => {
+ type a = ContractFilter["event"];
+ // ^?
+
+ assertType([] as readonly ("Approve" | "Transfer")[] | undefined);
+});
diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts
index 9d66e85ca..3a655b73b 100644
--- a/packages/core/src/config/config.test.ts
+++ b/packages/core/src/config/config.test.ts
@@ -2,6 +2,7 @@ import { http } from "viem";
import { test } from "vitest";
import { createConfig } from "./config";
+import { abiSimple } from "./config.test-d";
test("createConfig enforces matching network names", () => {
createConfig({
@@ -21,3 +22,23 @@ test("createConfig enforces matching network names", () => {
],
});
});
+
+test("createConfig() has strict events inferred from abi", () => {
+ createConfig({
+ networks: [
+ { name: "mainnet", chainId: 1, transport: http("http://127.0.0.1:8545") },
+ ],
+ contracts: [
+ {
+ name: "BaseRegistrarImplementation",
+ network: [{ name: "mainnet" }],
+ abi: abiSimple,
+ event: ["Transfer", "Approve"],
+ address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
+ startBlock: 16370000,
+ endBlock: 16370020,
+ maxBlockRange: 10,
+ },
+ ],
+ });
+});
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index bd0bc9bad..4b1edd08d 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -4,7 +4,7 @@ import type { Transport } from "viem";
/**
* Keep only AbiEvents from an Abi
*/
-export type FilterEvents = T extends readonly [
+export type FilterEvents = T extends readonly [
infer First,
...infer Rest extends Abi
]
@@ -18,7 +18,7 @@ export type FilterEvents = T extends readonly [
*/
export type FilterElement<
TElement,
- TArr extends readonly unknown[]
+ TArr extends readonly unknown[] | unknown
> = TArr extends readonly [infer First, ...infer Rest]
? TElement extends First
? FilterElement
@@ -29,8 +29,8 @@ export type FilterElement<
* Return an array of safe event names that handle multiple events with the same name
*/
export type SafeEventNames<
- TAbi extends readonly AbiEvent[],
- TArr extends readonly AbiEvent[]
+ TAbi extends readonly AbiEvent[] | unknown,
+ TArr extends readonly AbiEvent[] | unknown
> = TAbi extends readonly [
infer First extends AbiEvent,
...infer Rest extends readonly AbiEvent[]
@@ -45,7 +45,8 @@ export type SafeEventNames<
: [];
type ContractRequired<
- TNetworkName extends string | unknown = string | unknown
+ TNetworkNames extends string | unknown = string | unknown,
+ TAbi extends Abi | unknown = Abi | unknown
> = {
/** Contract name. Must be unique across `contracts` and `filters`. */
name: string;
@@ -53,11 +54,11 @@ type ContractRequired<
* Network that this contract is deployed to. Must match a network name in `networks`.
* Any filter information overrides the values in the higher level "contracts" property. Factories cannot override an address and vice versa.
*/
- network: readonly ({ name: TNetworkName } & Partial)[];
+ network: readonly ({ name: TNetworkNames } & Partial>)[];
abi: Abi;
};
-type ContractFilter = (
+export type ContractFilter = (
| {
/** Contract address. */
address?: `0x${string}`;
@@ -81,69 +82,89 @@ type ContractFilter = (
/** Maximum block range to use when calling `eth_getLogs`. Default: `10_000`. */
maxBlockRange?: number;
- event?:
- | {
- signature: AbiEvent;
- args: any[];
- }
- | AbiEvent[];
+ event?: readonly SafeEventNames<
+ FilterEvents,
+ FilterEvents
+ >[number][];
+};
+
+type Database =
+ | {
+ kind: "sqlite";
+ /** Path to SQLite database file. Default: `"./.ponder/cache.db"`. */
+ filename?: string;
+ }
+ | {
+ kind: "postgres";
+ /** PostgreSQL database connection string. Default: `process.env.DATABASE_URL`. */
+ connectionString?: string;
+ };
+
+type Network = {
+ /** Network name. Must be unique across all networks. */
+ name: string;
+ /** Chain ID of the network. */
+ chainId: number;
+ /** A viem `http`, `webSocket`, or `fallback` [Transport](https://viem.sh/docs/clients/transports/http.html).
+ *
+ * __To avoid rate limiting, include a custom RPC URL.__ Usage:
+ *
+ * ```ts
+ * import { http } from "viem";
+ *
+ * const network = {
+ * name: "mainnet",
+ * chainId: 1,
+ * transport: http("https://eth-mainnet.g.alchemy.com/v2/..."),
+ * }
+ * ```
+ */
+ transport: Transport;
+ /** Polling frequency (in ms). Default: `1_000`. */
+ pollingInterval?: number;
+ /** Maximum concurrency of RPC requests during the historical sync. Default: `10`. */
+ maxRpcRequestConcurrency?: number;
+};
+
+type Contract<
+ TNetworkNames extends string | unknown = string | unknown,
+ TAbi extends Abi | unknown = Abi | unknown
+> = ContractRequired & ContractFilter;
+
+type Option = {
+ /** Maximum number of seconds to wait for event processing to be complete before responding as healthy. If event processing exceeds this duration, the API may serve incomplete data. Default: `240` (4 minutes). */
+ maxHealthcheckDuration?: number;
};
export type ResolvedConfig<
- TNetworkName extends string | unknown = string | unknown
+ TNetworkNames extends string | unknown = string | unknown,
+ TAbi extends Abi = Abi
> = {
/** Database to use for storing blockchain & entity data. Default: `"postgres"` if `DATABASE_URL` env var is present, otherwise `"sqlite"`. */
- database?:
- | {
- kind: "sqlite";
- /** Path to SQLite database file. Default: `"./.ponder/cache.db"`. */
- filename?: string;
- }
- | {
- kind: "postgres";
- /** PostgreSQL database connection string. Default: `process.env.DATABASE_URL`. */
- connectionString?: string;
- };
+ database?: Database;
/** List of blockchain networks. */
- networks: readonly {
- /** Network name. Must be unique across all networks. */
- name: string;
- /** Chain ID of the network. */
- chainId: number;
- /** A viem `http`, `webSocket`, or `fallback` [Transport](https://viem.sh/docs/clients/transports/http.html).
- *
- * __To avoid rate limiting, include a custom RPC URL.__ Usage:
- *
- * ```ts
- * import { http } from "viem";
- *
- * const network = {
- * name: "mainnet",
- * chainId: 1,
- * transport: http("https://eth-mainnet.g.alchemy.com/v2/..."),
- * }
- * ```
- */
- transport: Transport;
- /** Polling frequency (in ms). Default: `1_000`. */
- pollingInterval?: number;
- /** Maximum concurrency of RPC requests during the historical sync. Default: `10`. */
- maxRpcRequestConcurrency?: number;
- }[];
+ networks: readonly Network[];
/** List of contracts to sync & index events from. Contracts defined here will be present in `context.contracts`. */
- contracts?: readonly (ContractRequired & ContractFilter)[];
+ contracts?: readonly Contract[];
/** Configuration for Ponder internals. */
- options?: {
- /** Maximum number of seconds to wait for event processing to be complete before responding as healthy. If event processing exceeds this duration, the API may serve incomplete data. Default: `240` (4 minutes). */
- maxHealthcheckDuration?: number;
- };
+ options?: Option;
};
/**
* Identity function for type-level validation of config
*/
export const createConfig = <
- const TConfig extends ResolvedConfig
+ const TConfig extends {
+ database?: Database;
+ networks: readonly Network[];
+ contracts: {
+ [key in keyof TConfig["contracts"]]: Contract<
+ TConfig["networks"][number]["name"],
+ TConfig["contracts"][key]["abi"]
+ >;
+ };
+ options?: Option;
+ }
>(
config:
| TConfig
@@ -151,3 +172,10 @@ export const createConfig = <
| (() => TConfig)
| (() => Promise)
) => config;
+
+// contracts: {
+// [key in keyof TConfig["contracts"] & number]: Contract<
+// TConfig["networks"][number]["name"],
+// TConfig["contracts"][key]["abi"]
+// >;
+// };
From fb3a2a85225f8f1a03f90b6a66865c351530e6de Mon Sep 17 00:00:00 2001
From: typedarray <90073088+0xOlias@users.noreply.github.com>
Date: Fri, 3 Nov 2023 17:57:46 -0400
Subject: [PATCH 19/44] Fix the stale table bug (#405)
* FIX THE STALE TABLE BUG
* tolerate errors in test cleanup
* chore: changeset
* try removing timeout
* Add logs
* add another log
* another log
* add global shutdown
* add schema to log
* remove logs from migration test
* use tx
* add retry
---
.changeset/loud-days-perform.md | 5 ++
packages/core/src/Ponder.ts | 4 +-
packages/core/src/_test/globalSetup.ts | 37 +++++++++-
packages/core/src/_test/setup.ts | 18 +++--
.../event-store/postgres/migrations.test.ts | 35 ++++++----
.../src/event-store/sqlite/migrations.test.ts | 32 ++++-----
packages/core/src/server/service.test.ts | 5 --
.../core/src/user-store/postgres/store.ts | 29 ++++----
packages/core/src/user-store/sqlite/store.ts | 29 ++++----
packages/core/src/user-store/store.test.ts | 70 -------------------
packages/core/src/user-store/store.ts | 2 +-
11 files changed, 119 insertions(+), 147 deletions(-)
create mode 100644 .changeset/loud-days-perform.md
diff --git a/.changeset/loud-days-perform.md b/.changeset/loud-days-perform.md
new file mode 100644
index 000000000..6ea1d31db
--- /dev/null
+++ b/.changeset/loud-days-perform.md
@@ -0,0 +1,5 @@
+---
+"@ponder/core": patch
+---
+
+Fixed a bug where stale tables were left in the database after the service was stopped.
diff --git a/packages/core/src/Ponder.ts b/packages/core/src/Ponder.ts
index 7e5b452c5..f5f13ab65 100644
--- a/packages/core/src/Ponder.ts
+++ b/packages/core/src/Ponder.ts
@@ -293,13 +293,13 @@ export class Ponder {
)
);
- await this.buildService.kill?.();
+ await this.buildService.kill();
this.uiService.kill();
this.indexingService.kill();
await this.serverService.kill();
- await this.userStore.teardown();
await this.common.telemetry.kill();
+ await this.userStore.kill();
await this.eventStore.kill();
this.common.logger.debug({
diff --git a/packages/core/src/_test/globalSetup.ts b/packages/core/src/_test/globalSetup.ts
index 6fded9655..0b413ee17 100644
--- a/packages/core/src/_test/globalSetup.ts
+++ b/packages/core/src/_test/globalSetup.ts
@@ -1,5 +1,6 @@
import { startProxy } from "@viem/anvil";
import dotenv from "dotenv";
+import { Pool } from "pg";
import { FORK_BLOCK_NUMBER } from "./constants";
@@ -11,11 +12,45 @@ export default async function () {
throw new Error('Missing environment variable "ANVIL_FORK_URL"');
}
- return await startProxy({
+ const shutdownProxy = await startProxy({
options: {
chainId: 1,
forkUrl: ANVIL_FORK_URL,
forkBlockNumber: FORK_BLOCK_NUMBER,
},
});
+
+ let cleanupDatabase: () => Promise;
+ if (process.env.DATABASE_URL) {
+ cleanupDatabase = async () => {
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
+
+ const schemaRows = await pool.query(`
+ SELECT nspname FROM pg_catalog.pg_namespace WHERE nspname ~ '^vitest_pool_';
+ `);
+ const schemas = schemaRows.rows.map((r) => r.nspname) as string[];
+
+ for (const schema of schemas) {
+ const tableRows = await pool.query(`
+ SELECT table_name FROM information_schema.tables WHERE table_schema = '${schema}'
+ `);
+ const tables = tableRows.rows.map((r) => r.table_name) as string[];
+
+ for (const table of tables) {
+ await pool.query(
+ `DROP TABLE IF EXISTS "${schema}"."${table}" CASCADE`
+ );
+ }
+ await pool.query(`DROP SCHEMA IF EXISTS "${schema}" CASCADE`);
+ console.log(`Dropped ${tables.length} tables from schema "${schema}".`);
+ }
+
+ await pool.end();
+ };
+ }
+
+ return async () => {
+ await shutdownProxy();
+ await cleanupDatabase?.();
+ };
}
diff --git a/packages/core/src/_test/setup.ts b/packages/core/src/_test/setup.ts
index 08c4d0eca..6af8cddf1 100644
--- a/packages/core/src/_test/setup.ts
+++ b/packages/core/src/_test/setup.ts
@@ -69,31 +69,31 @@ beforeEach((context) => {
*/
export async function setupEventStore(
context: TestContext,
- options = { skipMigrateUp: false }
+ options = { migrateUp: true }
) {
if (process.env.DATABASE_URL) {
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const databaseSchema = `vitest_pool_${process.pid}_${poolId}`;
context.eventStore = new PostgresEventStore({ pool, databaseSchema });
- if (!options.skipMigrateUp) await context.eventStore.migrateUp();
+ if (options.migrateUp) await context.eventStore.migrateUp();
return async () => {
try {
await pool.query(`DROP SCHEMA IF EXISTS "${databaseSchema}" CASCADE`);
+ await context.eventStore.kill();
} catch (e) {
- // This query fails in end-to-end tests where the pool has
+ // This fails in end-to-end tests where the pool has
// already been shut down during the Ponder instance kill() method.
// It's fine to ignore the error.
}
- await context.eventStore.kill();
};
} else {
const rawSqliteDb = new SqliteDatabase(":memory:");
const db = patchSqliteDatabase({ db: rawSqliteDb });
context.eventStore = new SqliteEventStore({ db });
- if (!options.skipMigrateUp) await context.eventStore.migrateUp();
+ if (options.migrateUp) await context.eventStore.migrateUp();
return async () => {
await context.eventStore.kill();
@@ -121,7 +121,13 @@ export async function setupUserStore(context: TestContext) {
}
return async () => {
- await context.userStore.teardown();
+ try {
+ await context.userStore.kill();
+ } catch (e) {
+ // This fails in end-to-end tests where the pool has
+ // already been shut down during the Ponder instance kill() method.
+ // It's fine to ignore the error.
+ }
};
}
diff --git a/packages/core/src/event-store/postgres/migrations.test.ts b/packages/core/src/event-store/postgres/migrations.test.ts
index 708d1b8c7..b5dd9947b 100644
--- a/packages/core/src/event-store/postgres/migrations.test.ts
+++ b/packages/core/src/event-store/postgres/migrations.test.ts
@@ -15,7 +15,7 @@ import {
rpcToPostgresTransaction,
} from "./format";
-beforeEach((context) => setupEventStore(context, { skipMigrateUp: true }));
+beforeEach((context) => setupEventStore(context, { migrateUp: false }));
const seed_2023_07_24_0_drop_finalized = async (db: Kysely) => {
await db
@@ -60,20 +60,27 @@ const seed_2023_07_24_0_drop_finalized = async (db: Kysely) => {
.execute();
};
-test("2023_07_24_0_drop_finalized -> 2023_09_19_0_new_sync_design succeeds", async (context) => {
- const { eventStore } = context;
+test(
+ "2023_07_24_0_drop_finalized -> 2023_09_19_0_new_sync_design succeeds",
+ async (context) => {
+ const { eventStore } = context;
- if (eventStore.kind !== "postgres") return;
+ if (eventStore.kind !== "postgres") return;
- const { error } = await eventStore.migrator.migrateTo(
- "2023_07_24_0_drop_finalized"
- );
- expect(error).toBeFalsy();
+ const { error } = await eventStore.migrator.migrateTo(
+ "2023_07_24_0_drop_finalized"
+ );
- await seed_2023_07_24_0_drop_finalized(eventStore.db);
+ expect(error).toBeFalsy();
- const { error: latestError } = await eventStore.migrator.migrateTo(
- "2023_09_19_0_new_sync_design"
- );
- expect(latestError).toBeFalsy();
-}, 15_000);
+ await seed_2023_07_24_0_drop_finalized(eventStore.db);
+
+ const { error: latestError } = await eventStore.migrator.migrateTo(
+ "2023_09_19_0_new_sync_design"
+ );
+ expect(latestError).toBeFalsy();
+ },
+ // This test is flaky. It seems like a Postgres isolation issue with our
+ // test setup. Annoying!
+ { timeout: 15_000, retry: 3 }
+);
diff --git a/packages/core/src/event-store/sqlite/migrations.test.ts b/packages/core/src/event-store/sqlite/migrations.test.ts
index 4a2cc939f..1b9d29b3e 100644
--- a/packages/core/src/event-store/sqlite/migrations.test.ts
+++ b/packages/core/src/event-store/sqlite/migrations.test.ts
@@ -15,7 +15,7 @@ import {
rpcToSqliteTransaction,
} from "./format";
-beforeEach((context) => setupEventStore(context, { skipMigrateUp: true }));
+beforeEach((context) => setupEventStore(context, { migrateUp: false }));
const seed_2023_07_24_0_drop_finalized = async (db: Kysely) => {
await db
@@ -60,24 +60,20 @@ const seed_2023_07_24_0_drop_finalized = async (db: Kysely) => {
.execute();
};
-test(
- "2023_07_24_0_drop_finalized -> 2023_09_19_0_new_sync_design succeeds",
- async (context) => {
- const { eventStore } = context;
+test("2023_07_24_0_drop_finalized -> 2023_09_19_0_new_sync_design succeeds", async (context) => {
+ const { eventStore } = context;
- if (eventStore.kind !== "sqlite") return;
+ if (eventStore.kind !== "sqlite") return;
- const { error } = await eventStore.migrator.migrateTo(
- "2023_07_24_0_drop_finalized"
- );
- expect(error).toBeFalsy();
+ const { error } = await eventStore.migrator.migrateTo(
+ "2023_07_24_0_drop_finalized"
+ );
+ expect(error).toBeFalsy();
- await seed_2023_07_24_0_drop_finalized(eventStore.db);
+ await seed_2023_07_24_0_drop_finalized(eventStore.db);
- const { error: latestError } = await eventStore.migrator.migrateTo(
- "2023_09_19_0_new_sync_design"
- );
- expect(latestError).toBeFalsy();
- },
- {}
-);
+ const { error: latestError } = await eventStore.migrator.migrateTo(
+ "2023_09_19_0_new_sync_design"
+ );
+ expect(latestError).toBeFalsy();
+});
diff --git a/packages/core/src/server/service.test.ts b/packages/core/src/server/service.test.ts
index 569c1ec03..4d4ebf641 100644
--- a/packages/core/src/server/service.test.ts
+++ b/packages/core/src/server/service.test.ts
@@ -1393,7 +1393,6 @@ test("serves singular entity versioned at specified timestamp", async (context)
expect(testEntity.string).toBe("updated");
await service.kill();
- await userStore.teardown();
});
test("serves plural entities versioned at specified timestamp", async (context) => {
@@ -1452,7 +1451,6 @@ test("serves plural entities versioned at specified timestamp", async (context)
]);
await service.kill();
- await userStore.teardown();
});
test("derived field respects skip argument", async (context) => {
@@ -1485,7 +1483,6 @@ test("derived field respects skip argument", async (context) => {
});
await service.kill();
- await userStore.teardown();
});
test("responds with appropriate status code pre and post historical sync", async (context) => {
@@ -1530,7 +1527,6 @@ test("responds with appropriate status code pre and post historical sync", async
});
await service.kill();
- await userStore.teardown();
});
// This is a known limitation for now, which is that the timestamp version of entities
@@ -1592,5 +1588,4 @@ test.skip("serves derived entities versioned at provided timestamp", async (cont
});
await service.kill();
- await userStore.teardown();
});
diff --git a/packages/core/src/user-store/postgres/store.ts b/packages/core/src/user-store/postgres/store.ts
index 859dc8c6c..0b3da3e7f 100644
--- a/packages/core/src/user-store/postgres/store.ts
+++ b/packages/core/src/user-store/postgres/store.ts
@@ -169,22 +169,21 @@ export class PostgresUserStore implements UserStore {
});
};
- /**
- * Tears down the store by dropping all tables for the current schema.
- */
- teardown = async () => {
- if (!this.schema) return;
+ async kill() {
+ const entities = this.schema?.entities ?? [];
+ if (entities.length > 0) {
+ await this.db.transaction().execute(async (tx) => {
+ await Promise.all(
+ entities.map(async (model) => {
+ const tableName = `${model.name}_${this.versionId}`;
+ await tx.schema.dropTable(tableName).execute();
+ })
+ );
+ });
+ }
- // Drop tables from existing schema.
- await this.db.transaction().execute(async (tx) => {
- await Promise.all(
- this.schema!.entities.map((model) => {
- const tableName = `${model.name}_${this.versionId}`;
- tx.schema.dropTable(tableName);
- })
- );
- });
- };
+ await this.db.destroy();
+ }
findUnique = async ({
modelName,
diff --git a/packages/core/src/user-store/sqlite/store.ts b/packages/core/src/user-store/sqlite/store.ts
index 4092d5644..193749d2c 100644
--- a/packages/core/src/user-store/sqlite/store.ts
+++ b/packages/core/src/user-store/sqlite/store.ts
@@ -150,22 +150,21 @@ export class SqliteUserStore implements UserStore {
});
};
- /**
- * Tears down the store by dropping all tables for the current schema.
- */
- teardown = async () => {
- if (!this.schema) return;
+ async kill() {
+ const entities = this.schema?.entities ?? [];
+ if (entities.length > 0) {
+ await this.db.transaction().execute(async (tx) => {
+ await Promise.all(
+ entities.map(async (model) => {
+ const tableName = `${model.name}_${this.versionId}`;
+ await tx.schema.dropTable(tableName).execute();
+ })
+ );
+ });
+ }
- // Drop tables from existing schema.
- await this.db.transaction().execute(async (tx) => {
- await Promise.all(
- this.schema!.entities.map((model) => {
- const tableName = `${model.name}_${this.versionId}`;
- tx.schema.dropTable(tableName);
- })
- );
- });
- };
+ await this.db.destroy();
+ }
findUnique = async ({
modelName,
diff --git a/packages/core/src/user-store/store.test.ts b/packages/core/src/user-store/store.test.ts
index aff263080..c3828df45 100644
--- a/packages/core/src/user-store/store.test.ts
+++ b/packages/core/src/user-store/store.test.ts
@@ -35,8 +35,6 @@ test("reload() binds the schema", async (context) => {
await userStore.reload({ schema });
expect(userStore.schema).toBe(schema);
-
- await userStore.teardown();
});
test("create() inserts a record that is effective after timestamp", async (context) => {
@@ -56,8 +54,6 @@ test("create() inserts a record that is effective after timestamp", async (conte
id: "id1",
});
expect(instance).toMatchObject({ id: "id1", name: "Skip", age: 12 });
-
- await userStore.teardown();
});
test("create() inserts a record that is effective at timestamp", async (context) => {
@@ -77,8 +73,6 @@ test("create() inserts a record that is effective at timestamp", async (context)
id: "id1",
});
expect(instance).toMatchObject({ id: "id1", name: "Skip", age: 12 });
-
- await userStore.teardown();
});
test("create() inserts a record that is not effective before timestamp", async (context) => {
@@ -98,8 +92,6 @@ test("create() inserts a record that is not effective before timestamp", async (
id: "id1",
});
expect(instance).toBeNull();
-
- await userStore.teardown();
});
test("create() throws on unique constraint violation", async (context) => {
@@ -121,8 +113,6 @@ test("create() throws on unique constraint violation", async (context) => {
data: { name: "Skip", age: 13 },
})
).rejects.toThrow();
-
- await userStore.teardown();
});
test("create() respects optional fields", async (context) => {
@@ -143,8 +133,6 @@ test("create() respects optional fields", async (context) => {
});
expect(instance).toMatchObject({ id: "id1", name: "Skip", age: null });
-
- await userStore.teardown();
});
test("create() accepts enums", async (context) => {
@@ -165,8 +153,6 @@ test("create() accepts enums", async (context) => {
});
expect(instance).toMatchObject({ id: "id1", name: "Skip", kind: "CAT" });
-
- await userStore.teardown();
});
test("create() throws on invalid enum value", async (context) => {
@@ -181,8 +167,6 @@ test("create() throws on invalid enum value", async (context) => {
data: { name: "Skip", kind: "NOTACAT" },
})
).rejects.toThrow();
-
- await userStore.teardown();
});
test("create() accepts BigInt fields as bigint and returns as bigint", async (context) => {
@@ -203,8 +187,6 @@ test("create() accepts BigInt fields as bigint and returns as bigint", async (co
});
expect(instance).toMatchObject({ id: "id1", name: "Skip", bigAge: 100n });
-
- await userStore.teardown();
});
test("update() updates a record", async (context) => {
@@ -236,8 +218,6 @@ test("update() updates a record", async (context) => {
id: "id1",
});
expect(updatedInstance).toMatchObject({ id: "id1", name: "Peanut Butter" });
-
- await userStore.teardown();
});
test("update() updates a record using an update function", async (context) => {
@@ -274,8 +254,6 @@ test("update() updates a record using an update function", async (context) => {
id: "id1",
name: "Skip and Skipper",
});
-
- await userStore.teardown();
});
test("update() updates a record and maintains older version", async (context) => {
@@ -306,8 +284,6 @@ test("update() updates a record and maintains older version", async (context) =>
name: "Skip",
bigAge: 100n,
});
-
- await userStore.teardown();
});
test("update() throws if trying to update an instance in the past", async (context) => {
@@ -329,8 +305,6 @@ test("update() throws if trying to update an instance in the past", async (conte
data: { name: "Peanut Butter" },
})
).rejects.toThrow();
-
- await userStore.teardown();
});
test("update() updates a record in-place within the same timestamp", async (context) => {
@@ -356,8 +330,6 @@ test("update() updates a record in-place within the same timestamp", async (cont
id: "id1",
});
expect(updatedInstance).toMatchObject({ id: "id1", name: "Peanut Butter" });
-
- await userStore.teardown();
});
test("upsert() inserts a new record", async (context) => {
@@ -373,8 +345,6 @@ test("upsert() inserts a new record", async (context) => {
const instance = await userStore.findUnique({ modelName: "Pet", id: "id1" });
expect(instance).toMatchObject({ id: "id1", name: "Skip", age: 12 });
-
- await userStore.teardown();
});
test("upsert() updates a record", async (context) => {
@@ -403,8 +373,6 @@ test("upsert() updates a record", async (context) => {
id: "id1",
});
expect(updatedInstance).toMatchObject({ id: "id1", name: "Jelly", age: 12 });
-
- await userStore.teardown();
});
test("upsert() updates a record using an update function", async (context) => {
@@ -435,8 +403,6 @@ test("upsert() updates a record using an update function", async (context) => {
id: "id1",
});
expect(updatedInstance).toMatchObject({ id: "id1", name: "Skip", age: 7 });
-
- await userStore.teardown();
});
test("upsert() throws if trying to update an instance in the past", async (context) => {
@@ -459,8 +425,6 @@ test("upsert() throws if trying to update an instance in the past", async (conte
update: { name: "Peanut Butter" },
})
).rejects.toThrow();
-
- await userStore.teardown();
});
test("upsert() updates a record in-place within the same timestamp", async (context) => {
@@ -487,8 +451,6 @@ test("upsert() updates a record in-place within the same timestamp", async (cont
id: "id1",
});
expect(updatedInstance).toMatchObject({ id: "id1", name: "Peanut Butter" });
-
- await userStore.teardown();
});
test("delete() removes a record", async (context) => {
@@ -511,8 +473,6 @@ test("delete() removes a record", async (context) => {
id: "id1",
});
expect(deletedInstance).toBe(null);
-
- await userStore.teardown();
});
test("delete() retains older version of record", async (context) => {
@@ -534,8 +494,6 @@ test("delete() retains older version of record", async (context) => {
id: "id1",
});
expect(deletedInstance).toMatchObject({ id: "id1", name: "Skip", age: 12 });
-
- await userStore.teardown();
});
test("delete() removes a record entirely if only present for one timestamp", async (context) => {
@@ -559,8 +517,6 @@ test("delete() removes a record entirely if only present for one timestamp", asy
id: "id1",
});
expect(deletedInstance).toBe(null);
-
- await userStore.teardown();
});
test("delete() removes a record entirely if only present for one timestamp after update()", async (context) => {
@@ -600,8 +556,6 @@ test("delete() removes a record entirely if only present for one timestamp after
id: "id1",
});
expect(deletedInstance).toBe(null);
-
- await userStore.teardown();
});
test("delete() deletes versions effective in the delete timestamp", async (context) => {
@@ -630,8 +584,6 @@ test("delete() deletes versions effective in the delete timestamp", async (conte
id: "id1",
});
expect(instancePriorToDelete!.name).toBe("Skip");
-
- await userStore.teardown();
});
test("findMany() returns current versions of all records", async (context) => {
@@ -670,8 +622,6 @@ test("findMany() returns current versions of all records", async (context) => {
"Foo",
"Bar",
]);
-
- await userStore.teardown();
});
test("findMany() sorts on bigint field", async (context) => {
@@ -708,8 +658,6 @@ test("findMany() sorts on bigint field", async (context) => {
orderBy: { bigAge: "asc" },
});
expect(instances.map((i) => i.bigAge)).toMatchObject([null, 10n, 105n, 190n]);
-
- await userStore.teardown();
});
test("findMany() filters on bigint gt", async (context) => {
@@ -747,8 +695,6 @@ test("findMany() filters on bigint gt", async (context) => {
});
expect(instances.map((i) => i.bigAge)).toMatchObject([105n, 190n]);
-
- await userStore.teardown();
});
test("findMany() sorts and filters together", async (context) => {
@@ -787,8 +733,6 @@ test("findMany() sorts and filters together", async (context) => {
});
expect(instances.map((i) => i.name)).toMatchObject(["Bar", "Zarbar"]);
-
- await userStore.teardown();
});
test("findMany() errors on invalid filter condition", async (context) => {
@@ -801,8 +745,6 @@ test("findMany() errors on invalid filter condition", async (context) => {
where: { name: { invalidWhereCondition: "ar" } },
})
).rejects.toThrow("Invalid filter condition name: invalidWhereCondition");
-
- await userStore.teardown();
});
test("findMany() errors on orderBy object with multiple keys", async (context) => {
@@ -815,8 +757,6 @@ test("findMany() errors on orderBy object with multiple keys", async (context) =
orderBy: { name: "asc", bigAge: "desc" },
})
).rejects.toThrow("Invalid sort condition: Must have exactly one property");
-
- await userStore.teardown();
});
test("createMany() inserts multiple entities", async (context) => {
@@ -836,8 +776,6 @@ test("createMany() inserts multiple entities", async (context) => {
const instances = await userStore.findMany({ modelName: "Pet" });
expect(instances.length).toBe(3);
-
- await userStore.teardown();
});
test("createMany() inserts a large number of entities", async (context) => {
@@ -859,8 +797,6 @@ test("createMany() inserts a large number of entities", async (context) => {
const instances = await userStore.findMany({ modelName: "Pet" });
expect(instances.length).toBe(ENTITY_COUNT);
-
- await userStore.teardown();
});
test("updateMany() updates multiple entities", async (context) => {
@@ -889,8 +825,6 @@ test("updateMany() updates multiple entities", async (context) => {
const instances = await userStore.findMany({ modelName: "Pet" });
expect(instances.map((i) => i.bigAge)).toMatchObject([10n, 300n, 300n]);
-
- await userStore.teardown();
});
test("revert() deletes versions newer than the safe timestamp", async (context) => {
@@ -937,8 +871,6 @@ test("revert() deletes versions newer than the safe timestamp", async (context)
const persons = await userStore.findMany({ modelName: "Person" });
expect(persons.length).toBe(1);
expect(persons[0].name).toBe("Bobby");
-
- await userStore.teardown();
});
test("revert() updates versions that only existed during the safe timestamp to latest", async (context) => {
@@ -962,6 +894,4 @@ test("revert() updates versions that only existed during the safe timestamp to l
const pets = await userStore.findMany({ modelName: "Pet" });
expect(pets.length).toBe(1);
expect(pets[0].name).toBe("Skip");
-
- await userStore.teardown();
});
diff --git a/packages/core/src/user-store/store.ts b/packages/core/src/user-store/store.ts
index 85eaac974..c83361e1a 100644
--- a/packages/core/src/user-store/store.ts
+++ b/packages/core/src/user-store/store.ts
@@ -78,7 +78,7 @@ export interface UserStore {
versionId?: string;
reload(options?: { schema?: Schema }): Promise;
- teardown(): Promise;
+ kill(): Promise;
revert(options: { safeTimestamp: number }): Promise;
From d89abd534828ad7bc29bfd4042d4887381124e1d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 3 Nov 2023 17:59:00 -0400
Subject: [PATCH 20/44] chore: version packages (#397)
Co-authored-by: github-actions[bot]
---
.changeset/large-buckets-flash.md | 7 -------
.changeset/loud-days-perform.md | 5 -----
packages/core/CHANGELOG.md | 10 ++++++++++
packages/core/package.json | 2 +-
packages/create-ponder/CHANGELOG.md | 2 ++
packages/create-ponder/package.json | 2 +-
packages/eslint-config-ponder/CHANGELOG.md | 2 ++
packages/eslint-config-ponder/package.json | 2 +-
8 files changed, 17 insertions(+), 15 deletions(-)
delete mode 100644 .changeset/large-buckets-flash.md
delete mode 100644 .changeset/loud-days-perform.md
diff --git a/.changeset/large-buckets-flash.md b/.changeset/large-buckets-flash.md
deleted file mode 100644
index a580e6549..000000000
--- a/.changeset/large-buckets-flash.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-"@ponder/core": patch
----
-
-BREAKING: This release includes a major update to Ponder's sync engine. Upgrading to this version will delete all cached sync progress and you will need to re-sync your app from scratch. If you're running a large Ponder app in production, please test this version on a branch + separate environment before upgrading on main.
-
-Added support for factory contracts. Please see the [documentation](https://ponder.sh/docs/contracts#factory-contracts) for a complete guide & API reference.
diff --git a/.changeset/loud-days-perform.md b/.changeset/loud-days-perform.md
deleted file mode 100644
index 6ea1d31db..000000000
--- a/.changeset/loud-days-perform.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"@ponder/core": patch
----
-
-Fixed a bug where stale tables were left in the database after the service was stopped.
diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md
index 124698823..4e2a049a1 100644
--- a/packages/core/CHANGELOG.md
+++ b/packages/core/CHANGELOG.md
@@ -1,5 +1,15 @@
# @ponder/core
+## 0.0.94
+
+### Patch Changes
+
+- [#361](https://github.com/0xOlias/ponder/pull/361) [`54bbd92`](https://github.com/0xOlias/ponder/commit/54bbd92ddfde8a17c45c244f1e0e6cf0000e4e9b) Thanks [@0xOlias](https://github.com/0xOlias)! - BREAKING: This release includes a major update to Ponder's sync engine. Upgrading to this version will delete all cached sync progress and you will need to re-sync your app from scratch. If you're running a large Ponder app in production, please test this version on a branch + separate environment before upgrading on main.
+
+ Added support for factory contracts. Please see the [documentation](https://ponder.sh/docs/contracts#factory-contracts) for a complete guide & API reference.
+
+- [#405](https://github.com/0xOlias/ponder/pull/405) [`fb3a2a8`](https://github.com/0xOlias/ponder/commit/fb3a2a85225f8f1a03f90b6a66865c351530e6de) Thanks [@0xOlias](https://github.com/0xOlias)! - Fixed a bug where stale tables were left in the database after the service was stopped.
+
## 0.0.93
### Patch Changes
diff --git a/packages/core/package.json b/packages/core/package.json
index d33ca8aee..f1decdd07 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@ponder/core",
- "version": "0.0.93",
+ "version": "0.0.94",
"description": "An open-source framework for crypto application backends",
"license": "MIT",
"repository": {
diff --git a/packages/create-ponder/CHANGELOG.md b/packages/create-ponder/CHANGELOG.md
index 78afc66bc..5afadd20c 100644
--- a/packages/create-ponder/CHANGELOG.md
+++ b/packages/create-ponder/CHANGELOG.md
@@ -1,5 +1,7 @@
# create-ponder
+## 0.0.94
+
## 0.0.93
## 0.0.92
diff --git a/packages/create-ponder/package.json b/packages/create-ponder/package.json
index 8d28e2b32..89752c394 100644
--- a/packages/create-ponder/package.json
+++ b/packages/create-ponder/package.json
@@ -1,6 +1,6 @@
{
"name": "create-ponder",
- "version": "0.0.93",
+ "version": "0.0.94",
"description": "A CLI tool to create Ponder apps",
"license": "MIT",
"repository": {
diff --git a/packages/eslint-config-ponder/CHANGELOG.md b/packages/eslint-config-ponder/CHANGELOG.md
index 268352f7d..5f9e83733 100644
--- a/packages/eslint-config-ponder/CHANGELOG.md
+++ b/packages/eslint-config-ponder/CHANGELOG.md
@@ -1,5 +1,7 @@
# eslint-config-ponder
+## 0.0.94
+
## 0.0.93
## 0.0.92
diff --git a/packages/eslint-config-ponder/package.json b/packages/eslint-config-ponder/package.json
index d2bd5a93f..99bdd3e37 100644
--- a/packages/eslint-config-ponder/package.json
+++ b/packages/eslint-config-ponder/package.json
@@ -1,6 +1,6 @@
{
"name": "eslint-config-ponder",
- "version": "0.0.93",
+ "version": "0.0.94",
"description": "ESLint config for Ponder apps",
"license": "MIT",
"main": "./index.js",
From 2f74dd3a56816def4279078f9fd54ac7d0bc0486 Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Fri, 3 Nov 2023 18:44:14 -0400
Subject: [PATCH 21/44] event type inference working
---
packages/core/src/config/config.test-d.ts | 18 ++++++++++++++++++
packages/core/src/config/config.test.ts | 2 +-
packages/core/src/config/config.ts | 23 ++++++++++++++---------
3 files changed, 33 insertions(+), 10 deletions(-)
diff --git a/packages/core/src/config/config.test-d.ts b/packages/core/src/config/config.test-d.ts
index b9b91536d..f70d34cc0 100644
--- a/packages/core/src/config/config.test-d.ts
+++ b/packages/core/src/config/config.test-d.ts
@@ -4,6 +4,7 @@ import {
ContractFilter,
FilterElement,
FilterEvents,
+ Kevin,
SafeEventNames,
} from "./config";
@@ -121,3 +122,20 @@ test("infer event names from abi", () => {
assertType([] as readonly ("Approve" | "Transfer")[] | undefined);
});
+
+test("kevin", () => {
+ const a = [
+ {
+ name: "BaseRegistrarImplementation",
+ network: [{ name: "mainnet" }],
+ abi: abiSimple,
+ event: ["Approve"],
+ address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
+ startBlock: 16370000,
+ endBlock: 16370020,
+ maxBlockRange: 10,
+ },
+ ] as const;
+ type t = Kevin[0]["event"];
+ // ^?
+});
diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts
index 3a655b73b..f4b3bad8b 100644
--- a/packages/core/src/config/config.test.ts
+++ b/packages/core/src/config/config.test.ts
@@ -33,7 +33,7 @@ test("createConfig() has strict events inferred from abi", () => {
name: "BaseRegistrarImplementation",
network: [{ name: "mainnet" }],
abi: abiSimple,
- event: ["Transfer", "Approve"],
+ event: ["Approve"],
address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
startBlock: 16370000,
endBlock: 16370020,
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index 4b1edd08d..f6549c07a 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -129,7 +129,19 @@ type Network = {
type Contract<
TNetworkNames extends string | unknown = string | unknown,
TAbi extends Abi | unknown = Abi | unknown
-> = ContractRequired & ContractFilter;
+> = ContractRequired & ContractFilter;
+
+export type Kevin<
+ TContracts extends readonly Omit[],
+ TNetworkNames extends string | unknown = string | unknown
+> = TContracts extends readonly [
+ infer First,
+ ...infer Rest extends readonly Contract[]
+]
+ ? First extends { abi: infer _abi }
+ ? readonly [Contract, ...Kevin]
+ : Kevin
+ : [];
type Option = {
/** Maximum number of seconds to wait for event processing to be complete before responding as healthy. If event processing exceeds this duration, the API may serve incomplete data. Default: `240` (4 minutes). */
@@ -158,7 +170,7 @@ export const createConfig = <
database?: Database;
networks: readonly Network[];
contracts: {
- [key in keyof TConfig["contracts"]]: Contract<
+ [key in keyof TConfig["contracts"] & number]: Contract<
TConfig["networks"][number]["name"],
TConfig["contracts"][key]["abi"]
>;
@@ -172,10 +184,3 @@ export const createConfig = <
| (() => TConfig)
| (() => Promise)
) => config;
-
-// contracts: {
-// [key in keyof TConfig["contracts"] & number]: Contract<
-// TConfig["networks"][number]["name"],
-// TConfig["contracts"][key]["abi"]
-// >;
-// };
From d809832fad80f26caa87f5c9c7f5a2b933978931 Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Sat, 4 Nov 2023 12:34:35 -0400
Subject: [PATCH 22/44] cleanup
---
packages/core/src/config/config.test-d.ts | 18 ------------------
packages/core/src/config/config.test.ts | 9 ++++++---
packages/core/src/config/config.ts | 21 ++++-----------------
packages/core/src/config/sources.ts | 14 ++++++++------
4 files changed, 18 insertions(+), 44 deletions(-)
diff --git a/packages/core/src/config/config.test-d.ts b/packages/core/src/config/config.test-d.ts
index f70d34cc0..b9b91536d 100644
--- a/packages/core/src/config/config.test-d.ts
+++ b/packages/core/src/config/config.test-d.ts
@@ -4,7 +4,6 @@ import {
ContractFilter,
FilterElement,
FilterEvents,
- Kevin,
SafeEventNames,
} from "./config";
@@ -122,20 +121,3 @@ test("infer event names from abi", () => {
assertType([] as readonly ("Approve" | "Transfer")[] | undefined);
});
-
-test("kevin", () => {
- const a = [
- {
- name: "BaseRegistrarImplementation",
- network: [{ name: "mainnet" }],
- abi: abiSimple,
- event: ["Approve"],
- address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
- startBlock: 16370000,
- endBlock: 16370020,
- maxBlockRange: 10,
- },
- ] as const;
- type t = Kevin[0]["event"];
- // ^?
-});
diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts
index f4b3bad8b..c12f9b26a 100644
--- a/packages/core/src/config/config.test.ts
+++ b/packages/core/src/config/config.test.ts
@@ -2,7 +2,7 @@ import { http } from "viem";
import { test } from "vitest";
import { createConfig } from "./config";
-import { abiSimple } from "./config.test-d";
+import { abiWithSameEvent } from "./config.test-d";
test("createConfig enforces matching network names", () => {
createConfig({
@@ -32,8 +32,11 @@ test("createConfig() has strict events inferred from abi", () => {
{
name: "BaseRegistrarImplementation",
network: [{ name: "mainnet" }],
- abi: abiSimple,
- event: ["Approve"],
+ abi: abiWithSameEvent,
+ event: [
+ "Transfer",
+ "Approve(address indexed, bytes32 indexed, uint256)",
+ ],
address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
startBlock: 16370000,
endBlock: 16370020,
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index f6549c07a..6b0b6fab3 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -82,10 +82,9 @@ export type ContractFilter = (
/** Maximum block range to use when calling `eth_getLogs`. Default: `10_000`. */
maxBlockRange?: number;
- event?: readonly SafeEventNames<
- FilterEvents,
- FilterEvents
- >[number][];
+ event?: Abi extends TAbi
+ ? string[]
+ : readonly SafeEventNames, FilterEvents>[number][];
};
type Database =
@@ -131,18 +130,6 @@ type Contract<
TAbi extends Abi | unknown = Abi | unknown
> = ContractRequired & ContractFilter;
-export type Kevin<
- TContracts extends readonly Omit[],
- TNetworkNames extends string | unknown = string | unknown
-> = TContracts extends readonly [
- infer First,
- ...infer Rest extends readonly Contract[]
-]
- ? First extends { abi: infer _abi }
- ? readonly [Contract, ...Kevin]
- : Kevin
- : [];
-
type Option = {
/** Maximum number of seconds to wait for event processing to be complete before responding as healthy. If event processing exceeds this duration, the API may serve incomplete data. Default: `240` (4 minutes). */
maxHealthcheckDuration?: number;
@@ -150,7 +137,7 @@ type Option = {
export type ResolvedConfig<
TNetworkNames extends string | unknown = string | unknown,
- TAbi extends Abi = Abi
+ TAbi extends Abi | unknown = Abi | unknown
> = {
/** Database to use for storing blockchain & entity data. Default: `"postgres"` if `DATABASE_URL` env var is present, otherwise `"sqlite"`. */
database?: Database;
diff --git a/packages/core/src/config/sources.ts b/packages/core/src/config/sources.ts
index 7e59e39f6..b27476bd0 100644
--- a/packages/core/src/config/sources.ts
+++ b/packages/core/src/config/sources.ts
@@ -137,17 +137,19 @@ const buildTopics = (
.map((event) =>
encodeEventTopics({
abi: [event],
- eventName: event.name,
+ eventName: event,
})
)
.flat(),
];
} else {
+ // TODO:KYLE handle this once events get more complex
+ return [];
// Single event with args
- return encodeEventTopics({
- abi: [events.signature],
- eventName: events.signature.name,
- args: events.args,
- });
+ // return encodeEventTopics({
+ // abi: [events.signature],
+ // eventName: events.signature.name,
+ // args: events.args,
+ // });
}
};
From d53f66156b7d9175142fc33a78464be72f7eee63 Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Sat, 4 Nov 2023 12:39:33 -0400
Subject: [PATCH 23/44] change config event shape
---
packages/core/src/config/config.ts | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index 6b0b6fab3..c9803ba3b 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -82,9 +82,19 @@ export type ContractFilter = (
/** Maximum block range to use when calling `eth_getLogs`. Default: `10_000`. */
maxBlockRange?: number;
- event?: Abi extends TAbi
- ? string[]
- : readonly SafeEventNames, FilterEvents>[number][];
+ filter?: Abi extends TAbi
+ ? string[] | { event: string }
+ :
+ | readonly SafeEventNames<
+ FilterEvents,
+ FilterEvents
+ >[number][]
+ | {
+ event: SafeEventNames<
+ FilterEvents,
+ FilterEvents
+ >[number];
+ };
};
type Database =
From 25b686aa8b0ce09ec0f6c04649b89d4a8ca847ba Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Sat, 4 Nov 2023 14:24:35 -0400
Subject: [PATCH 24/44] inferring event args not working
---
packages/core/src/config/config.test-d.ts | 34 +++++++-----
packages/core/src/config/config.test.ts | 27 +++++++++-
packages/core/src/config/config.ts | 66 ++++++++++++++++++-----
packages/core/src/config/sources.test.ts | 7 +++
packages/core/src/config/sources.ts | 6 ++-
5 files changed, 111 insertions(+), 29 deletions(-)
create mode 100644 packages/core/src/config/sources.test.ts
diff --git a/packages/core/src/config/config.test-d.ts b/packages/core/src/config/config.test-d.ts
index b9b91536d..777529355 100644
--- a/packages/core/src/config/config.test-d.ts
+++ b/packages/core/src/config/config.test-d.ts
@@ -1,9 +1,9 @@
import { assertType, test } from "vitest";
import {
- ContractFilter,
- FilterElement,
FilterEvents,
+ RecoverAbiEvent,
+ ResolvedConfig,
SafeEventNames,
} from "./config";
@@ -18,14 +18,17 @@ export const abiSimple = [
{
indexed: true,
type: "address",
+ name: "from",
},
{
indexed: true,
type: "address",
+ name: "to",
},
{
indexed: false,
type: "uint256",
+ name: "amount",
},
],
name: "Approve",
@@ -89,12 +92,6 @@ test("filter events", () => {
] as const);
});
-test("filter elements", () => {
- type a = FilterElement<"a", readonly ["a", "b", "c"]>;
- // ^?
- assertType(["b", "c"] as const);
-});
-
test("safe event names", () => {
type a = SafeEventNames<
// ^?
@@ -109,15 +106,28 @@ test("safe event names", () => {
FilterEvents
>;
assertType([
- "Approve(address indexed, address indexed, uint256)",
+ "Approve(address indexed from, address indexed to, uint256 amount)",
"Transfer",
"Approve(address indexed, bytes32 indexed, uint256)",
]);
});
-test("infer event names from abi", () => {
- type a = ContractFilter["event"];
+test("ResolvedConfig default values", () => {
+ type a = NonNullable[number]["filter"];
// ^?
+ assertType({} as string[] | { event: string } | undefined);
+});
+
+test("RecoverAbiEvent", () => {
+ type a = RecoverAbiEvent<
+ // ^?
+ FilterEvents,
+ SafeEventNames<
+ FilterEvents,
+ FilterEvents
+ >,
+ "Approve"
+ >;
- assertType([] as readonly ("Approve" | "Transfer")[] | undefined);
+ assertType (abiSimple[1]);
});
diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts
index c12f9b26a..4c326d8c9 100644
--- a/packages/core/src/config/config.test.ts
+++ b/packages/core/src/config/config.test.ts
@@ -2,7 +2,7 @@ import { http } from "viem";
import { test } from "vitest";
import { createConfig } from "./config";
-import { abiWithSameEvent } from "./config.test-d";
+import { abiSimple, abiWithSameEvent } from "./config.test-d";
test("createConfig enforces matching network names", () => {
createConfig({
@@ -33,7 +33,7 @@ test("createConfig() has strict events inferred from abi", () => {
name: "BaseRegistrarImplementation",
network: [{ name: "mainnet" }],
abi: abiWithSameEvent,
- event: [
+ filter: [
"Transfer",
"Approve(address indexed, bytes32 indexed, uint256)",
],
@@ -45,3 +45,26 @@ test("createConfig() has strict events inferred from abi", () => {
],
});
});
+
+test("createConfig() has strict arg types for event", () => {
+ createConfig({
+ networks: [
+ { name: "mainnet", chainId: 1, transport: http("http://127.0.0.1:8545") },
+ ],
+ contracts: [
+ {
+ name: "BaseRegistrarImplementation",
+ network: [{ name: "mainnet" }],
+ abi: abiSimple,
+ filter: {
+ event: "Approve",
+ args: {},
+ },
+ address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
+ startBlock: 16370000,
+ endBlock: 16370020,
+ maxBlockRange: 10,
+ },
+ ],
+ });
+});
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index c9803ba3b..8a6059373 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -1,5 +1,5 @@
import type { Abi, AbiEvent, FormatAbiItem } from "abitype";
-import type { Transport } from "viem";
+import type { GetEventArgs, Transport } from "viem";
/**
* Keep only AbiEvents from an Abi
@@ -16,7 +16,7 @@ export type FilterEvents = T extends readonly [
/**
* Remove TElement from TArr
*/
-export type FilterElement<
+type FilterElement<
TElement,
TArr extends readonly unknown[] | unknown
> = TArr extends readonly [infer First, ...infer Rest]
@@ -44,9 +44,28 @@ export type SafeEventNames<
readonly [First["name"], ...SafeEventNames]
: [];
+export type RecoverAbiEvent<
+ TAbi extends readonly AbiEvent[] | unknown,
+ TSafeNames extends readonly string[],
+ TSafeName extends string
+> = TAbi extends readonly [
+ infer FirstAbi,
+ ...infer RestAbi extends readonly AbiEvent[]
+]
+ ? TSafeNames extends readonly [
+ infer FirstName,
+ ...infer RestName extends readonly string[]
+ ]
+ ? FirstName extends TSafeName
+ ? FirstAbi
+ : RecoverAbiEvent
+ : []
+ : [];
+
type ContractRequired<
TNetworkNames extends string | unknown = string | unknown,
- TAbi extends Abi | unknown = Abi | unknown
+ TAbi extends Abi | unknown = Abi | unknown,
+ TEventName extends string = string
> = {
/** Contract name. Must be unique across `contracts` and `filters`. */
name: string;
@@ -54,11 +73,13 @@ type ContractRequired<
* Network that this contract is deployed to. Must match a network name in `networks`.
* Any filter information overrides the values in the higher level "contracts" property. Factories cannot override an address and vice versa.
*/
- network: readonly ({ name: TNetworkNames } & Partial>)[];
+ network: readonly ({ name: TNetworkNames } & Partial<
+ ContractFilter
+ >)[];
abi: Abi;
};
-export type ContractFilter = (
+type ContractFilter = (
| {
/** Contract address. */
address?: `0x${string}`;
@@ -94,6 +115,22 @@ export type ContractFilter = (
FilterEvents,
FilterEvents
>[number];
+ args: GetEventArgs<
+ Abi,
+ string,
+ {
+ EnableUnion: true;
+ IndexedOnly: true;
+ Required: false;
+ },
+ RecoverAbiEvent<
+ TAbi,
+ SafeEventNames, FilterEvents>,
+ TEventName
+ > extends infer _abiEvent extends AbiEvent
+ ? _abiEvent
+ : AbiEvent
+ >;
};
};
@@ -137,24 +174,22 @@ type Network = {
type Contract<
TNetworkNames extends string | unknown = string | unknown,
- TAbi extends Abi | unknown = Abi | unknown
-> = ContractRequired & ContractFilter;
+ TAbi extends Abi | unknown = Abi | unknown,
+ TEventName extends string = string
+> = ContractRequired & ContractFilter;
type Option = {
/** Maximum number of seconds to wait for event processing to be complete before responding as healthy. If event processing exceeds this duration, the API may serve incomplete data. Default: `240` (4 minutes). */
maxHealthcheckDuration?: number;
};
-export type ResolvedConfig<
- TNetworkNames extends string | unknown = string | unknown,
- TAbi extends Abi | unknown = Abi | unknown
-> = {
+export type ResolvedConfig = {
/** Database to use for storing blockchain & entity data. Default: `"postgres"` if `DATABASE_URL` env var is present, otherwise `"sqlite"`. */
database?: Database;
/** List of blockchain networks. */
networks: readonly Network[];
/** List of contracts to sync & index events from. Contracts defined here will be present in `context.contracts`. */
- contracts?: readonly Contract[];
+ contracts?: readonly Contract[];
/** Configuration for Ponder internals. */
options?: Option;
};
@@ -169,7 +204,12 @@ export const createConfig = <
contracts: {
[key in keyof TConfig["contracts"] & number]: Contract<
TConfig["networks"][number]["name"],
- TConfig["contracts"][key]["abi"]
+ TConfig["contracts"][key]["abi"],
+ TConfig["contracts"][key]["filter"] extends {
+ event: infer _event extends string;
+ }
+ ? _event
+ : string
>;
};
options?: Option;
diff --git a/packages/core/src/config/sources.test.ts b/packages/core/src/config/sources.test.ts
new file mode 100644
index 000000000..63b4cb693
--- /dev/null
+++ b/packages/core/src/config/sources.test.ts
@@ -0,0 +1,7 @@
+import { test } from "vitest";
+
+test.todo("buildSources() builds topics for multiple events");
+
+test.todo("buildSources() builds topics for event with args");
+
+test.todo("buildSources() overrides default values with network values");
diff --git a/packages/core/src/config/sources.ts b/packages/core/src/config/sources.ts
index b27476bd0..318564963 100644
--- a/packages/core/src/config/sources.ts
+++ b/packages/core/src/config/sources.ts
@@ -76,7 +76,7 @@ export const buildSources = ({
(n) => n.name === networkContract.name
)!;
- const resolvedEvents = networkContract.event ?? contract.event;
+ const resolvedEvents = networkContract.filter ?? contract.filter;
const topics = resolvedEvents
? buildTopics(resolvedEvents)
@@ -128,7 +128,9 @@ export const buildSources = ({
};
const buildTopics = (
- events: NonNullable[number]["event"]>
+ events: NonNullable<
+ NonNullable[number]["filter"]
+ >
): Topics => {
if (Array.isArray(events)) {
// List of event signatures
From 435f567cb947cbcdc03a3c04d7c00fc03e5fc5ea Mon Sep 17 00:00:00 2001
From: Kyle Scott
Date: Sat, 4 Nov 2023 14:33:19 -0400
Subject: [PATCH 25/44] fixed event arg type inference
---
packages/core/src/config/config.test.ts | 6 ++--
packages/core/src/config/config.ts | 40 ++++++++++++++-----------
2 files changed, 26 insertions(+), 20 deletions(-)
diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts
index 4c326d8c9..ae5d7c1e7 100644
--- a/packages/core/src/config/config.test.ts
+++ b/packages/core/src/config/config.test.ts
@@ -35,7 +35,7 @@ test("createConfig() has strict events inferred from abi", () => {
abi: abiWithSameEvent,
filter: [
"Transfer",
- "Approve(address indexed, bytes32 indexed, uint256)",
+ "Approve(address indexed from, address indexed to, uint256 amount)",
],
address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
startBlock: 16370000,
@@ -58,7 +58,9 @@ test("createConfig() has strict arg types for event", () => {
abi: abiSimple,
filter: {
event: "Approve",
- args: {},
+ args: {
+ to: ["0x"],
+ },
},
address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
startBlock: 16370000,
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index 8a6059373..2be6c0643 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -4,7 +4,7 @@ import type { GetEventArgs, Transport } from "viem";
/**
* Keep only AbiEvents from an Abi
*/
-export type FilterEvents = T extends readonly [
+export type FilterEvents = T extends readonly [
infer First,
...infer Rest extends Abi
]
@@ -18,7 +18,7 @@ export type FilterEvents = T extends readonly [
*/
type FilterElement<
TElement,
- TArr extends readonly unknown[] | unknown
+ TArr extends readonly unknown[]
> = TArr extends readonly [infer First, ...infer Rest]
? TElement extends First
? FilterElement
@@ -29,8 +29,8 @@ type FilterElement<
* Return an array of safe event names that handle multiple events with the same name
*/
export type SafeEventNames<
- TAbi extends readonly AbiEvent[] | unknown,
- TArr extends readonly AbiEvent[] | unknown
+ TAbi extends readonly AbiEvent[],
+ TArr extends readonly AbiEvent[]
> = TAbi extends readonly [
infer First extends AbiEvent,
...infer Rest extends readonly AbiEvent[]
@@ -45,7 +45,7 @@ export type SafeEventNames<
: [];
export type RecoverAbiEvent<
- TAbi extends readonly AbiEvent[] | unknown,
+ TAbi extends readonly AbiEvent[],
TSafeNames extends readonly string[],
TSafeName extends string
> = TAbi extends readonly [
@@ -63,9 +63,9 @@ export type RecoverAbiEvent<
: [];
type ContractRequired<
- TNetworkNames extends string | unknown = string | unknown,
- TAbi extends Abi | unknown = Abi | unknown,
- TEventName extends string = string
+ TNetworkNames extends string,
+ TAbi extends readonly AbiEvent[],
+ TEventName extends string
> = {
/** Contract name. Must be unique across `contracts` and `filters`. */
name: string;
@@ -79,7 +79,10 @@ type ContractRequired<
abi: Abi;
};
-type ContractFilter = (
+type ContractFilter<
+ TAbi extends readonly AbiEvent[],
+ TEventName extends string
+> = (
| {
/** Contract address. */
address?: `0x${string}`;
@@ -103,8 +106,8 @@ type ContractFilter = (
/** Maximum block range to use when calling `eth_getLogs`. Default: `10_000`. */
maxBlockRange?: number;
- filter?: Abi extends TAbi
- ? string[] | { event: string }
+ filter?: readonly AbiEvent[] extends TAbi
+ ? string[] | { event: string; args?: unknown }
:
| readonly SafeEventNames<
FilterEvents,
@@ -115,7 +118,7 @@ type ContractFilter = (
FilterEvents,
FilterEvents