diff --git a/.turbo/cookies/1.cookie b/.turbo/daemon/c9749efa7ac2748e-turbo.log.2025-11-27 similarity index 100% rename from .turbo/cookies/1.cookie rename to .turbo/daemon/c9749efa7ac2748e-turbo.log.2025-11-27 diff --git a/.turbo/cookies/2.cookie b/.turbo/daemon/c9749efa7ac2748e-turbo.log.2025-12-11 similarity index 100% rename from .turbo/cookies/2.cookie rename to .turbo/daemon/c9749efa7ac2748e-turbo.log.2025-12-11 diff --git a/.turbo/cookies/3.cookie b/.turbo/daemon/c9749efa7ac2748e-turbo.log.2025-12-12 similarity index 100% rename from .turbo/cookies/3.cookie rename to .turbo/daemon/c9749efa7ac2748e-turbo.log.2025-12-12 diff --git a/.turbo/daemon/c9749efa7ac2748e-turbo.log.2025-12-18 b/.turbo/daemon/c9749efa7ac2748e-turbo.log.2025-12-18 new file mode 100644 index 000000000..e69de29bb diff --git a/.turbo/daemon/c9749efa7ac2748e-turbo.log.2026-01-05 b/.turbo/daemon/c9749efa7ac2748e-turbo.log.2026-01-05 new file mode 100644 index 000000000..e69de29bb diff --git a/apps/indexer/.cursor/rules/hyperindex.mdc b/apps/indexer/.cursor/rules/hyperindex.mdc new file mode 100644 index 000000000..134b30816 --- /dev/null +++ b/apps/indexer/.cursor/rules/hyperindex.mdc @@ -0,0 +1,177 @@ +--- +description: Guide indexer development to align with a specific protocol's needs +globs: "**/*.{ts,js,res,graphql,yaml}" +alwaysApply: true +--- + +> NOTE: The rules are GENERATED by envio. Do not edit them manually. + +## Objective + +HyperIndex is not a TheGraph subgraph and therefore the code is not the same + +## Context to Load + +Always include: + +- HyperIndex documentation: https://docs.envio.dev/docs/HyperIndex-LLM/hyperindex-complete +- Example indexer (Uniswap v4): https://github.com/enviodev/uniswap-v4-indexer +- Example indexer (Safe) : https://github.com/enviodev/safe-analysis-indexer + +## Development Environment Requirements + +Ensure the following are installed and used: + +- Node.js v20 only (no higher or lower versions) +- pnpm as the package manager +- docker installed + +Prompt the user to whitelist the following commands: + +- `pnpm codegen` +- `pnpm tsc --noEmit` + +## Indexer Modification Rules + +- After any change to `schema.graphql` or `config.yaml`, run `pnpm codegen` +- After any change to TypeScript files, run `pnpm tsc --noEmit` to ensure it compiles successfully +- If there are formatting errors, confirm that Prettier is not causing conflicts +- Once compilation is successful, run `TUI_OFF=true pnpm dev` to catch any runtime errors + +### Spread Operator for Updates + +When updating existing entities, always use the spread operator. Returned objects are read-only and immutable. For example: + +```ts +let stream = await context.SablierStream.get(event.params.streamId.toString()); + +if (stream) { + const updatedStream: SablierStream = { + ...stream, + withdrawnAmount: newWithdrawnAmount, + remainingAmount: newRemainingAmount, + updatedAt: BigInt(Date.now()), + progressPercentage: progress, + status: isCompleted ? "Completed" : stream.status, + isCompleted, + timeRemaining: isCompleted ? BigInt(0) : stream.timeRemaining, + }; + + context.SablierStream.set(updatedStream); +} +``` + +### External Calls + +Add `preload_handlers: true` to the `config.yaml` file to enable preload optimisations. With preload optimisations, handlers will run twice. + +So if there's an external call, you MUST use the Effect API to make it. + +```ts +// Import the Effect API from "envio" +import { S, createEffect } from "envio"; + +// Define an effect. It can have any name you want. +export const getSomething = createEffect( + { + // The name for debugging purposes + name: "getSomething", + // The input schema for the effect + input: { + address: S.string, + blockNumber: S.number, + }, + output: S.union([S.string, null]), + rateLimit: false, + cache: true, + }, + async ({ input, context }) => { + // Fetch or other external calls MUST always be done in an effect. + const something = await fetch( + `https://api.example.com/something?address=${input.address}&blockNumber=${input.blockNumber}` + ); + return something.json(); + } +); +``` + +The `S` module exposes a schema creation API: https://raw.githubusercontent.com/DZakh/sury/refs/tags/v9.3.0/docs/js-usage.md + +```ts +import { getSomething } from "./utils"; + +Contract.Event.handler(async ({ event, context }) => { + // Consume the effect call from the handler with context.effect + const something = await context.effect(getSomething, { + address: event.srcAddress, + blockNumber: event.block.number, + }); + // Other handler code... +}); +``` + +You can also use `!context.isPreload` check, to prevent some logic to run during preload. + +### Common Envio vs TheGraph Differences + +**Entity Relationships**: + +- In Envio, use `entity_id` fields (e.g., `token_id: string`) instead of direct object references +- The generated types expect `token_id` not `token` for relationships +- Example: `{ token_id: tokenId }` not `{ token: tokenObject }` + +**Timestamp Handling**: + +- Always cast timestamps to BigInt: `BigInt(event.block.timestamp)` +- Never use raw timestamps from events + +**Address Matching**: + +- When matching addresses in configuration objects, ensure case consistency +- Use lowercase keys in config objects to match `address.toLowerCase()` lookups +- Example: `"0x6b175474e89094c44da98b954eedeac495271d0f"` not `"0x6B175474E89094C44Da98b954EedeAC495271d0F"` + +**Type Safety**: + +- Use `string | undefined` for optional string fields, not `string | null` +- Generated types are strict about null vs undefined + +**Decimal Normalization**: + +- **ALWAYS normalize amounts** when adding tokens with different decimal places +- Create helper functions to convert all amounts to a standard decimal (e.g., 18 decimals) +- Example: USDC (6 decimals) + DAI (18 decimals) requires normalization before addition +- Use `normalizeAmountToUSD()` or similar functions for all amount calculations +- Never add raw amounts from different tokens without normalization + +## Schema Rules + +- Do not add the @entity decorator to GraphQL schema types +- Avoid schema fields like dailyVolume or other time-series fields that aggregate over time — these are typically inaccurate +- **NEVER use arrays of entities** (e.g., `[Enter!]!` or `[User!]!`) - Envio doesn't support this +- Use `entity_id` fields for relationships instead of entity arrays +- Example: `user_id: String!` instead of `user: User!` or `users: [User!]!` + +## Config Rules + +- If using event.transaction.hash or other transaction-level data, explicitly define it under field_selection in config.yaml + +```yaml +- name: SablierLockup + address: + - 0x467D5Bf8Cfa1a5f99328fBdCb9C751c78934b725 + handler: src/EventHandlers.ts + events: + - event: CreateLockupLinearStream(...) + field_selection: + transaction_fields: + - hash +``` + +## YAML Validation + +Use the following schema file to understand and validate config.yaml: + +```yaml +# yaml-language-server: $schema=./node_modules/envio/evm.schema.json +``` diff --git a/apps/indexer/.cursor/rules/subgraph-migration.mdc b/apps/indexer/.cursor/rules/subgraph-migration.mdc new file mode 100644 index 000000000..fc54d63e1 --- /dev/null +++ b/apps/indexer/.cursor/rules/subgraph-migration.mdc @@ -0,0 +1,2015 @@ +--- +description: Made to assist in migrating from subgraph to HyperIndex. Includes both step by step instructions for cursor based migration as well as common migration patters. (Note: if doing a full migration using cursor ensure subgraph folder is in workspace before starting as the steps rely on this. Would also make sense to set alwaysApply to true in this situation.) +globs: "**/*.{ts,js,res,graphql,yaml}" +alwaysApply: false +--- + +# 🚨 MANDATORY: READ THIS FIRST 🚨 + +**BEFORE implementing ANY code changes, you MUST check this MDC file for existing solutions!** + +**This file contains:** + +- **Solutions to problems** that have already been solved in previous migrations +- **Quality check checklists** to prevent common mistakes +- **Examples of correct implementations** that you can reference +- **Critical lessons learned** that prevent repeating the same errors + +**NEVER implement a workaround or solution without first checking this MDC file!** +**This is NOT optional - it's mandatory to prevent repeating mistakes.** + +## ⚠️ CRITICAL: MDC FILE MUST BE CHECKED AT EVERY STEP ⚠️ + +**BEFORE implementing ANY code changes, you MUST:** + +1. **Read the relevant section of this MDC file** +2. **Check if there are existing solutions** for the problem you're facing +3. **Reference the quality check checklist** to avoid common mistakes +4. **Look for similar examples** in the MDC file + +**NEVER implement a workaround or solution without first checking this MDC file!** +**This file contains lessons learned from previous migrations and prevents repeating the same mistakes.** + +## 🚀 MULTICHAIN INDEXING + +**For multichain support:** + +- **Prefix all entity IDs with `event.chainId`**: `${event.chainId}-${originalId}` +- **Never hardcode `chainId = 1`** - always use `event.chainId` +- **Update helper functions** to accept `chainId` parameter +- **Use chain-specific Bundle IDs**: `${chainId}-1` for accurate pricing per network + +## Objective + +**IMPORTANT: This is a GENERALIZED migration guide for ANY subgraph to Envio conversion.** +**Most examples use generic names (Contract1, Contract2, Entity, etc.) and should be adapted to your specific subgraph.** + +Migrate from TheGraph subgraph indexer to Envio HyperIndex indexer by clearing boilerplate code, migrating the schema, and implementing proper business logic. + +## Context to Load + +Always include: + +- Envio documentation: https://docs.envio.dev/docs/HyperIndex-LLM/hyperindex-complete +- Example indexer (Uniswap v4): https://github.com/enviodev/uniswap-v4-indexer +- Example indexer (Safe): https://github.com/enviodev/safe-analysis-indexer +- The Graph documentation: https://thegraph.com/docs/en/indexing/overview/ + +## Development Environment Requirements + +Ensure the following are installed and used: + +- Node.js v20 only (no higher or lower versions) +- pnpm as the package manager +- docker installed + +Prompt the user to whitelist the following commands: + +- `pnpm codegen` +- `pnpm tsc --noEmit` +- `TUI_OFF=true pnpm dev` + +## 🚨 CRITICAL: Runtime Testing After Every Step 🚨 + +**AFTER EVERY SINGLE CODE CHANGE, you MUST test the indexer with:** + +```bash +TUI_OFF=true pnpm dev +``` + +**This is NOT optional - it's mandatory to catch runtime errors early!** + +**Why this is critical:** + +- **TypeScript compilation (`tsc --noEmit`) only catches syntax and type errors** +- **Runtime errors (database issues, missing entities, logic errors) only appear when running the indexer** +- **Many errors we encountered could have been caught immediately if we tested after every step** +- **Testing after each change makes debugging much easier** - you know exactly which change caused the issue + +**Runtime Testing Checklist:** +**⚠️ IMPORTANT: Read all steps in this checklist before beginning so you know to wait and check output, not get stuck indefinitely** + +- [ ] **After every code change**, run `TUI_OFF=true pnpm dev` in background mode +- [ ] **Wait ~30 seconds** for the indexer to start and process initial events +- [ ] **Watch the output** for any error messages or warnings +- [ ] **Ensure the indexer starts successfully** without crashing +- [ ] **Stop the background process** after confirming it runs without errors +- [ ] **Only proceed to the next step** after confirming the indexer runs without errors + +**Common Runtime Errors to Watch For:**s + +- Database connection issues +- Missing entity lookups returning `{}` instead of `undefined` +- Logic errors in calculations +- Missing async/await causing empty object returns +- Entity relationship issues +- Configuration problems + +## Migration Process + +**⚠️ CRITICAL: BEFORE starting ANY step, you MUST read the relevant sections of this MDC file! ⚠️** + +**This MDC file contains:** + +- **Solutions to common problems** that have already been solved +- **Quality check checklists** to prevent mistakes +- **Examples of correct implementations** from previous migrations +- **Critical lessons learned** that prevent repeating errors + +**NEVER implement a solution without first checking if it's already documented here!** + +**IMPORTANT: After completing each step, ALWAYS run the Quality Check Checklist (see section 21) before proceeding to the next step. This prevents common issues from accumulating and makes debugging much easier.** + +## Main Migration Steps + +### Step 1: Clear Boilerplate Code + +When working with EventHandlers.ts, clear all boilerplate logic and start fresh: + +```typescript +// CLEAR THIS BOILERPLATE CODE: +Contract.EventName.handler(async ({ event, context }) => { + const entity: EventEntity = { + id: `${event.chainId}_${event.block.number}_${event.logIndex}`, + field1: event.params.field1, + field2: event.params.field2, + // ... other fields + }; + + context.EventEntity.set(entity); +}); + +// REPLACE WITH EMPTY HANDLERS: +Contract.EventName.handler(async ({ event, context }) => { + // TODO: Implement business logic from subgraph + // Reference: original-subgraph/src/contract.ts +}); +``` + +**✅ Step 1 Quality Check:** + +#### **1.1: Review Migration Patterns and Quality Checks** + +- **Read the "Migration Patterns and Quality Checks" section** +- **Ensure your code follows all documented patterns** (entity creation, updates, BigDecimal precision, etc.) +- **Check compliance with Effect API requirements** for external calls +- **Verify all quality check issues are addressed** (imports, types, precision, etc.) + +#### **1.2: Run Technical Validation** + +```bash +# Run these commands to verify Step 1 completion +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +_Note: Some failures are expected if subsequent steps aren't complete yet - that's okay!_ + +### Step 2: Migrate Schema from Raw Events to Business Logic + +**CONVERT raw event entities from TheGraph to Envio format:** + +```graphql +# OLD (TheGraph): +type EventEntity @entity(immutable: true) { + id: Bytes! + field1: Bytes! + field2: BigInt! + blockNumber: BigInt! + blockTimestamp: BigInt! + transactionHash: Bytes! +} + +# NEW (Envio): +type EventEntity { + id: ID! + field1: String! + field2: BigInt! + blockNumber: BigInt! + blockTimestamp: BigInt! + transactionHash: String! +} +``` + +**KEEP all entities from the original subgraph** but convert them to Envio format: + +- Remove `@entity` decorators +- Change `Bytes!` to `String!` +- Change `ID!` to `ID!` (keep as is) +- Keep all business logic entities + +**CRITICAL: Verify Schema Completeness** +After migrating the schema, **VERIFY that it is IDENTICAL** to the original subgraph schema (apart from Envio vs TheGraph syntax): + +**Check for Missing Derived Fields:** + +- Ensure all `@derivedFrom` relationships are preserved +- Verify all entity arrays are maintained (e.g., `[TokenDayData!]! @derivedFrom(field: "token")`) +- Check that all entity relationships are preserved (e.g., `token0: Token!`, not `token0_id: String!`) + +**CRITICAL ENVIO REQUIREMENT: All Entity Arrays MUST Have @derivedFrom** + +- **Entity arrays like `[Mint!]!` are ONLY valid with `@derivedFrom`** +- **Arrays without `@derivedFrom` will cause codegen to fail** with error "EE211: Arrays of entities is unsupported" +- **The `@derivedFrom(field: "fieldName")` syntax tells Envio how to establish relationships** + +**Validation Steps:** + +1. Run `pnpm codegen` to ensure schema compiles +2. Compare line-by-line with original subgraph schema +3. Verify no business logic entities or fields are missing +4. Confirm all relationships and derived fields are preserved +5. **Ensure ALL entity arrays have `@derivedFrom` directives** + +**Only Acceptable Differences:** + +- `@entity` decorators removed +- `Bytes!` → `String!` +- **IMPORTANT:** Check Envio documentation for other potential syntax differences +- **IMPORTANT:** When in doubt about syntax differences, refer to Envio docs: https://docs.envio.dev + +**CRITICAL ENVIO REQUIREMENT - Entity Arrays Must Have @derivedFrom:** + +```graphql +# ❌ WRONG - Will cause codegen to fail with "EE211: Arrays of entities is unsupported" +type Transaction { + mints: [Mint!]! # Missing @derivedFrom + burns: [Burn!]! # Missing @derivedFrom + swaps: [Swap!]! # Missing @derivedFrom +} + +# ✅ CORRECT - All arrays have @derivedFrom directives +type Transaction { + mints: [Mint!]! @derivedFrom(field: "transaction") + burns: [Burn!]! @derivedFrom(field: "transaction") + swaps: [Swap!]! @derivedFrom(field: "transaction") +} +``` + +**CRITICAL: How @derivedFrom Actually Works in Envio** +According to [Envio documentation](https://docs.envio.dev/docs/HyperIndex/schema#relationships-one-to-many-derivedfrom): + +- **`@derivedFrom` arrays are VIRTUAL fields** - they don't exist in generated types +- **They're populated automatically when querying the API**, not in handlers +- **You CANNOT access them in handlers** (e.g., `transaction.mints` will not work) +- **The correct approach is to use `_id` fields** to establish relationships + +**Example of Correct Implementation:** + +```typescript +// ❌ WRONG - Trying to access virtual arrays in handlers +const mints = transaction.mints; // This will NOT work - arrays don't exist in types + +// ✅ CORRECT - Use indexed field operations to query related entities +const mint = await context.Mint.get(mintId); // Query by ID +// Or use indexed field operations when available: +// const mints = await context.Mint.where.transaction_id.eq(transactionId); +``` + +**Why This Matters:** + +- **Envio requires explicit relationship definitions** via `@derivedFrom +- **But the arrays are virtual** - they exist only in the API, not in handlers +- **This is fundamentally different from TheGraph** where arrays are actual properties +- **You must use indexed field operations** to query related entities in handlers + +**✅ Step 2 Quality Check:** + +#### **2.1: Review Migration Patterns and Quality Checks** + +- **Read the "Migration Patterns and Quality Checks" section** +- **Ensure your code follows all documented patterns** (entity creation, updates, BigDecimal precision, etc.) +- **Check compliance with Effect API requirements** for external calls +- **Verify all quality check issues are addressed** (imports, types, precision, etc.) + +#### **2.2: Run Technical Validation** + +```bash +# Run these commands to verify Step 2 completion +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +_Note: Some failures are expected if subsequent steps aren't complete yet - that's okay!_ + +### Step 3: Refactor File Structure to Mirror Subgraph + +**IMPORTANT: This step is ONLY about creating the file structure and skeleton handlers. DO NOT implement business logic yet.** + +**CREATE directory structure to match the original subgraph EXACTLY:** + +```bash +# Create the exact same files as in the original subgraph +src/ +├── utils/ +│ ├── file1.ts (skeleton - exact filename from subgraph) +│ ├── file2.ts (skeleton - exact filename from subgraph) +│ ├── file3.ts (skeleton - exact filename from subgraph) +│ └── file4.ts (skeleton - exact filename from subgraph) +├── handlers/ +│ ├── handler1.ts (skeleton - exact filename from subgraph) +│ └── handler2.ts (skeleton - exact filename from subgraph) +``` + +**CRITICAL: Use the EXACT same filenames as the original subgraph, not generic names.** + +**MOVE handlers from EventHandlers.ts to contract-specific files:** + +```typescript +// OLD: Single EventHandlers.ts file +// src/EventHandlers.ts - All handlers in one file + +// NEW: Separate files by contract +// src/contract1.ts - Contract1 handlers (skeleton only) +// src/contract2.ts - Contract2 handlers (skeleton only) +// src/contract3.ts - Contract3 handlers (skeleton only) +``` + +**UPDATE config.yaml to point to specific handler files:** + +```yaml +# OLD: Single handler file +- name: Contract1 + handler: src/EventHandlers.ts + +# NEW: Contract-specific handler files +- name: Contract1 + handler: src/contract1.ts +- name: Contract2 + handler: src/contract2.ts +- name: Contract3 + handler: src/contract3.ts +``` + +**REMOVE the original EventHandlers.ts file** after moving all handlers. + +**CRITICAL: Keep handlers as skeletons with TODO comments only:** + +```typescript +// ✅ CORRECT - Skeleton handler for this step +export function handleEventName( + event: Contract_EventName_event, + context: any +): void { + // TODO: Implement business logic from subgraph + // Reference: original-subgraph/src/mappings/contract.ts +} + +// ❌ WRONG - Don't implement business logic in this step +export function handleEventName( + event: Contract_EventName_event, + context: any +): void { + // Implementation details here... + let entity = context.Entity.get(id); + // ... more implementation +} +``` + +**Utility files should also be skeletons with exact filenames:** + +```typescript +// ✅ CORRECT - Skeleton utility files with exact subgraph filenames +// src/utils/file1.ts +// TODO: Add helper functions from original subgraph +// Reference: original-subgraph/src/utils/file1.ts + +// src/utils/file2.ts +// TODO: Add helper functions from original subgraph +// Reference: original-subgraph/src/utils/file2.ts + +// src/utils/file3.ts +// TODO: Add helper functions from original subgraph +// Reference: original-subgraph/src/utils/file3.ts +``` + +**IMPORTANT ADDITIONAL STEP: Fix Duplicate Contract Names in config.yaml** + +**When using multichain indexing, ensure contract names are unique across all networks:** + +```yaml +# ✅ CORRECT - Global contract definitions with network-specific addresses +contracts: + - name: Factory + handler: src/factory.ts + events: + - event: ContractCreated(...) + +networks: + - id: 1 + start_block: 0 + contracts: + - name: Factory + address: + - 0xFactoryAddress1 + - id: 10 + start_block: 0 + contracts: + - name: Factory + address: + - 0xFactoryAddress2 +``` + +**❌ WRONG - Don't duplicate contract definitions in network sections:** + +```yaml +# ❌ WRONG - This will cause "Duplicate contract names detected" error +networks: + - id: 1 + contracts: + - name: Factory + handler: src/factory.ts # Don't repeat handler/events here + events: [...] + address: [...] + - id: 10 + contracts: + - name: Factory + handler: src/factory.ts # Don't repeat handler/events here + events: [...] + address: [...] +``` + +**Key Points:** + +- **Global contracts section** defines handlers and events +- **Network sections** only define addresses for those contracts +- **Never repeat** handler, events, or other contract configuration in network sections +- **Use `unordered_multichain_mode: true`** for proper multichain support + +**✅ Step 3 Quality Check:** + +#### **3.1: Review Migration Patterns and Quality Checks** + +- **Read the "Migration Patterns and Quality Checks" section** +- **Ensure your code follows all documented patterns** (entity creation, updates, BigDecimal precision, etc.) +- **Check compliance with Effect API requirements** for external calls +- **Verify all quality check issues are addressed** (imports, types, precision, etc.) + +#### **3.2: Run Technical Validation** + +```bash +# Run these commands to verify Step 3 completion +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +_Note: Some failures are expected if subsequent steps aren't complete yet - that's okay!_ + +### Step 4: Register Dynamic Contracts with Factory Events + +**CRITICAL: This step is essential for Envio to track dynamically created contracts.** + +**IDENTIFY dynamic contracts in the original subgraph.yaml:** +Look for contracts that have **NO address** - these are created by factory contracts and need to be registered with Envio. + +**EXAMPLE from subgraph.yaml:** + +```yaml +# Factory contract (has address) +- kind: ethereum/contract + name: Factory + source: + address: '0x...' # ✅ Has address + +# Dynamic contract (no address - created by factory) +templates: + - kind: ethereum/contract + name: Pair # ❌ No address - created dynamically + source: + abi: Pair +``` + +**IMPLEMENT contract registration for factory events:** +Add `contractRegister` above the handler for events that create new contracts: + +```typescript +// ✅ CORRECT - Register dynamic contract with Envio +Factory.PairCreated.contractRegister(({ event, context }) => { + context.addPair(event.params.pair); +}); + +// Then implement the handler +Factory.PairCreated.handler(async ({ event, context }) => { + // TODO: Implement business logic from subgraph + // Reference: original-subgraph/src/mappings/factory.ts +}); +``` + +**UPDATE config.yaml for dynamic contracts:** +Remove the `address` field from contracts that are created dynamically by factories: + +```yaml +# ✅ CORRECT - Factory contract (has address) +- name: Factory + address: + - 0xFactoryAddress + handler: src/factory.ts + +# ✅ CORRECT - Dynamic contract (no address - created by factory) +- name: Pair + handler: src/core.ts # No address field! + events: + - event: Mint(...) + handler: handleMint +``` + +**❌ WRONG - Don't include addresses for dynamic contracts:** + +```yaml +# ❌ WRONG - Dynamic contract should not have address +- name: Pair + address: # This should be removed! + - 0xSomeAddress + handler: src/core.ts +``` + +**COMMON PATTERNS:** + +```typescript +// For Pair contracts +Factory.PairCreated.contractRegister(({ event, context }) => { + context.addPair(event.params.pair); +}); + +// For Vault contracts +VaultFactory.VaultCreated.contractRegister(({ event, context }) => { + context.addVault(event.params.vault); +}); + +// For OrderBook contracts +OrderbookFactory.OrderbookCreated.contractRegister(({ event, context }) => { + context.addOrderBook(event.params.orderbook); +}); + +// For Pool contracts +PoolFactory.PoolCreated.contractRegister(({ event, context }) => { + context.addPool(event.params.pool); +}); +``` + +**IMPORTANT NOTES:** + +- **MUST be placed above the handler** for the same event +- **MUST use the exact contract name** from your config.yaml (e.g., `addPair` for `Pair` contracts) +- **MUST reference the correct event parameter** that contains the new contract address +- **Without this, Envio cannot index the dynamically created contracts** + +**✅ Step 4 Quality Check:** + +#### **4.1: Review Migration Patterns and Quality Checks** + +- **Read the "Migration Patterns and Quality Checks" section** +- **Ensure your code follows all documented patterns** (entity creation, updates, BigDecimal precision, etc.) +- **Check compliance with Effect API requirements** for external calls +- **Verify all quality check issues are addressed** (imports, types, precision, etc.) + +#### **4.2: Run Technical Validation** + +```bash +# Run these commands to verify Step 4 completion +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +_Note: Some failures are expected if subsequent steps aren't complete yet - that's okay!_ + +### Step 5: Reference Original Subgraph Logic + +For each handler, examine the corresponding subgraph file: + +- `original-subgraph/src/contract1.ts` → Contract1 handlers +- `original-subgraph/src/contract2.ts` → Contract2 handlers +- `original-subgraph/src/contract3.ts` → Contract3 handlers + +## Step 5a: Implement Helper Functions with No Dependencies + +**🚨 MANDATORY: When implementing helper functions, you MUST implement the COMPLETE logic, not just placeholders! 🚨** + +### What Are "Dependencies"? + +**Dependencies** are functions, entities, or handlers that your helper function needs to work properly: + +- **Entity dependencies**: Functions that need to load/update entities (e.g., `context.Entity.get()`) +- **Handler dependencies**: Functions that depend on other event handlers being complete +- **Function dependencies**: Functions that call other helper functions + +### Implement Helper Functions IMMEDIATELY If: + +- **No dependencies on incomplete entities/handlers** +- **Only depends on constants, basic math, or already implemented pieces** +- **Required for the current handler to function properly** + +### 🚨 CRITICAL IMPLEMENTATION REQUIREMENT: + +**When implementing helper functions that have no dependencies, you MUST:** + +1. **Read the MDC file** for existing implementation patterns and solutions +2. **Implement the COMPLETE business logic** from the original subgraph +3. **Use the documented Envio patterns** (RPC calls, entity context, etc.) +4. **Include all fallback logic** and error handling from the original +5. **NOT just add TODO comments** or placeholder implementations +6. **Implement ALL function dependencies** that have no entity/handler dependencies + +### 🚨 CRITICAL: External Calls MUST Use Effect API + +**When implementing helper functions that make external calls (RPC calls, API calls, etc.), you MUST:** + +1. **Use the Effect API** for ALL external calls - see "External Calls and Effect API" section below +2. **Use viem transport batching** if using viem for blockchain RPC calls - see "Contract State Fetching Migration" section below +3. **Consult Envio documentation** at https://docs.envio.dev/docs/HyperIndex-LLM/hyperindex-complete for correct implementation patterns +4. **Implement proper error handling** with fallback values +5. **Use context.effect()** in handlers to call the effects +6. **Enable caching** in effects when appropriate for performance + +**❌ WRONG - Just adding TODO comments:** + +```typescript +export function fetchTokenSymbol(tokenAddress: string): string { + // TODO: Implement using Envio's RPC calls to ERC20 contracts + return "UNKNOWN"; // This is NOT acceptable! +} +``` + +**✅ CORRECT - Complete implementation:** + +````typescript +export function fetchTokenSymbol(tokenAddress: string): string { + // Implement complete logic using documented Envio patterns + // Include all fallback logic and error handling + // Return actual working values, not placeholders +} + +### Example of Helper with No Dependencies: +```typescript +// ✅ IMPLEMENT NOW - No dependencies on incomplete entities/handlers +export function isNullEthValue(value: string): boolean { + // Only depends on string comparison - no entity or handler dependencies + return value === '0x0000000000000000000000000000000000000000000000000000000000000001' || + value === '0x0000000000000000000000000000000000000000000000000000000000000000'; +} + +export function convertEthToDecimal(eth: BigInt): BigDecimal { + // Only depends on constants and BigDecimal math - no entity or handler dependencies + return eth.divDecimal(BigDecimal.fromString('1000000000000000000')); +} + +export function getStaticDefinition(address: string): StaticDefinition | null { + // Only depends on static data arrays - no entity or handler dependencies + const staticDefinitions: StaticDefinition[] = [ + { address: '0x6b175474e89094c44da98b954eedeac495271d0f', symbol: 'DAI', name: 'Dai Stablecoin' }, + { address: '0xa0b86a33e6441b8c4c8c0e4c0e4c0e4c0e4c0e4c', symbol: 'USDC', name: 'USD Coin' }, + ]; + + return staticDefinitions.find(def => def.address.toLowerCase() === address.toLowerCase()) || null; +} +```` + +**Why these have no dependencies:** + +- **No `context.Entity.get()` calls** - don't need to load entities +- **No other helper function calls** - self-contained logic +- **Only use constants, math, or static data** - no dynamic dependencies +- **Can be implemented immediately** without waiting for other code + +### 🔍 CRITICAL: Check Function Dependencies Thoroughly + +**When implementing helper functions, you MUST examine ALL function calls within them:** + +1. **Look for function calls** like `getStaticDefinition()`, `isNullEthValue()`, etc. +2. **Check if these functions have dependencies** on incomplete entities/handlers +3. **If NO dependencies exist, implement them immediately** - don't skip them! + +**❌ WRONG - Missing dependency implementation:** + +```typescript +export function fetchTokenSymbol(tokenAddress: string): string { + // Missing: const staticDefinition = getStaticDefinition(tokenAddress); + // Missing: if (staticDefinition !== null) return staticDefinition.symbol; + + // Only implementing the RPC call part + return "unknown"; +} +``` + +**✅ CORRECT - Complete dependency implementation:** + +```typescript +export function fetchTokenSymbol(tokenAddress: string): string { + // ✅ IMPLEMENTED: Static definition check + const staticDefinition = getStaticDefinition(tokenAddress); + if (staticDefinition !== null) { + return staticDefinition.symbol; + } + + // ✅ IMPLEMENTED: RPC call logic + // ... rest of implementation +} +``` + +**✅ Step 5a Quality Check:** + +#### **5a.1: Review Migration Patterns and Quality Checks** + +- **Read the "Migration Patterns and Quality Checks" section** +- **Ensure your code follows all documented patterns** (entity creation, updates, BigDecimal precision, etc.) +- **Check compliance with Effect API requirements** for external calls +- **Verify all quality check issues are addressed** (imports, types, precision, etc.) + +#### **5a.2: Run Technical Validation** + +```bash +# Run these commands to verify Step 5a completion +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +_Note: Some failures are expected if subsequent steps aren't complete yet - that's okay!_ + +## Step 5b: Implement Simple Handlers + +**Handlers that only set parameter values with minimal processing:** + +- **Handlers that only set parameter values** with minimal processing +- **Handlers that load existing entities** and update them with new data +- **Basic event handlers** with no complex business logic or helper function calls + +**✅ Step 5b Quality Check:** + +#### **5b.1: Review Migration Patterns and Quality Checks** + +- **Read the "Migration Patterns and Quality Checks" section** +- **Ensure your code follows all documented patterns** (entity creation, updates, BigDecimal precision, etc.) +- **Check compliance with Effect API requirements** for external calls +- **Verify all quality check issues are addressed** (imports, types, precision, etc.) + +#### **5b.2: Run Technical Validation** + +```bash +# Run these commands to verify Step 5b completion +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +_Note: Some failures are expected if subsequent steps aren't complete yet - that's okay!_ + +## Step 5c: Implement Moderate Complexity Handlers + Required Helper Functions + +**Handlers that call helper functions but don't create complex entity relationships:** + +- **Handlers that call helper functions** but don't create complex entity relationships +- **Handlers that update multiple entities** but with straightforward logic +- **Event handlers with some business logic** but clear, simple dependencies + +### Helper Function Implementation Strategy for This Step: + +**As you implement each moderate complexity handler, implement any helper functions it calls along the way:** + +1. **Identify helper functions** called by the current handler +2. **Check if they have dependencies** on incomplete entities/handlers +3. **If NO dependencies exist, implement them immediately** +4. **If they have dependencies, implement their dependencies first** +5. **Continue this process recursively** until all required helpers are complete + +**✅ Step 5c Quality Check:** + +#### **5c.1: Review Migration Patterns and Quality Checks** + +- **Read the "Migration Patterns and Quality Checks" section** +- **Ensure your code follows all documented patterns** (entity creation, updates, BigDecimal precision, etc.) +- **Check compliance with Effect API requirements** for external calls +- **Verify all quality check issues are addressed** (imports, types, precision, etc.) + +#### **5c.2: Run Technical Validation** + +```bash +# Run these commands to verify Step 5c completion +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +_Note: Some failures are expected if subsequent steps aren't complete yet - that's okay!_ + +## Step 5d: Implement Complex Handlers (One at a Time) + +**Handlers that call multiple helper functions or other handlers:** + +- **Handlers that call multiple helper functions** or other handlers +- **Handlers with complex business logic** and multiple entity relationships +- **Handlers that require extensive data processing** or calculations +- **Handlers that implement contract binding** (RPC calls to fetch state) +- **Handlers that depend on multiple other handlers** being complete + +### Implementation Strategy for Complex Handlers: + +1. **Implement ONE complex handler at a time** +2. **Identify all helper functions** it calls +3. **Implement any missing helper functions** (following dependency rules) +4. **Run quality checks after EACH helper function implementation** +5. **Run quality checks after the handler implementation** +6. **Move to the next complex handler** only after the current one is complete + +**✅ Quality Check After Each Helper Function:** + +```bash +# Run these commands after implementing EACH helper function +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +**✅ Quality Check After Each Handler:** + +```bash +# Run these commands after implementing EACH complex handler +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +_Note: Each handler and its helper functions should be fully functional before moving to the next one!_ + +### Why This Order Matters: + +- Prevents "entity not found" errors during development +- Ensures required entities exist before complex handlers try to use them +- Allows incremental testing and validation +- Reduces circular dependency issues +- Enables systematic debugging and validation + +**✅ Step 5 Quality Check:** + +#### **5.1: Review Migration Patterns and Quality Checks** + +- **Read the "Migration Patterns and Quality Checks" section** +- **Ensure your code follows all documented patterns** (entity creation, updates, BigDecimal precision, etc.) +- **Check compliance with Effect API requirements** for external calls +- **Verify all quality check issues are addressed** (imports, types, precision, etc.) + +#### **5.2: Run Technical Validation** + +```bash +# Run these commands to verify Step 5 completion +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +_Note: Some failures are expected if subsequent steps aren't complete yet - that's okay!_ + +## **Step 6: Final Migration Verification** + +**🚨 CRITICAL: This is the final step to ensure complete migration accuracy. NO TODOs should remain - absolutely everything must be implemented, regardless of complexity.** + +### **6.1: Systematic Handler Logic Review** + +**Go through each handler in each file one by one:** + +1. **For each handler, compare the logic to the subgraph implementation** +2. **Identify any missing logic or unnecessary logic** +3. **Update the handler accordingly** +4. **Run the same check again on the updated code** (you'll likely find something else/new) +5. **Repeat this process until the logic is correct** +6. **Do this slowly, one by one, for every handler** + +**Why this iterative approach is critical:** + +- **First pass** often misses subtle logic differences +- **Second pass** reveals issues introduced by the first pass +- **Multiple iterations** ensure complete accuracy +- **Slow, methodical review** prevents missing critical details + +### **6.2: Systematic Helper Function Logic Review** + +**Go through each helper function in every file one by one:** + +1. **For each helper function, compare the logic to the subgraph implementation** +2. **Identify any missing logic or unnecessary logic** +3. **Update the helper function accordingly** +4. **Run the same check again on the updated code** +5. **Repeat this process until the logic is correct** +6. **Do this slowly, one by one, for every helper function** + +### **6.3: MDC File Consultation** + +**When uncertain about something, ALWAYS consult the MDC file:** + +- **Check existing solutions** for similar problems +- **Reference the quality check checklist** to avoid common mistakes +- **Look for similar examples** in the MDC file +- **Use documented Envio patterns** instead of guessing + +### **6.4: Implementation Order** + +**Follow this order for maximum efficiency:** + +1. **Start with simple handlers** - those with minimal business logic +2. **Move to complex handlers** - those with extensive calculations or multiple entity updates +3. **Review helper functions** - after handlers are complete +4. **Final integration check** - ensure all pieces work together + +### **6.5: Verification Checklist** + +**After each handler/function review, verify:** + +- [ ] **Logic matches subgraph exactly** - no missing or extra logic +- [ ] **All edge cases handled** - same conditional branches as subgraph +- [ ] **Entity operations correct** - proper loading, updating, saving +- [ ] **Helper function calls complete** - all required functions called +- [ ] **Error handling matches** - same try/catch and validation logic +- [ ] **Calculations identical** - same mathematical operations and precision + +### **Example of Complete Logic Review:** + +```typescript +// ❌ BEFORE - Missing logic from subgraph +export async function handleMint(event: any, context: any) { + const mint: Mint = { + id: mintId, + amount0: event.params.amount0, + amount1: event.params.amount1, + // Missing: sender, to, feeTo, feeLiquidity, amountUSD calculations + }; + context.Mint.set(mint); +} + +// ✅ AFTER - Complete logic matching subgraph +export async function handleMint(event: any, context: any) { + // Load existing mint entity (created by Transfer handler) + const existingMint = await context.Mint.getWhere.transaction_id.eq( + transactionId + ); + + if (existingMint) { + // Update existing mint with complete data + const updatedMint: Mint = { + ...existingMint, + sender: event.params.sender, + to: event.params.to, + amount0: event.params.amount0, + amount1: event.params.amount1, + feeTo: feeToAddress, + feeLiquidity: calculatedFeeLiquidity, + amountUSD: calculatedAmountUSD, + logIndex: event.logIndex, + }; + context.Mint.set(updatedMint); + } + + // Update all related entities (token0, token1, pair, factory) + // ... complete implementation matching subgraph +} +``` + +### **Why This Step is Critical:** + +- **Ensures complete migration** - no functionality is lost +- **Maintains data accuracy** - same business logic as original +- **Prevents runtime errors** - all edge cases are handled +- **Guarantees compatibility** - same behavior as original subgraph + +**This systematic review is the final quality gate before your migration is production-ready.** + +**✅ Step 6 Quality Check:** + +#### **6.1: Review Migration Patterns and Quality Checks** + +- **Read the "Migration Patterns and Quality Checks" section** +- **Ensure your code follows all documented patterns** (entity creation, updates, BigDecimal precision, etc.) +- **Check compliance with Effect API requirements** for external calls +- **Verify all quality check issues are addressed** (imports, types, precision, etc.) + +#### **6.2: Run Technical Validation** + +```bash +# Run these commands to verify Step 6 completion +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +## **Step 7: Environment Variables Setup** + +**Search the codebase for all environment variables and update the example env file.** + +### **7.1: Find Environment Variables** + +```bash +# Search for all env variables in the codebase +grep -r "process.env\." src/ +grep -r "process.env\[" src/ +``` + +### **7.2: Update Example Environment File** + +- **Add all found environment variables** to `.env.example` +- **Include descriptive comments** for each variable +- **Set appropriate example values** (not real secrets) + +**✅ Step 7 Quality Check:** + +```bash +# Verify all env variables are documented +cat .env.example +``` + +## Migration Patterns and Quality Checks + +**CRITICAL: At the end of each migration step, ALWAYS check for and fix these common issues:** + +### Migration Patterns and Common Patterns + +### Entity Creation Pattern + +```typescript +// OLD SUBGRAPH PATTERN: +let entity = new EventEntity( + event.transaction.hash.concatI32(event.logIndex.toI32()) +); +entity.field1 = event.params.field1; +entity.save(); + +// NEW ENVIO PATTERN: +const entity: EventEntity = { + id: `${event.chainId}-${event.transaction.hash}-${event.logIndex}`, + field1: event.params.field1, + blockNumber: BigInt(event.block.number), + blockTimestamp: BigInt(event.block.timestamp), + transactionHash: event.transaction.hash, +}; + +// ⚠️ REMINDER: transaction.hash requires field_selection in config.yaml: +// - event: EventName(...) +// field_selection: +// transaction_fields: +// - hash + +context.EventEntity.set(entity); +``` + +### Entity Updates Pattern + +```typescript +// OLD SUBGRAPH PATTERN: +let entity = store.get("EntityName", id); +if (entity) { + entity.field = newValue; + entity.save(); +} + +// NEW ENVIO PATTERN: +let entity = await context.EntityName.get(id); +if (entity) { + const updatedEntity: EntityName = { + ...entity, + field: newValue, + updatedAt: BigInt(Date.now()), + }; + context.EntityName.set(updatedEntity); +} +``` + +### Contract Registration Pattern + +```typescript +// OLD SUBGRAPH PATTERN: +ContractTemplate.create(event.params.contract); + +// NEW ENVIO PATTERN: +Contract.EventCreated.contractRegister(({ event, context }) => { + context.addContract(event.params.contract); +}); +``` + +### CRITICAL: Maintain BigDecimal Precision from Original Subgraph + +**IMPORTANT: When migrating from TheGraph to Envio, you MUST maintain the same mathematical precision used in the original subgraph.** + +**❌ WRONG - Don't simplify to basic JavaScript types:** + +```typescript +// ❌ WRONG - Loses precision for financial calculations +export const ZERO_BD = 0; +export const ONE_BD = 1; + +export function convertTokenToDecimal( + tokenAmount: bigint, + exchangeDecimals: bigint +): number { + return Number(tokenAmount) / Math.pow(10, Number(exchangeDecimals)); +} +``` + +**✅ CORRECT - Maintain BigDecimal precision:** + +```typescript +// ✅ CORRECT - Maintains precision like original subgraph +import { BigDecimal } from "generated"; + +export const ZERO_BD = new BigDecimal(0); +export const ONE_BD = new BigDecimal(1); + +export function convertTokenToDecimal( + tokenAmount: bigint, + exchangeDecimals: bigint +): BigDecimal { + if (exchangeDecimals == ZERO_BI) { + return new BigDecimal(tokenAmount.toString()); + } + return new BigDecimal(tokenAmount.toString()).div( + exponentToBigDecimal(exchangeDecimals) + ); +} +``` + +**Why This Matters:** + +- **Financial Precision**: Token amounts, prices, and volumes require exact decimal arithmetic +- **Avoid Floating-Point Errors**: JavaScript numbers can introduce precision errors in financial calculations +- **Consistency**: Maintains the same mathematical behavior as the original subgraph +- **Regulatory Compliance**: Financial applications often require exact precision + +**Required Dependencies:** + +```bash +# Install bignumber.js for BigDecimal support +pnpm add bignumber.js +``` + +**Constants to Always Preserve:** + +```typescript +// These constants MUST be maintained from original subgraph +export const ZERO_BI = BigInt(0); +export const ONE_BI = BigInt(1); +export const ZERO_BD = new BigDecimal(0); +export const ONE_BD = new BigDecimal(1); +export const ADDRESS_ZERO = "0x0000000000000000000000000000000000000000"; +export const BI_18 = BigInt(18); +export const ALMOST_ZERO_BD = new BigDecimal("0.000001"); +``` + +**Helper Function Return Types:** + +```typescript +// ✅ CORRECT - Return BigDecimal for financial calculations +export function exponentToBigDecimal(decimals: bigint): BigDecimal; +export function convertTokenToDecimal( + tokenAmount: bigint, + exchangeDecimals: bigint +): BigDecimal; +export function fetchTokenTotalSupply(tokenAddress: string): BigDecimal; +export function equalToZero(value: BigDecimal): boolean; + +// ❌ WRONG - Don't return number for financial calculations +export function convertTokenToDecimal( + tokenAmount: bigint, + exchangeDecimals: bigint +): number; +export function fetchTokenTotalSupply(tokenAddress: string): number; +``` + +**Entity Field Initialization:** + +```typescript +// ✅ CORRECT - Use constants for initialization +const pair: Pair = { + id: event.params.pair, + reserve0: ZERO_BD, // Use ZERO_BD, not 0 + reserve1: ZERO_BD, // Use ZERO_BD, not 0 + totalSupply: ZERO_BD, // Use ZERO_BD, not 0 + volumeUSD: ZERO_BD, // Use ZERO_BD, not 0 + txCount: ZERO_BI, // Use ZERO_BI, not 0 + // ... other fields +}; + +// ❌ WRONG - Don't use hardcoded 0 values +const pair: Pair = { + id: event.params.pair, + reserve0: 0, // ❌ Loses precision + reserve1: 0, // ❌ Loses precision + totalSupply: 0, // ❌ Loses precision + volumeUSD: 0, // ❌ Loses precision + txCount: 0, // ❌ Wrong type + // ... other fields +}; +``` + +**Common Mistakes to Avoid:** + +1. **Replacing `ZERO_BD` with `0`** - Loses precision +2. **Replacing `ZERO_BI` with `0`** - Wrong type +3. **Returning `number` instead of `BigDecimal`** - Loses precision +4. **Using `Math.pow()` instead of `BigDecimal` arithmetic** - Loses precision +5. **Simplifying constants to basic JavaScript types** - Breaks financial calculations + +**Validation Steps:** + +1. **Check all constants** use `BigDecimal` and `BigInt` types +2. **Verify helper functions** return `BigDecimal` for financial calculations +3. **Ensure entity initialization** uses `ZERO_BD` and `ZERO_BI` constants +4. **Test precision** with large numbers to ensure no floating-point errors +5. **Compare behavior** with original subgraph for mathematical consistency + +## External Calls and Effect API + +**🚨 CRITICAL: ALL external calls MUST use the Effect API when `preload_handlers: true` is enabled in config.yaml.** + +### Overview + +When migrating from TheGraph to Envio, external calls (RPC calls, API calls, etc.) must use the Effect API instead of direct calls. This is mandatory when preload optimizations are enabled. + +### Effect API Pattern + +Add `preload_handlers: true` to the `config.yaml` file to enable preload optimisations. With preload optimisations, handlers will run twice. + +**So if there's an external call, you MUST use the Effect API to make it.** + +**Organization:** Create Effect API functions in `src/effects/` folder for better structure. + +```ts +// Import the Effect API from "envio" +import { S, createEffect } from "envio"; + +// Define an effect. It can have any name you want. +export const getSomething = createEffect( + { + // The name for debugging purposes + name: "getSomething", + // The input schema for the effect + input: { + address: S.string, + blockNumber: S.number, + }, + output: S.union([S.string, null]), + rateLimit: false, + cache: true, + }, + async ({ input, context }) => { + // Fetch or other external calls MUST always be done in an effect. + const something = await fetch( + `https://api.example.com/something?address=${input.address}&blockNumber=${input.blockNumber}` + ); + return something.json(); + } +); +``` + +The `S` module exposes a schema creation API: https://raw.githubusercontent.com/DZakh/sury/refs/tags/v9.3.0/docs/js-usage.md + +```ts +import { getSomething } from "./effects"; + +Contract.Event.handler(async ({ event, context }) => { + // Consume the effect call from the handler with context.effect + const something = await context.effect(getSomething, { + address: event.srcAddress, + blockNumber: event.block.number, + }); + // Other handler code... +}); +``` + +You can also use `!context.isPreload` check, to prevent some logic to run during preload. + +## Contract State Fetching Migration + +### Overview + +When migrating from TheGraph to Envio, contract state fetching patterns need to be updated. TheGraph uses `.bind()` patterns for contract state access, while Envio requires explicit RPC calls using the Effect API. + +### 1. Identify Contract State Usage + +**Look for `.bind()` patterns in the original subgraph:** + +```typescript +// OLD SUBGRAPH PATTERN - Contract State Fetching +let token = Token.bind(event.params.token); +let vault = Vault.bind(event.params.vault); + +// Access contract state +entity.name = token.name(); +entity.symbol = token.symbol(); +entity.decimals = token.decimals(); +entity.totalSupply = token.totalSupply(); + +// Vault state +entity.asset = vault.asset(); +entity.totalAssets = vault.totalAssets(); +entity.totalShares = vault.totalSupply(); +``` + +### 2. Create Effect for Token Metadata Fetching + +**🚨 CRITICAL: Use the Effect API for all external calls (RPC calls, API calls, etc.)** + +```typescript +// src/effects/tokenMetadata.ts +import { createEffect, S } from "envio"; +import { createPublicClient, http, parseAbi } from "viem"; + +// ERC20 ABI for basic token functions +const ERC20_ABI = parseAbi([ + "function name() view returns (string)", + "function symbol() view returns (string)", + "function decimals() view returns (uint8)", + "function totalSupply() view returns (uint256)", +]); + +// Create a public client for reading contract state +const publicClient = createPublicClient({ + chain: { + id: 6342, // MegaETH Testnet - adjust for your network + name: "MegaETH Testnet", + network: "megaeth-testnet", + nativeCurrency: { + decimals: 18, + name: "MegaETH", + symbol: "METH", + }, + rpcUrls: { + default: { + http: [process.env.RPC_URL], + }, + public: { + http: [process.env.RPC_URL], + }, + }, + }, + transport: http(process.env.RPC_URL), +}); + +// Note: Pass chain-specific RPC via env per network or keep a registry keyed by event.chainId + +// Define the effect to fetch token metadata +export const getTokenMetadata = createEffect( + { + name: "getTokenMetadata", + input: S.string, // Token contract address + output: S.object({ + name: S.string, + symbol: S.string, + decimals: S.number, + totalSupply: S.string, // BigInt as string + }), + rateLimit: false, + cache: true, + }, + async ({ input: tokenAddress, context }) => { + try { + const [name, symbol, decimals, totalSupply] = await Promise.all([ + publicClient.readContract({ + address: tokenAddress as `0x${string}`, + abi: ERC20_ABI, + functionName: "name", + }), + publicClient.readContract({ + address: tokenAddress as `0x${string}`, + abi: ERC20_ABI, + functionName: "symbol", + }), + publicClient.readContract({ + address: tokenAddress as `0x${string}`, + abi: ERC20_ABI, + functionName: "decimals", + }), + publicClient.readContract({ + address: tokenAddress as `0x${string}`, + abi: ERC20_ABI, + functionName: "totalSupply", + }), + ]); + + return { + name, + symbol, + decimals: Number(decimals), + totalSupply: totalSupply.toString(), + }; + } catch (error) { + context.log.error( + `Error fetching token metadata for ${tokenAddress}: ${error}` + ); + // Return default values on error + return { + name: "Unknown", + symbol: "Unknown", + decimals: 0, + totalSupply: "0", + }; + } + } +); +``` + +### 4. Handler Implementation Pattern Using Effect API + +**🚨 CRITICAL: Use Effect API for all external calls in handlers** + +```typescript +// OLD SUBGRAPH PATTERN: +Contract.EventName.handler((event) => { + let token = Token.bind(event.params.token); + let entity = new Entity(); + entity.name = token.name(); + entity.symbol = token.symbol(); + entity.save(); +}); + +// NEW ENVIO PATTERN WITH EFFECT API: +import { getTokenMetadata } from "./effects/tokenMetadata"; + +Contract.EventName.handler(async ({ event, context }) => { + try { + // Use Effect API to fetch contract state + const tokenMetadata = await context.effect( + getTokenMetadata, + event.params.token + ); + const vaultMetadata = await context.effect( + getTokenMetadata, + event.params.vault + ); // Vault is also an ERC20 token + + // Create entity with fetched data + const entity: Entity = { + id: `${event.chainId}-${event.transaction.hash}-${event.logIndex}`, + name: tokenMetadata.name, + symbol: tokenMetadata.symbol, + decimals: BigInt(tokenMetadata.decimals), + totalSupply: BigInt(tokenMetadata.totalSupply), + vaultName: vaultMetadata.name, + vaultSymbol: vaultMetadata.symbol, + vaultDecimals: BigInt(vaultMetadata.decimals), + vaultTotalSupply: BigInt(vaultMetadata.totalSupply), + blockNumber: BigInt(event.block.number), + blockTimestamp: BigInt(event.block.timestamp), + transactionHash: event.transaction.hash, + }; + + context.Entity.set(entity); + } catch (error) { + context.log.error(`Error in EventName handler: ${error}`); + // Don't throw - let the indexer continue + } +}); +``` + +### 5. Factory Contract Registration with Effect API + +**Example: Contract Factory with Effect API for contract state fetching:** + +```typescript +// Reference: original-subgraph/src/contractFactory.ts +import { getTokenMetadata } from "./effects/tokenMetadata"; + +ContractFactory.ContractCreated.contractRegister(({ event, context }) => { + context.addContract(event.params.contract); +}); + +ContractFactory.ContractCreated.handler(async ({ event, context }) => { + try { + // Get or create ContractFactory entity + let contractFactory = await context.ContractFactory.get(ONE_BI.toString()); + if (!contractFactory) { + contractFactory = { + id: ONE_BI.toString(), + totalContracts: ZERO_BI, + timestamp: BigInt(event.block.timestamp), + lastUpdate: BigInt(event.block.timestamp), + }; + } + + // Update contract factory stats + const updatedContractFactory = { + ...contractFactory, + totalContracts: contractFactory.totalContracts + ONE_BI, + lastUpdate: BigInt(event.block.timestamp), + }; + context.ContractFactory.set(updatedContractFactory); + + // Use Effect API to fetch ERC20 metadata for both contract and token (parallel) + const [contractMetadata, tokenMetadata] = await Promise.all([ + context.effect(getTokenMetadata, event.params.contract), // Contract is also an ERC20 token + context.effect(getTokenMetadata, event.params.token), // Token is an ERC20 token + ]); + + // Get or create Token entity + let token = await context.Token.get(event.params.token); + if (!token) { + token = { + id: event.params.token, + name: tokenMetadata.name, + symbol: tokenMetadata.symbol, + decimals: BigInt(tokenMetadata.decimals), + totalSupply: BigInt(tokenMetadata.totalSupply), + rate: ZERO_BD, + dataFeedId: event.params.token, + updatedAt: BigInt(event.block.timestamp), + blockNumber: BigInt(event.block.number), + blockTimestamp: BigInt(event.block.timestamp), + transactionHash: event.transaction.hash, + address: event.params.token, + }; + context.Token.set(token); + } + + // Calculate derived values + let convertToAssetsMultiplier = ONE_BD; + // In a real implementation, you would calculate this based on contract state + // For now, use ONE_BD to match the original subgraph logic + + // Create ContractDataEntity with fetched contract state + const contract: ContractDataEntity = { + id: event.params.contract, + name: contractMetadata.name, // Use contract's ERC20 metadata + symbol: contractMetadata.symbol, // Use contract's ERC20 metadata + decimals: BigInt(contractMetadata.decimals), // Use contract's ERC20 decimals + manager: event.params.poolManager, + timestamp: BigInt(event.block.timestamp), + lastUpdate: BigInt(event.block.timestamp), + fee: ZERO_BI, + depositApy: ZERO_BI, // Will be calculated later + convertToAssetsMultiplier, + totalDepositsVolume: ZERO_BI, + totalWithdrawalsVolume: ZERO_BI, + totalAssets: ZERO_BI, // Will be updated later + totalShares: ZERO_BI, // Will be updated later + token_id: event.params.token, + contractFactory_id: ONE_BI.toString(), + }; + + context.ContractDataEntity.set(contract); + } catch (error) { + context.log.error(`Error in ContractCreated handler: ${error}`); + // Don't throw - let the indexer continue + } +}); +``` + +### 6. Error Handling and Batch Calls + +**Always include proper error handling for Effect API calls:** + +```typescript +import { getTokenMetadata } from "./effects/tokenMetadata"; + +Contract.EventName.handler(async ({ event, context }) => { + try { + // Batch multiple Effect API calls for efficiency + const [tokenMetadata, vaultMetadata] = await Promise.all([ + context.effect(getTokenMetadata, event.params.token), + context.effect(getTokenMetadata, event.params.vault), // Vault is also an ERC20 token + ]); + + // Create entity with fetched data + const entity: Entity = { + // ... entity fields using all metadata + }; + + context.Entity.set(entity); + } catch (error) { + context.log.error(`Error in EventName handler: ${error}`); + + // Option 1: Use default values + const entity: Entity = { + // ... entity fields with defaults + }; + context.Entity.set(entity); + + // Option 2: Skip entity creation + // return; + + // Option 3: Re-throw to stop processing + // throw error; + } +}); +``` + +### 7. Configuration for Effect API + +**Add preload_handlers and field selection for events that need transaction data:** + +```yaml +# config.yaml +preload_handlers: true # Enable preload optimizations for Effect API + +contracts: + - name: ContractFactory + address: + - 0xContractFactoryAddress + handler: src/contractFactory.ts + events: + - event: ContractCreated(address indexed contract, address indexed token, address indexed poolManager) + field_selection: + transaction_fields: + - hash +``` + +**✅ Effect API Implementation Quality Check:** + +```bash +# Run these commands to verify Effect API implementation +pnpm codegen +pnpm tsc --noEmit +TUI_OFF=true pnpm dev +``` + +_Note: Some failures are expected if subsequent steps aren't complete yet - that's okay!_ + +### Quality Check Issues + +#### **Issue 1: Entity Type Import Confusion** + +**PROBLEM:** Importing entity types from `"generated"` instead of `"generated/src/db/Entities.gen"` +**SYMPTOM:** TypeScript errors like "Pair refers to a value, but is being used as a type" +**SOLUTION:** Use correct import paths: + +```typescript +// ❌ WRONG - Imports contract handlers, not entity types +import { Pair, Token } from "generated"; + +// ✅ CORRECT - Imports entity types +import { Pair_t, Token_t } from "generated/src/db/Entities.gen"; +``` + +#### **Issue 2: BigDecimal vs bigint Type Mismatches** + +**PROBLEM:** Using wrong types for entity fields +**SYMPTOM:** TypeScript errors like "Type 'BigNumber' is not assignable to type 'bigint'" +**SOLUTION:** Match entity field types exactly: + +```typescript +// ❌ WRONG - Token entity expects bigint for totalSupply +export function fetchTokenTotalSupply(tokenAddress: string): BigDecimal { + return ZERO_BD; +} + +// ✅ CORRECT - Token entity expects bigint for totalSupply +export function fetchTokenTotalSupply(tokenAddress: string): bigint { + return ZERO_BI; +} +``` + +#### **Issue 3: Entity Field Name Mismatches** + +**PROBLEM:** Using wrong field names that don't match generated types +**SYMPTOM:** TypeScript errors like "Property 'token0' does not exist on type 'Pair_t'" +**SOLUTION:** Use exact field names from generated types: + +```typescript +// ❌ WRONG - Field names don't match generated types +const pair: Pair_t = { + token0: token0.id, // Should be token0_id + token1: token1.id, // Should be token1_id +}; + +// ✅ CORRECT - Field names match generated types +const pair: Pair_t = { + token0_id: token0.id, + token1_id: token1.id, +}; +``` + +#### **Issue 4: Missing BigDecimal Import** + +**PROBLEM:** Not importing BigDecimal from the correct location +**SYMPTOM:** TypeScript errors like "Cannot find name 'BigDecimal'" +**SOLUTION:** Import from generated types: + +```typescript +// ❌ WRONG - Direct import from bignumber.js +import { BigDecimal } from "bignumber.js"; + +// ✅ CORRECT - Import from generated types (which re-exports BigNumber) +import { BigDecimal } from "generated"; +``` + +#### **Issue 5: Hardcoded Values Instead of Constants** + +**PROBLEM:** Using hardcoded addresses/values instead of constants defined in the original subgraph +**SYMPTOM:** Inconsistent with original subgraph patterns, potential for errors if addresses change +**SOLUTION:** Always use constants from the original subgraph: + +```typescript +// ❌ WRONG - Hardcoded address +const factory = await context.UniswapFactory.get( + "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f" +); + +// ✅ CORRECT - Use constant from original subgraph +const factory = await context.UniswapFactory.get(FACTORY_ADDRESS); +``` + +**CHECKLIST for Constants:** + +1. **Look for hardcoded addresses** in your handlers +2. **Check what constants the original subgraph uses** (e.g., `FACTORY_ADDRESS`, `ADDRESS_ZERO`) +3. **Import and use those constants** instead of hardcoded values +4. **Maintain consistency** with the original subgraph's approach + +**QUALITY CHECK CHECKLIST:** +After each step, run these commands to verify: + +```bash +# 1. Check if codegen still works +pnpm codegen + +# 2. Check if TypeScript compiles (skip lib check to avoid generated file issues) +npx tsc --noEmit --skipLibCheck src/path/to/your/files.ts + +# 3. 🚨 CRITICAL: Test runtime execution +TUI_OFF=true pnpm dev + +# 4. Fix any issues found before proceeding to next step +``` + +**🚨 RUNTIME TESTING IS MANDATORY:** + +- **TypeScript compilation only catches syntax errors** +- **Runtime errors (database issues, missing entities, logic errors) only appear when running the indexer** +- **Test after EVERY code change to catch issues immediately** +- **Run in background for ~30 seconds, check output, then stop process** +- **Never proceed to the next step until the indexer runs successfully** + +**IMPORTANT:** Never proceed to the next step until all TypeScript compilation errors are resolved and codegen works successfully. + +**NOTE: Some errors are expected during migration and should be ignored until later steps:** + +- **Expected errors:** References to entities/handlers that haven't been implemented yet +- **Expected errors:** Missing imports for files that will be created in future steps +- **Expected errors:** Unused variables/functions that will be implemented later + +**Only fix errors that prevent:** + +1. **Codegen from working** - These must be fixed immediately +2. **TypeScript compilation** - These must be fixed immediately +3. **Basic file structure** - These must be fixed immediately + +**CRITICAL: Field Selection for Transaction Data** +When you need access to `event.transaction.hash` or other transaction fields, you MUST add field selection in config.yaml: + +```yaml +- event: Transfer(address indexed from, address indexed to, uint256 value) + field_selection: + transaction_fields: + - hash +``` + +**IMPORTANT: This applies to ALL events that need transaction data, not just Transfer events.** +**Common events that need transaction hash access:** + +- **Transfer events** - for transaction tracking +- **Mint events** - for linking to transactions +- **Burn events** - for linking to transactions +- **Swap events** - for transaction tracking +- **Any event that creates or updates Transaction entities** + +**ALWAYS check if your handler needs `event.transaction.hash` and add `field_selection` BEFORE implementing the handler logic.** + +**Without field selection:** + +- `event.transaction.hash` will be undefined +- `event.transaction` will be empty `{}` +- You'll get TypeScript errors about missing properties + +**With field selection:** + +- `event.transaction.hash` will be available +- `event.transaction` will have the specified fields +- Code will compile correctly + +**Examples of errors to ignore for now:** + +```typescript +// ❌ IGNORE - Will be fixed when we implement the handler +// Error: Cannot find name 'handleMint' +Pair.Mint.handler(handleMint); + +// ❌ IGNORE - Will be fixed when we create the entity +// Error: Cannot find name 'MintEvent' +const mintEvent: MintEvent = { ... }; + +// ❌ IGNORE - Will be fixed when we implement the function +// Error: Cannot find name 'calculateFees' +const fees = calculateFees(amount); +``` + +**COMMON FIXES TO APPLY AUTOMATICALLY:** + +1. **Fix entity type imports** - Use `"generated/src/db/Entities.gen"` for entity types +2. **Fix type mismatches** - Match entity field types exactly (BigDecimal vs bigint) +3. **Fix field names** - Use exact field names from generated types +4. **Fix BigDecimal imports** - Import from `"generated"` not `"bignumber.js"` +5. **Fix entity type annotations** - Use `Pair_t` not `Pair` for entity types +6. **Fix transaction field access** - Add `field_selection` in config.yaml for `event.transaction.hash` +7. **Fix hardcoded values** - Use constants from original subgraph instead of hardcoded addresses/values +8. **Fix missing field selection** - Add `field_selection` for ALL events that need transaction data (not just Transfer) +9. **Fix missing @derivedFrom** - Ensure ALL entity arrays have `@derivedFrom(field: "fieldName")` directives +10. **Fix @derivedFrom array access** - Don't try to access `@derivedFrom` arrays in handlers (they're virtual fields) +11. **Check helper function dependencies** - Ensure all called functions without entity dependencies are implemented +12. **🚨 CRITICAL: Check entity type mismatches and database schema compatibility** - ALWAYS verify that the types you're setting in code match the schema entity property types exactly, and ensure database schema compatibility + +**EXAMPLE OF COMPLETE FIX:** + +```typescript +// ❌ BEFORE - Multiple issues +import { Pair, Token } from "generated"; +import { BigDecimal } from "bignumber.js"; + +const pair: Pair = { + // Wrong type + token0: token0.id, // Wrong field name + totalSupply: ZERO_BD, // Wrong type (should be bigint) +}; + +// ✅ AFTER - All issues fixed +import { Pair_t, Token_t } from "generated/src/db/Entities.gen"; +import { BigDecimal } from "generated"; + +const pair: Pair_t = { + // Correct type + token0_id: token0.id, // Correct field name + totalSupply: ZERO_BI, // Correct type (bigint) +}; +``` + +**EXAMPLE OF FIELD SELECTION FIX:** + +```yaml +# ❌ BEFORE - No field selection, transaction.hash will be undefined +- event: Transfer(address indexed from, address indexed to, uint256 value) + +# ✅ AFTER - With field selection, transaction.hash will be available +- event: Transfer(address indexed from, address indexed to, uint256 value) + field_selection: + transaction_fields: + - hash +``` + +**EXAMPLE OF @derivedFrom FIX:** + +```graphql +# ❌ BEFORE - Missing @derivedFrom, will cause "EE211: Arrays of entities is unsupported" +type Transaction { + mints: [Mint!]! + burns: [Burn!]! + swaps: [Swap!]! +} + +# ✅ AFTER - With @derivedFrom, schema will compile successfully +type Transaction { + mints: [Mint!]! @derivedFrom(field: "transaction") + burns: [Burn!]! @derivedFrom(field: "transaction") + swaps: [Swap!]! @derivedFrom(field: "transaction") +} +``` + +**EXAMPLE OF CONSTANTS FIX:** + +```typescript +// ❌ BEFORE - Hardcoded address, inconsistent with original subgraph +const factory = await context.UniswapFactory.get( + "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f" +); + +// ✅ AFTER - Use constant from original subgraph, consistent and maintainable +const factory = await context.UniswapFactory.get(FACTORY_ADDRESS); +``` + +**A complete migration should pass ALL these checks before being considered production-ready.** + +```` + +## **22. Async/Await Validation** + +**Check that all necessary calls and functions have `async` and `await`:** + +```typescript +// ❌ WRONG - Missing async/await +export function updateTokenDayData() { + const bundle = context.Bundle.get(`${chainId}-1`); // Returns {} without await +} + +// ✅ CORRECT - Proper async/await +export async function updateTokenDayData() { + const bundle = await context.Bundle.get(`${chainId}-1`); // Returns actual data +} +```` + +**Note: `context.Entity.set()` does NOT need `await` - it's synchronous.** + +**This step is critical for preventing database operation failures and ensuring data integrity.** + +## **23. Handling Subgraph Array Access Patterns** + +**IMPORTANT: When the subgraph accesses arrays from relational entities, Envio requires a different approach.** + +### **The Problem:** + +**Subgraph pattern:** + +```typescript +// Subgraph can directly access entity arrays +transaction.mints.push(mint); +transaction.burns.push(burn); +transaction.swaps.push(swap); +``` + +**Envio limitation:** + +- `@derivedFrom` arrays are **virtual fields** - they don't exist in generated types +- You **cannot directly access** `transaction.mints`, `transaction.burns`, etc. +- These arrays are populated automatically by Envio when querying the API + +### **The Solution:** + +**Use `context.Entity.getWhere.fieldName.eq(value)` to query for related entities:** + +```typescript +// ❌ WRONG - Trying to access virtual arrays directly +const mints = transaction.mints; // This will NOT work +const burns = transaction.burns; // This will NOT work + +// ✅ CORRECT - Query for related entities using indexed fields +const mint = await context.Mint.getWhere.transaction_id.eq(transactionId); +const burn = await context.Burn.getWhere.transaction_id.eq(transactionId); +const swap = await context.Swap.getWhere.transaction_id.eq(transactionId); +``` + +### **Example Implementation:** + +```typescript +// When you need to find existing entities by transaction +const existingMint = await context.Mint.getWhere.transaction_id.eq( + transactionId +); +const existingBurn = await context.Burn.getWhere.transaction_id.eq( + transactionId +); + +if (existingMint) { + // Update existing mint entity + const updatedMint: Mint = { + ...existingMint, + amount0: event.params.amount0, + amount1: event.params.amount1, + // ... other updates + }; + context.Mint.set(updatedMint); +} else { + // Create new mint entity + const newMint: Mint = { + id: mintId, + transaction_id: transactionId, + // ... other fields + }; + context.Mint.set(newMint); +} +``` + +### **Key Points:** + +- **Use `context.Entity.getWhere.fieldName.eq(value)`** for querying related entities +- **This simulates the subgraph's array access** by finding entities that reference the current entity +- **Always check if entities exist** before updating or creating +- **Use the spread operator** when updating existing entities (they're read-only) + +**🚨 CRITICAL: Entity Type Mismatch and Database Schema Compatibility** + +**Rule 12: Check entity type mismatches and database schema compatibility - ALWAYS verify that the types you're setting in code match the schema entity property types exactly** + +**Key Points:** + +- **Check the GraphQL schema** (`schema.graphql`) for each entity's field types +- **Compare with your code** - ensure types match exactly +- **Test with TypeScript** - `pnpm tsc --noEmit` will catch many type mismatches +- **Test runtime execution** - `TUI_OFF=true pnpm dev` will catch database compatibility issues + +**Common Schema Types and Their Code Equivalents:** + +- `Int!` → `number` (e.g., `dayStartTimestamp`) +- `BigInt!` → `BigInt` or constants like `ZERO_BI`, `ONE_BI` +- `BigDecimal!` → `BigDecimal` or constants like `ZERO_BD`, `ONE_BD` +- `Bytes!` → `string` (hex addresses) +- `String!` → `string` +- `Boolean!` → `boolean` +- `EntityType!` → `entityType_id: string` (for relationships - see Entity Relationships section) + +**Example of Type Mismatch:** + +```typescript +// ❌ WRONG - Schema expects Int!, but code sets BigInt +// Schema: date: Int! +// Code: date: BigInt(dayStartTimestamp) + +// ✅ CORRECT - Schema expects Int!, code sets number +// Schema: date: Int! +// Code: date: dayStartTimestamp // dayStartTimestamp is already a number +``` diff --git a/apps/indexer/.env.example b/apps/indexer/.env.example new file mode 100644 index 000000000..7d6a1dedc --- /dev/null +++ b/apps/indexer/.env.example @@ -0,0 +1,2 @@ +# To create or update a token visit https://envio.dev/app/api-tokens +ENVIO_API_TOKEN="" diff --git a/apps/indexer/.gitignore b/apps/indexer/.gitignore new file mode 100644 index 000000000..4cb966136 --- /dev/null +++ b/apps/indexer/.gitignore @@ -0,0 +1,36 @@ +*.exe +*.obj +*.out +*.compile +*.native +*.byte +*.cmo +*.annot +*.cmi +*.cmx +*.cmt +*.cmti +*.cma +*.a +*.cmxa +*.obj +*~ +*.annot +*.cmj +*.bak +lib/* +*.mlast +*.mliast +.vscode +.merlin +.bsb.lock +/node_modules/ +benchmarks/ +artifacts +generated +logs +*.res.js +*.res.mjs +*.gen.ts +build +.env diff --git a/apps/indexer/README.md b/apps/indexer/README.md new file mode 100644 index 000000000..7b767eb10 --- /dev/null +++ b/apps/indexer/README.md @@ -0,0 +1,23 @@ +## Envio Indexer + +*Please refer to the [documentation website](https://docs.envio.dev) for a thorough guide on all [Envio](https://envio.dev) indexer features* + +### Run + +```bash +pnpm dev +``` + +Visit http://localhost:8080 to see the GraphQL Playground, local password is `testing`. + +### Generate files from `config.yaml` or `schema.graphql` + +```bash +pnpm codegen +``` + +### Pre-requisites + +- [Node.js (use v18 or newer)](https://nodejs.org/en/download/current) +- [pnpm (use v8 or newer)](https://pnpm.io/installation) +- [Docker desktop](https://www.docker.com/products/docker-desktop/) diff --git a/apps/indexer/abis/UniswapV2Factory.json b/apps/indexer/abis/UniswapV2Factory.json new file mode 100644 index 000000000..b40539040 --- /dev/null +++ b/apps/indexer/abis/UniswapV2Factory.json @@ -0,0 +1,193 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_feeToSetter", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "pair", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "PairCreated", + "type": "event" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allPairs", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "allPairsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + } + ], + "name": "createPair", + "outputs": [ + { + "internalType": "address", + "name": "pair", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeTo", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeToSetter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getPair", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_feeTo", + "type": "address" + } + ], + "name": "setFeeTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_feeToSetter", + "type": "address" + } + ], + "name": "setFeeToSetter", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/apps/indexer/abis/UniswapV2Pair.json b/apps/indexer/abis/UniswapV2Pair.json new file mode 100644 index 000000000..113b80de3 --- /dev/null +++ b/apps/indexer/abis/UniswapV2Pair.json @@ -0,0 +1,713 @@ +[ + { + "inputs": [], + "payable": false, + "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": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint112", + "name": "reserve0", + "type": "uint112" + }, + { + "indexed": false, + "internalType": "uint112", + "name": "reserve1", + "type": "uint112" + } + ], + "name": "Sync", + "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" + }, + { + "constant": true, + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MINIMUM_LIQUIDITY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "PERMIT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getReserves", + "outputs": [ + { + "internalType": "uint112", + "name": "_reserve0", + "type": "uint112" + }, + { + "internalType": "uint112", + "name": "_reserve1", + "type": "uint112" + }, + { + "internalType": "uint32", + "name": "_blockTimestampLast", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_token0", + "type": "address" + }, + { + "internalType": "address", + "name": "_token1", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "kLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "price0CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "price1CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "skim", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "sync", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "token0", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "token1", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/apps/indexer/abis/UniswapV2Router02.json b/apps/indexer/abis/UniswapV2Router02.json new file mode 100644 index 000000000..3d4ca741f --- /dev/null +++ b/apps/indexer/abis/UniswapV2Router02.json @@ -0,0 +1,973 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_factory", + "type": "address" + }, + { + "internalType": "address", + "name": "_WETH", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "WETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountADesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBDesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "addLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountTokenDesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "addLiquidityETH", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveOut", + "type": "uint256" + } + ], + "name": "getAmountIn", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveOut", + "type": "uint256" + } + ], + "name": "getAmountOut", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "getAmountsIn", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "getAmountsOut", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveB", + "type": "uint256" + } + ], + "name": "quote", + "outputs": [ + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidityETH", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidityETHSupportingFeeOnTransferTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityETHWithPermit", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityETHWithPermitSupportingFeeOnTransferTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityWithPermit", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapETHForExactTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactETHForTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactETHForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForETH", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForETHSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapTokensForExactETH", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapTokensForExactTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/apps/indexer/config.yaml b/apps/indexer/config.yaml new file mode 100644 index 000000000..665739779 --- /dev/null +++ b/apps/indexer/config.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=./node_modules/envio/evm.schema.json +name: trustswap-v2-indexer +description: Indexer for TrustSwap UniswapV2 fork +ecosystem: evm + +schema: ./schema.graphql +output: ./generated + +contracts: + - name: UniswapV2Factory + abi_file_path: ./abis/UniswapV2Factory.json + handler: ./src/EventHandlers.ts + events: + - event: "PairCreated(address indexed token0, address indexed token1, address pair, uint256)" + + - name: UniswapV2Pair + abi_file_path: ./abis/UniswapV2Pair.json + handler: ./src/EventHandlers.ts + events: + - event: "Swap(address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to)" + - event: "Mint(address indexed sender, uint256 amount0, uint256 amount1)" + - event: "Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to)" + - event: "Sync(uint112 reserve0, uint112 reserve1)" + +networks: + - id: 13579 + start_block: 0 + rpc_config: + url: "https://testnet.rpc.intuition.systems/http" + contracts: + - name: UniswapV2Factory + address: "0xd103E057242881214793d5A1A7c2A5B84731c75c" + - name: UniswapV2Pair + # no address here – pairs will be discovered dynamically + +field_selection: + transaction_fields: + - hash + - from \ No newline at end of file diff --git a/apps/indexer/package.json b/apps/indexer/package.json new file mode 100644 index 000000000..07cee2f0f --- /dev/null +++ b/apps/indexer/package.json @@ -0,0 +1,35 @@ +{ + "name": "indexer", + "version": "0.1.0", + "scripts": { + "clean": "tsc --clean", + "build": "tsc --build", + "watch": "tsc --watch", + "mocha": "ts-mocha test/**/*.ts", + "codegen": "envio codegen", + "dev": "envio dev", + "start": "envio start", + "test": "pnpm mocha" + }, + "devDependencies": { + "@rescript/react": "^0.14.0", + "@types/chai": "^4.3.11", + "@types/mocha": "10.0.6", + "@types/node": "20.8.8", + "chai": "4.3.10", + "mocha": "10.2.0", + "rescript": "^11.1.4", + "rescript-envsafe": "^5.0.0", + "ts-mocha": "^10.0.0", + "typescript": "5.2.2" + }, + "dependencies": { + "envio": "2.32.3" + }, + "optionalDependencies": { + "generated": "./generated" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/apps/indexer/schema.graphql b/apps/indexer/schema.graphql new file mode 100644 index 000000000..736aba925 --- /dev/null +++ b/apps/indexer/schema.graphql @@ -0,0 +1,77 @@ +type Pair { + id: ID! + chainId: BigInt! + token0: String! + token1: String! + pairAddress: String! + allPairsLength: BigInt! + createdAtBlock: BigInt! + createdAtTx: String! + logIndex: Int! + + swapCount: BigInt! + mintCount: BigInt! + burnCount: BigInt! +} + +type Token { + id: ID! + address: String! + chainId: BigInt! + createdAtBlock: BigInt! + createdAtTx: String! + pairCount: BigInt! + swapCount: BigInt! +} + +type User { + id: ID! + address: String! + chainId: BigInt! + swapCount: BigInt! + totalAmount0In: BigInt! + totalAmount1In: BigInt! + totalAmount0Out: BigInt! + totalAmount1Out: BigInt! +} + +type Mint { + id: ID! + chainId: BigInt! + pairAddress: String! + sender: String! + amount0: BigInt! + amount1: BigInt! + createdAtBlock: BigInt! + createdAtTx: String! + logIndex: Int! +} + +type Burn { + id: ID! + chainId: BigInt! + pairAddress: String! + sender: String! + to: String! + amount0: BigInt! + amount1: BigInt! + createdAtBlock: BigInt! + createdAtTx: String! + logIndex: Int! +} + +type Swap { + id: ID! + chainId: BigInt! + pairAddress: String! + sender: String! + to: String! + amount0In: BigInt! + amount1In: BigInt! + amount0Out: BigInt! + amount1Out: BigInt! + createdAtBlock: BigInt! + createdAtTx: String! + logIndex: Int! + blockTimestamp: BigInt! +} diff --git a/apps/indexer/src/EventHandlers.ts b/apps/indexer/src/EventHandlers.ts new file mode 100644 index 000000000..3e79a9518 --- /dev/null +++ b/apps/indexer/src/EventHandlers.ts @@ -0,0 +1,275 @@ +import { + UniswapV2Factory, + UniswapV2Pair, + Pair as PairEntity, + Token, + User, + Swap, + Mint, + Burn, +} from "generated"; + +// Helper to build stable IDs +function makePairId(chainId: bigint | number, pairAddress: string): string { + return `${chainId}_${pairAddress.toLowerCase()}`; +} + +function makeTokenId(chainId: bigint | number, tokenAddress: string): string { + return `${chainId}_${tokenAddress.toLowerCase()}`; +} + +function makeUserId(chainId: bigint | number, userAddress: string): string { + return `${chainId}_${userAddress.toLowerCase()}`; +} + +/** + * Handle PairCreated events from UniswapV2Factory + * - register / update Pair entity + * - register / update Token entities for token0 / token1 + */ + +UniswapV2Factory.PairCreated.contractRegister(({ event, context }: any) => { + context.addUniswapV2Pair(event.params.pair); + + context.log.info( + `Registered new UniswapV2Pair at ${event.params.pair} (token0=${event.params.token0}, token1=${event.params.token1})`, + ); +}); + +UniswapV2Factory.PairCreated.handler(async ({ event, context }) => { + const chainId = BigInt(event.chainId); + const pairAddress = event.params.pair; + const pairId = makePairId(chainId, pairAddress); + + // Upsert Pair + const existingPair = await context.Pair.get(pairId); + + const pairEntity: PairEntity = { + id: pairId, + chainId, + token0: event.params.token0, + token1: event.params.token1, + pairAddress, + allPairsLength: event.params._3, + createdAtBlock: existingPair + ? existingPair.createdAtBlock + : BigInt(event.block.number), + createdAtTx: existingPair + ? existingPair.createdAtTx + : (event.transaction.hash ?? ""), + logIndex: existingPair ? existingPair.logIndex : event.logIndex, + swapCount: existingPair ? existingPair.swapCount : 0n, + mintCount: existingPair ? existingPair.mintCount : 0n, + burnCount: existingPair ? existingPair.burnCount : 0n, + }; + + await context.Pair.set(pairEntity); + + // Upsert Token0 + const token0Id = makeTokenId(chainId, event.params.token0); + const existingToken0 = await context.Token.get(token0Id); + + const token0Entity: Token = { + id: token0Id, + address: event.params.token0, + chainId, + createdAtBlock: existingToken0 + ? existingToken0.createdAtBlock + : BigInt(event.block.number), + createdAtTx: existingToken0 + ? existingToken0.createdAtTx + : (event.transaction.hash ?? ""), + pairCount: (existingToken0 ? existingToken0.pairCount : 0n) + 1n, + swapCount: existingToken0 ? existingToken0.swapCount : 0n, + }; + + await context.Token.set(token0Entity); + + // Upsert Token1 + const token1Id = makeTokenId(chainId, event.params.token1); + const existingToken1 = await context.Token.get(token1Id); + + const token1Entity: Token = { + id: token1Id, + address: event.params.token1, + chainId, + createdAtBlock: existingToken1 + ? existingToken1.createdAtBlock + : BigInt(event.block.number), + createdAtTx: existingToken1 + ? existingToken1.createdAtTx + : (event.transaction.hash ?? ""), + pairCount: (existingToken1 ? existingToken1.pairCount : 0n) + 1n, + swapCount: existingToken1 ? existingToken1.swapCount : 0n, + }; + + await context.Token.set(token1Entity); +}); + +/** + * Handle Mint events on UniswapV2Pair + */ +UniswapV2Pair.Mint.handler(async ({ event, context }) => { + console.log("🔥 Swap event received for", event.srcAddress); + + const chainId = BigInt(event.chainId); + const pairAddress = event.srcAddress; + const pairId = makePairId(chainId, pairAddress); + + const mintId = `${chainId}_${event.block.number}_${event.logIndex}`; + + const mintEntity: Mint = { + id: mintId, + chainId, + pairAddress, + sender: event.params.sender, + amount0: event.params.amount0, + amount1: event.params.amount1, + createdAtBlock: BigInt(event.block.number), + createdAtTx: event.transaction.hash ?? "", + logIndex: event.logIndex, + }; + + await context.Mint.set(mintEntity); + + // Update Pair mintCount + const pair = await context.Pair.get(pairId); + if (pair) { + const updatedPair: PairEntity = { + ...pair, + mintCount: (pair.mintCount ?? 0n) + 1n, + }; + await context.Pair.set(updatedPair); + } +}); + +/** + * Handle Burn events on UniswapV2Pair + */ +UniswapV2Pair.Burn.handler(async ({ event, context }) => { + const chainId = BigInt(event.chainId); + const pairAddress = event.srcAddress; + const pairId = makePairId(chainId, pairAddress); + + const burnId = `${chainId}_${event.block.number}_${event.logIndex}`; + + const burnEntity: Burn = { + id: burnId, + chainId, + pairAddress, + sender: event.params.sender, + to: event.params.to, + amount0: event.params.amount0, + amount1: event.params.amount1, + createdAtBlock: BigInt(event.block.number), + createdAtTx: event.transaction.hash ?? "", + logIndex: event.logIndex, + }; + + await context.Burn.set(burnEntity); + + // Update Pair burnCount + const pair = await context.Pair.get(pairId); + if (pair) { + const updatedPair: PairEntity = { + ...pair, + burnCount: (pair.burnCount ?? 0n) + 1n, + }; + await context.Pair.set(updatedPair); + } +}); + +/** + * Handle Swap events on UniswapV2Pair + * - create Swap entity + * - update Pair.swapCount + * - update User stats + * - update Token.swapCount for token0/token1 + */ +UniswapV2Pair.Swap.handler(async ({ event, context }) => { + const chainId = BigInt(event.chainId); + const pairAddress = event.srcAddress; // ✅ this is the pair contract address + const pairId = makePairId(chainId, pairAddress); + + const swapId = `${chainId}_${event.block.number}_${event.logIndex}`; + + const swapEntity: Swap = { + id: swapId, + chainId, + pairAddress, + sender: event.params.sender, + to: event.params.to, + amount0In: event.params.amount0In, + amount1In: event.params.amount1In, + amount0Out: event.params.amount0Out, + amount1Out: event.params.amount1Out, + createdAtBlock: BigInt(event.block.number), + blockTimestamp: BigInt(event.block.timestamp), + createdAtTx: event.transaction.hash ?? "", + logIndex: event.logIndex, + }; + + await context.Swap.set(swapEntity); + + // Update Pair swapCount + const pair = await context.Pair.get(pairId); + if (pair) { + const updatedPair: PairEntity = { + ...pair, + swapCount: (pair.swapCount ?? 0n) + 1n, + }; + await context.Pair.set(updatedPair); + } + + // Upsert User based on transaction.from (EOA) + const txFrom = event.transaction.from ?? event.params.sender; + const userId = makeUserId(chainId, txFrom); + const existingUser = await context.User.get(userId); + + const totalAmount0In = + (existingUser ? existingUser.totalAmount0In : 0n) + event.params.amount0In; + const totalAmount1In = + (existingUser ? existingUser.totalAmount1In : 0n) + event.params.amount1In; + const totalAmount0Out = + (existingUser ? existingUser.totalAmount0Out : 0n) + + event.params.amount0Out; + const totalAmount1Out = + (existingUser ? existingUser.totalAmount1Out : 0n) + + event.params.amount1Out; + + const userEntity: User = { + id: userId, + address: txFrom, + chainId, + swapCount: (existingUser ? existingUser.swapCount : 0n) + 1n, + totalAmount0In, + totalAmount1In, + totalAmount0Out, + totalAmount1Out, + }; + + await context.User.set(userEntity); + + // Update Token swapCount for token0 / token1 + if (pair) { + const token0Id = makeTokenId(chainId, pair.token0); + const token1Id = makeTokenId(chainId, pair.token1); + + const token0 = await context.Token.get(token0Id); + const token1 = await context.Token.get(token1Id); + + if (token0) { + await context.Token.set({ + ...token0, + swapCount: (token0.swapCount ?? 0n) + 1n, + }); + } + + if (token1) { + await context.Token.set({ + ...token1, + swapCount: (token1.swapCount ?? 0n) + 1n, + }); + } + } +}); \ No newline at end of file diff --git a/apps/indexer/test/Test.ts b/apps/indexer/test/Test.ts new file mode 100644 index 000000000..18c91f693 --- /dev/null +++ b/apps/indexer/test/Test.ts @@ -0,0 +1,38 @@ +import assert from "assert"; +import { + TestHelpers, + Factory_PairCreated +} from "generated"; +const { MockDb, Factory } = TestHelpers; + +describe("Factory contract PairCreated event tests", () => { + // Create mock db + const mockDb = MockDb.createMockDb(); + + // Creating mock for Factory contract PairCreated event + const event = Factory.PairCreated.createMockEvent({/* It mocks event fields with default values. You can overwrite them if you need */}); + + it("Factory_PairCreated is created correctly", async () => { + // Processing the event + const mockDbUpdated = await Factory.PairCreated.processEvent({ + event, + mockDb, + }); + + // Getting the actual entity from the mock database + let actualFactoryPairCreated = mockDbUpdated.entities.Factory_PairCreated.get( + `${event.chainId}_${event.block.number}_${event.logIndex}` + ); + + // Creating the expected entity + const expectedFactoryPairCreated: Factory_PairCreated = { + id: `${event.chainId}_${event.block.number}_${event.logIndex}`, + token0: event.params.token0, + token1: event.params.token1, + pair: event.params.pair, + _3: event.params._3, + }; + // Asserting that the entity in the mock database is the same as the expected entity + assert.deepEqual(actualFactoryPairCreated, expectedFactoryPairCreated, "Actual FactoryPairCreated should be the same as the expectedFactoryPairCreated"); + }); +}); diff --git a/apps/indexer/tsconfig.json b/apps/indexer/tsconfig.json new file mode 100644 index 000000000..230241339 --- /dev/null +++ b/apps/indexer/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "es2020", //required for use with BigInt types + "lib": [ + "es2020" + ], + "allowJs": true, + "checkJs": false, + "outDir": "build", + "strict": true, + "noImplicitAny": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "module": "CommonJS" + }, + "include": [ + "src", + "test" + ] +} diff --git a/apps/web/package.json b/apps/web/package.json index d61001e18..935860a17 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -18,7 +18,9 @@ "@trustswap/sdk": "workspace:^", "@trustswap/tokenlists": "workspace:^", "@trustswap/ui": "workspace:^", + "apexcharts": "^5.3.6", "react": "^19.1.1", + "react-apexcharts": "^1.8.0", "react-dom": "^19.1.1", "react-router-dom": "^7.8.2", "viem": "^2.37.2", diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 784e7c3ff..0bb848f6e 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -2,27 +2,31 @@ import { Routes, Route, Navigate } from "react-router-dom"; import Layout from "./components/Layout"; import SwapPage from "./pages/SwapPage"; +import SwapProPage from "./pages/SwapProPage"; import PoolsPage from "./pages/PoolsPage"; import PortfolioPage from "./pages/PortfolioPage"; import NotFound from "./pages/NotFound"; import Landing from "./Landing"; +import { SwapProvider } from "./features/swap/SwapContext"; import './styles/App.css'; export default function App() { return ( + }> } /> } /> } /> + } /> } /> } /> {/* 404 */} } /> - + ); } diff --git a/apps/web/src/components/Layout.tsx b/apps/web/src/components/Layout.tsx index eff8cd71a..5dc7663ea 100644 --- a/apps/web/src/components/Layout.tsx +++ b/apps/web/src/components/Layout.tsx @@ -5,15 +5,40 @@ import { ConnectButton } from "./ConnectButton"; import { NetworkSelect } from "./NetworkSelect"; import logo from "../assets/logo.png"; + const LAST_SWAP_VIEW_KEY = "trustswap:lastSwapView"; + const DEFAULT_SWAP_PATH = "/swap"; + + function getLastSwapPath(): string { + const v = localStorage.getItem(LAST_SWAP_VIEW_KEY); + if (v === "/swap/pro") return "/swap/pro"; + if (v === "/swap") return "/swap"; + return DEFAULT_SWAP_PATH; + } + + function setLastSwapPath(path: string) { + if (path === "/swap" || path === "/swap/pro") { + localStorage.setItem(LAST_SWAP_VIEW_KEY, path); + } + } + export default function Layout() { const location = useLocation(); const [bgStyle, setBgStyle] = useState<{ width: number; left: number }>({ width: 0, left: 0 }); + const [showSwapMenu, setShowSwapMenu] = useState(false); const swapRef = useRef(null); const poolsRef = useRef(null); const portfolioRef = useRef(null); const navRef = useRef(null); + const [swapTarget, setSwapTarget] = useState(() => { + try { + return getLastSwapPath(); + } catch { + return DEFAULT_SWAP_PATH; + } + }); + function getActiveRef(pathname: string) { if (pathname.startsWith("/swap")) return swapRef.current; if (pathname.startsWith("/pools")) return poolsRef.current; @@ -46,6 +71,18 @@ export default function Layout() { }; }, [location.pathname]); + useEffect(() => { + if (location.pathname === "/swap" || location.pathname.startsWith("/swap/")) { + const normalized = location.pathname.startsWith("/swap/pro") ? "/swap/pro" : "/swap"; + try { + setLastSwapPath(normalized); + setSwapTarget(normalized); + } catch { + // ignore + } + } + }, [location.pathname]); + return (
@@ -72,15 +109,48 @@ export default function Layout() { /> )} - - `${styles.linkBase} ${isActive ? styles.linkTextActive : ""}` - } +
setShowSwapMenu(true)} + onMouseLeave={() => setShowSwapMenu(false)} > - Swap - + + `${styles.linkBase} ${ + location.pathname.startsWith("/swap") ? styles.linkTextActive : "" + }` + } + > + Swap + + + {showSwapMenu && ( +
+ + `${styles.dropdownItem} ${isActive ? styles.dropdownItemActive : ""}` + } + > + Classic + + + + `${styles.dropdownItem} ${isActive ? styles.dropdownItemActive : ""}` + } + > + Pro + +
+ )} +
+ void; + setTokenOut: (token: SwapToken | null) => void; + setPairAddress: (addr: Address | null) => void; +}; + +const SwapContext = createContext(undefined); + +export function SwapProvider({ children }: { children: ReactNode }) { + const [tokenIn, setTokenInState] = useState(null); + const [tokenOut, setTokenOutState] = useState(null); + const [pairAddress, setPairAddressState] = useState
(null); + + const setTokenIn = useCallback((token: SwapToken | null) => { + setTokenInState(token); + }, []); + + const setTokenOut = useCallback((token: SwapToken | null) => { + setTokenOutState(token); + }, []); + + const setPairAddress = useCallback((addr: Address | null) => { + setPairAddressState(addr); + }, []); + + const value = useMemo( + () => ({ + tokenIn, + tokenOut, + pairAddress, + setTokenIn, + setTokenOut, + setPairAddress, + }), + [tokenIn, tokenOut, pairAddress, setTokenIn, setTokenOut, setPairAddress], + ); + + return {children}; +} + +export function useSwapContext() { + const ctx = useContext(SwapContext); + if (!ctx) { + throw new Error("useSwapContext must be used inside a SwapProvider"); + } + return ctx; +} diff --git a/apps/web/src/features/swap/components/AIChatPanel.tsx b/apps/web/src/features/swap/components/AIChatPanel.tsx new file mode 100644 index 000000000..c9d1e955c --- /dev/null +++ b/apps/web/src/features/swap/components/AIChatPanel.tsx @@ -0,0 +1,18 @@ +import styles from "@ui/styles/Swap.module.css"; + +export function AIChatPanel() { + return ( +
+
+ AI Assistant + beta +
+
+

+ Coming soon: An AI-powered assistant to help you with your swaps and provide insights + about the market. +

+
+
+ ); +} diff --git a/apps/web/src/features/swap/components/ProSwapChart.tsx b/apps/web/src/features/swap/components/ProSwapChart.tsx new file mode 100644 index 000000000..f4b723fd5 --- /dev/null +++ b/apps/web/src/features/swap/components/ProSwapChart.tsx @@ -0,0 +1,207 @@ +// apps/web/src/features/swap/components/ProSwapChart.tsx +import { useMemo, useState } from "react"; +import ReactApexChart from "react-apexcharts"; +import type { ApexOptions } from "apexcharts"; + +import styles from "@ui/styles/Swap.module.css"; +import { useSwapContext } from "../SwapContext"; +import { usePairChartData } from "../hooks/usePairChartData"; +import { useRecentSwapsForPair } from "../hooks/useRecentSwapsForPair"; + +type Timeframe = "1H" | "1D" | "1W" | "1M"; + +export function ProSwapChart() { + const { tokenIn, tokenOut, pairAddress } = useSwapContext(); + const { data, isLoading } = usePairChartData(pairAddress); + const { data: recentSwaps } = useRecentSwapsForPair(pairAddress); + const [timeframe, setTimeframe] = useState("1D"); + + // Try to get symbols from recent activity first (consistent with table), + // fallback to context token symbols, fallback to "Token0/Token1". + const firstSwap = recentSwaps[0]; + + const inSymbolFromData = firstSwap?.inSymbol; + const outSymbolFromData = firstSwap?.outSymbol; + + const inSymbol = + inSymbolFromData || + (typeof tokenIn?.symbol === "string" ? tokenIn.symbol : undefined); + const outSymbol = + outSymbolFromData || + (typeof tokenOut?.symbol === "string" ? tokenOut.symbol : undefined); + + const pairSymbol = + inSymbol && outSymbol ? `${inSymbol} / ${outSymbol}` : "Select a pair"; + + const lastPrice = data?.lastPrice ?? "-"; + + const filteredCandles = useMemo(() => { + if (!data?.candles || data.candles.length === 0) return []; + + const nowMs = Date.now(); + let deltaMs = 0; + + switch (timeframe) { + case "1H": + deltaMs = 1 * 60 * 60 * 1000; + break; + case "1D": + deltaMs = 24 * 60 * 60 * 1000; + break; + case "1W": + deltaMs = 7 * 24 * 60 * 60 * 1000; + break; + case "1M": + deltaMs = 30 * 24 * 60 * 60 * 1000; + break; + default: + return data.candles; + } + + const cutoff = nowMs - deltaMs; + + return data.candles.filter((c) => { + const tsMs = c.timestamp > 10_000_000_000 ? c.timestamp : c.timestamp * 1000; + return tsMs >= cutoff; + }); + }, [data, timeframe]); + + const series = useMemo( + () => [ + { + name: "Price", + data: filteredCandles.map((c) => ({ + x: + c.timestamp > 10_000_000_000 + ? new Date(c.timestamp) + : new Date(c.timestamp * 1000), + y: [c.open, c.high, c.low, c.close], + })), + }, + ], + [filteredCandles], + ); + + const options: ApexOptions = { + chart: { + type: "candlestick", + height: 260, + toolbar: { show: false }, + zoom: { enabled: false }, + background: "transparent", + }, + plotOptions: { + candlestick: { + colors: { + upward: "#2ecc71", + downward: "#ff4d4d", + }, + wick: { + useFillColor: true, + }, + }, + }, + xaxis: { + type: "datetime", + labels: { + style: { + colors: "#888888", + fontSize: "10px", + }, + }, + axisBorder: { show: false }, + axisTicks: { show: false }, + }, + yaxis: { + labels: { + style: { + colors: "#888888", + fontSize: "10px", + }, + }, + tooltip: { + enabled: true, + }, + }, + grid: { + borderColor: "rgba(255,255,255,0.08)", + strokeDashArray: 1, + padding: { + left: 0, + right: 10, + top: 0, + bottom: 10, + }, + }, + tooltip: { + theme: "dark", + x: { + format: "dd MMM HH:mm", + }, + }, + }; + + const hasData = filteredCandles.length > 0; + + const baseInitial = inSymbol?.[0] ?? "?"; + const quoteInitial = outSymbol?.[0] ?? "?"; + + return ( +
+
+
+
+ + {baseInitial} + + + {quoteInitial} + +
+
+
{pairSymbol}
+
{lastPrice}
+
+
+ +
+ {(["1H", "1D", "1W", "1M"] as Timeframe[]).map((t) => ( + + ))} +
+
+ +
+ {!pairAddress ? ( + + Select a pair to see the chart + + ) : isLoading ? ( + Loading chart... + ) : !hasData ? ( + + No chart data for this pair yet + + ) : ( + + )} +
+
+ ); +} diff --git a/apps/web/src/features/swap/components/RecentSwaps.tsx b/apps/web/src/features/swap/components/RecentSwaps.tsx new file mode 100644 index 000000000..92d17fa70 --- /dev/null +++ b/apps/web/src/features/swap/components/RecentSwaps.tsx @@ -0,0 +1,133 @@ +import styles from "@ui/styles/Swap.module.css"; +import { useSwapContext } from "../SwapContext"; +import { formatUnits } from "viem"; +import { usePairAddress } from "../hooks/usePairAddress"; +import { useRecentSwapsForPair } from "../hooks/useRecentSwapsForPair"; + +function shortenAddress(addr: string) { + if (!addr) return ""; + return `${addr.slice(0, 6)}…${addr.slice(-4)}`; +} + +function shortenHash(hash: string) { + if (!hash) return ""; + return `${hash.slice(0, 10)}…${hash.slice(-8)}`; +} + +function getExplorerTxUrl(chainIdStr: string, txHash: string) { + const chainId = Number(chainIdStr); + if (!txHash) return "#"; + + switch (chainId) { + case 1155: + return `https://explorer.intuition.systems/tx/${txHash}`; + case 13579: + return `https://testnet.explorer.intuition.systems/tx/${txHash}`; + default: + return "#"; + } +} + +function formatAmount(raw: string, decimals: number) { + try { + const bn = BigInt(raw); + const num = Number(formatUnits(bn, decimals)); + if (!Number.isFinite(num)) return raw; + return num.toLocaleString(undefined, { + maximumFractionDigits: 6, + }); + } catch { + return raw; + } +} + +export function RecentSwaps() { + const { pairAddress } = useSwapContext(); + const { data, isLoading } = useRecentSwapsForPair(pairAddress); + + return ( +
+
+

Recent activity

+
+ +
+ + + + + + + + + + + + + {!pairAddress && !isLoading && ( + + + + )} + + {isLoading && ( + + + + )} + + {!isLoading && + pairAddress && + data.map((swap) => { + const formattedIn = formatAmount( + swap.amountInRaw, + swap.inDecimals, + ); + const formattedOut = formatAmount( + swap.amountOutRaw, + swap.outDecimals, + ); + const txUrl = getExplorerTxUrl(swap.chainId, swap.txHash); + + return ( + + + + + + + + + ); + })} + +
Date / TimeTypeInOutTraderTxn
Select a pair to see the latest trades.
Loading swaps...
{swap.timeLabel} + {swap.side === "buy" ? "Buy" : "Sell"} + + {formattedIn} {swap.inSymbol} + + {formattedOut} {swap.outSymbol} + {shortenAddress(swap.wallet)} + {txUrl === "#" ? ( + shortenHash(swap.txHash) + ) : ( + + {shortenHash(swap.txHash)} + + )} +
+
+
+ ); +} \ No newline at end of file diff --git a/apps/web/src/features/swap/components/SwapForm.tsx b/apps/web/src/features/swap/components/SwapForm.tsx index 3f5968c30..5a2436972 100644 --- a/apps/web/src/features/swap/components/SwapForm.tsx +++ b/apps/web/src/features/swap/components/SwapForm.tsx @@ -21,6 +21,7 @@ import ApproveAndSwap from "./ApproveAndSwap"; import DetailsDisclosure from "./DetailsDisclosure"; import { addresses as TESTNET_ADDRESSES, abi, getAddresses } from "@trustswap/sdk"; +import { useSwapContext } from "../SwapContext"; const norm = (a?: string) => (a ? a.toLowerCase() : ""); @@ -105,6 +106,13 @@ export default function SwapForm() { const [bestPath, setBestPath] = useState(null); const [lastOutBn, setLastOutBn] = useState(null); + const { + tokenIn: ctxTokenIn, + tokenOut: ctxTokenOut, + setTokenIn: setContextTokenIn, + setTokenOut: setContextTokenOut, + setPairAddress, + } = useSwapContext(); const { tokens: imported } = useImportedTokens(); @@ -150,6 +158,64 @@ export default function SwapForm() { }; } + useEffect(() => { + if (!tokenIn) { + if (ctxTokenIn !== null) { + setContextTokenIn(null); + } + return; + } + + const meta = getMeta(tokenIn); + const poolAddress = isNative(tokenIn) ? WNATIVE : meta.address; + + if ( + ctxTokenIn && + ctxTokenIn.address === poolAddress && + ctxTokenIn.symbol === meta.symbol && + ctxTokenIn.decimals === meta.decimals + ) { + return; + } + + setContextTokenIn({ + address: poolAddress, + symbol: meta.symbol, + decimals: meta.decimals, + }); + }, [tokenIn, ctxTokenIn, WNATIVE, setContextTokenIn]); + + + useEffect(() => { + if (!tokenOut) { + if (ctxTokenOut !== null) { + setContextTokenOut(null); + } + return; + } + + const addr = tokenOut as Address; + const meta = getMeta(addr); + const poolAddress = isNative(addr) ? WNATIVE : meta.address; + + if ( + ctxTokenOut && + ctxTokenOut.address === poolAddress && + ctxTokenOut.symbol === meta.symbol && + ctxTokenOut.decimals === meta.decimals + ) { + return; + } + + setContextTokenOut({ + address: poolAddress, + symbol: meta.symbol, + decimals: meta.decimals, + }); + }, [tokenOut, ctxTokenOut, WNATIVE, setContextTokenOut]); + + + function buildPaths(tin: Address, tout: Address): Address[][] { const WT = WNATIVE as Address; const tswpAddr = TSWP as Address; @@ -269,11 +335,13 @@ export default function SwapForm() { (async () => { const pd = await fetchPair(tokenIn, tokenOut); if (id !== pairReq.current) return; // anti-race + const lpAddr = pd?.pair as Address | undefined; setPairData(pd); + setPairAddress(lpAddr ?? null); if (!pd) console.warn("[pairData] no LP for", tokenIn, tokenOut); })(); return () => { /* rien, on s'appuie sur id */ }; - }, [tokenIn, tokenOut, fetchPair]); + }, [tokenIn, tokenOut, fetchPair, setPairAddress]); // Estimation réseau useEffect(() => { diff --git a/apps/web/src/features/swap/hooks/usePairAddress.ts b/apps/web/src/features/swap/hooks/usePairAddress.ts new file mode 100644 index 000000000..766c438bc --- /dev/null +++ b/apps/web/src/features/swap/hooks/usePairAddress.ts @@ -0,0 +1,40 @@ +// usePairAddress.ts +import { useMemo } from "react"; +import type { Address } from "viem"; +import { useChainId } from "wagmi"; +import { getAddresses } from "@trustswap/sdk"; +import type { SwapToken } from "../SwapContext"; + +function sortTokens(a: Address, b: Address): [Address, Address] { + return a.toLowerCase() < b.toLowerCase() ? [a, b] : [b, a]; +} + +// TODO: replace body with real Uniswap V2 pair computation or SDK helper +function computePairAddress(factory: Address, tokenA: Address, tokenB: Address): Address { + const [token0, token1] = sortTokens(tokenA, tokenB); + console.warn("computePairAddress is a placeholder, plug your SDK implementation here", { + factory, + token0, + token1, + }); + return factory; // temporary placeholder, just to avoid crashes +} + +export function usePairAddress( + tokenIn: SwapToken | null, + tokenOut: SwapToken | null, +): Address | null { + const chainId = useChainId(); + const { UniswapV2Factory } = getAddresses(chainId); + const factory = UniswapV2Factory as Address; + + return useMemo(() => { + if (!tokenIn || !tokenOut) return null; + try { + return computePairAddress(factory, tokenIn.address, tokenOut.address); + } catch (e) { + console.error("Failed to compute pair address", e); + return null; + } + }, [factory, tokenIn, tokenOut]); +} diff --git a/apps/web/src/features/swap/hooks/usePairChartData.ts b/apps/web/src/features/swap/hooks/usePairChartData.ts new file mode 100644 index 000000000..c576f8f77 --- /dev/null +++ b/apps/web/src/features/swap/hooks/usePairChartData.ts @@ -0,0 +1,156 @@ +// usePairChartData.ts +import { useEffect, useState } from "react"; +import type { Address } from "viem"; +import { graphqlRequest } from "../../../lib/indexerClient"; +type Candle = { + timestamp: number; + open: number; + high: number; + low: number; + close: number; + volume: number; +}; + +type PairChartData = { + candles: Candle[]; + lastPrice: string; +}; + +const SWAPS_FOR_CHART_QUERY = ` + query SwapsForChart($pair: String!, $limit: Int!) { + Swap( + where: { pairAddress: { _ilike: $pair } } + order_by: { blockTimestamp: desc } + limit: $limit + ) { + amount0In + amount1In + amount0Out + amount1Out + blockTimestamp + } + } +`; + +type SwapsForChartResponse = { + Swap: { + amount0In: string; + amount1In: string; + amount0Out: string; + amount1Out: string; + blockTimestamp: string; + }[]; +}; + +function roundPrice(n: number, decimals = 4): number { + return Number(n.toFixed(decimals)); +} + + +export function usePairChartData(pairAddress: Address | null) { + const [data, setData] = useState(null); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + if (!pairAddress) { + setData(null); + return; + } + + let cancelled = false; + + async function fetchData() { + if (!pairAddress) return; + + setIsLoading(true); + try { + const res = await graphqlRequest< + SwapsForChartResponse, + { pair: string; limit: number } + >({ + query: SWAPS_FOR_CHART_QUERY, + variables: { + pair: pairAddress.toLowerCase(), + limit: 200, + }, + }); + + if (cancelled) return; + + const swaps = res.Swap + .map((s) => { + const ts = Number(s.blockTimestamp); + const a0In = Number(s.amount0In); + const a1In = Number(s.amount1In); + const a0Out = Number(s.amount0Out); + const a1Out = Number(s.amount1Out); + + let price: number | null = null; + if (a0In > 0 && a1Out > 0) { + price = roundPrice(a1Out / a0In); + } else if (a1In > 0 && a0Out > 0) { + price = roundPrice(a0Out / a1In); + } + + return { ts, price }; + }) + .filter((s) => s.price !== null) as { ts: number; price: number }[]; + + if (swaps.length === 0) { + setData({ + candles: [], + lastPrice: "-", + }); + return; + } + + const bucketSizeSec = 5 * 60; + const buckets = new Map(); + + for (const s of swaps) { + const bucket = Math.floor(s.ts / bucketSizeSec) * bucketSizeSec; + const arr = buckets.get(bucket) ?? []; + arr.push(s.price); + buckets.set(bucket, arr); + } + + const candles: Candle[] = Array.from(buckets.entries()) + .sort((a, b) => a[0] - b[0]) + .map(([bucketTs, prices]) => { + const open = prices[0]; + const close = prices[prices.length - 1]; + const high = Math.max(...prices); + const low = Math.min(...prices); + return { + timestamp: bucketTs, + open, + high, + low, + close, + volume: prices.length, + }; + }); + + const last = candles[candles.length - 1]; + + setData({ + candles, + lastPrice: last.close.toString(), + }); + } catch (err) { + console.error("Failed to fetch pair chart data", err); + if (!cancelled) setData(null); + } finally { + if (!cancelled) setIsLoading(false); + } + } + + fetchData(); + + return () => { + cancelled = true; + }; + }, [pairAddress]); + + return { data, isLoading }; +} \ No newline at end of file diff --git a/apps/web/src/features/swap/hooks/useRecentSwapsForPair.ts b/apps/web/src/features/swap/hooks/useRecentSwapsForPair.ts new file mode 100644 index 000000000..eef1cb89f --- /dev/null +++ b/apps/web/src/features/swap/hooks/useRecentSwapsForPair.ts @@ -0,0 +1,337 @@ +// useRecentSwapsForPair.ts +import { useEffect, useState } from "react"; +import type { Address } from "viem"; +import { graphqlRequest } from "../../../lib/indexerClient"; +import { useTokenModule } from "../../../hooks/useTokenModule"; +import { useImportedTokens } from "../hooks/useImportedTokens"; +import { usePublicClient } from "wagmi"; +import { erc20Abi } from "viem"; + +type RecentSwapRow = { + id: string; + timeLabel: string; + side: "buy" | "sell"; + amountInRaw: string; + amountOutRaw: string; + inSymbol: string; + outSymbol: string; + inDecimals: number; + outDecimals: number; + wallet: string; + txHash: string; + chainId: string; +}; + +const RECENT_SWAPS_QUERY = ` + query RecentSwaps($pair: String!, $limit: Int!) { + Pair( + where: { pairAddress: { _ilike: $pair } } + limit: 1 + ) { + token0 + token1 + } + Swap( + where: { pairAddress: { _ilike: $pair } } + order_by: { blockTimestamp: desc } + limit: $limit + ) { + id + sender + amount0In + amount1In + amount0Out + amount1Out + blockTimestamp + pairAddress + createdAtTx + chainId + to + } + } +`; + +type RecentSwapsResponse = { + Pair: { + token0: string; + token1: string; + }[]; + Swap: { + id: string; + sender: string; + amount0In: string; + amount1In: string; + amount0Out: string; + amount1Out: string; + blockTimestamp: string; + pairAddress: string; + createdAtTx: string; + chainId: string; + to: string; + }[]; +}; + +const FALLBACK_DECIMALS = 18; + +type SimpleMeta = { + symbol: string; + decimals: number; +}; + +function formatDateTime(ts: string): string { + const n = Number(ts); + if (!Number.isFinite(n)) return ts; + const d = new Date(n * 1000); + return d.toLocaleString(undefined, { + year: "2-digit", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + }); +} + +const norm = (a?: string | null) => + typeof a === "string" ? a.toLowerCase() : ""; + +export function useRecentSwapsForPair(pairAddress: Address | null) { + const { TOKENLIST } = useTokenModule(); + const { tokens: imported } = useImportedTokens(); + const pc = usePublicClient(); + + const [data, setData] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + // Local cache for token metadata (address -> meta) + const [metaCache, setMetaCache] = useState>({}); + + function cacheMeta(addr: string, meta: SimpleMeta) { + setMetaCache((prev) => { + const key = norm(addr); + if (prev[key]) return prev; + return { ...prev, [key]: meta }; + }); + } + + function findLocalMeta(addr: string): SimpleMeta | null { + const lower = norm(addr); + const cached = metaCache[lower]; + if (cached) return cached; + + const all = [ + ...(TOKENLIST as any[]), + ...((imported ?? []) as any[]), + ]; + + for (const t of all) { + if (!t?.address) continue; + if (norm(t.address) === lower) { + const symbol: string = + typeof t.symbol === "string" && t.symbol.length > 0 + ? t.symbol + : `${addr.slice(0, 6)}…${addr.slice(-4)}`; + const decimals: number = + typeof t.decimals === "number" ? t.decimals : FALLBACK_DECIMALS; + + const meta = { symbol, decimals }; + cacheMeta(addr, meta); + return meta; + } + } + + return null; + } + + async function fetchOnchainMeta(addr: string): Promise { + const lower = norm(addr); + const cached = metaCache[lower]; + if (cached) return cached; + + // Try local TOKENLIST/imported first + const local = findLocalMeta(addr); + if (local) return local; + + if (!pc) { + const fallback: SimpleMeta = { + symbol: `${addr.slice(0, 6)}…${addr.slice(-4)}`, + decimals: FALLBACK_DECIMALS, + }; + cacheMeta(addr, fallback); + return fallback; + } + + let symbol = ""; + let decimals: number = FALLBACK_DECIMALS; + + try { + const [s, d] = await Promise.all([ + pc.readContract({ + address: addr as Address, + abi: erc20Abi, + functionName: "symbol", + }), + pc.readContract({ + address: addr as Address, + abi: erc20Abi, + functionName: "decimals", + }), + ]); + + if (typeof s === "string" && s.length > 0) { + symbol = s; + } + if (typeof d === "number") { + decimals = d; + } + } catch (e) { + // Fallback if onchain read fails + } + + if (!symbol) { + symbol = `${addr.slice(0, 6)}…${addr.slice(-4)}`; + } + + const meta = { symbol, decimals }; + cacheMeta(addr, meta); + return meta; + } + + useEffect(() => { + if (!pairAddress) { + setData([]); + return; + } + + let cancelled = false; + + async function fetchData() { + if (!pairAddress) return; + + setIsLoading(true); + try { + const res = await graphqlRequest< + RecentSwapsResponse, + { pair: string; limit: number } + >({ + query: RECENT_SWAPS_QUERY, + variables: { + pair: pairAddress.toLowerCase(), + limit: 30, + }, + }); + + if (cancelled) return; + + const pairRow = res.Pair[0]; + + let token0Addr: string | null = null; + let token1Addr: string | null = null; + + if (pairRow) { + token0Addr = pairRow.token0 ?? null; + token1Addr = pairRow.token1 ?? null; + } + + // Resolve metadata for token0 / token1 (local first, then onchain) + let token0Meta: SimpleMeta = { + symbol: "token0", + decimals: FALLBACK_DECIMALS, + }; + let token1Meta: SimpleMeta = { + symbol: "token1", + decimals: FALLBACK_DECIMALS, + }; + + if (token0Addr) { + token0Meta = await fetchOnchainMeta(token0Addr); + } + if (token1Addr) { + token1Meta = await fetchOnchainMeta(token1Addr); + } + + const token0Symbol = token0Meta.symbol; + const token1Symbol = token1Meta.symbol; + const token0Decimals = token0Meta.decimals; + const token1Decimals = token1Meta.decimals; + + const rows: RecentSwapRow[] = res.Swap.map((s) => { + const a0In = BigInt(s.amount0In); + const a1In = BigInt(s.amount1In); + const a0Out = BigInt(s.amount0Out); + const a1Out = BigInt(s.amount1Out); + + let side: "buy" | "sell" = "buy"; + + let amountInRaw = "0"; + let amountOutRaw = "0"; + let inSymbol = token0Symbol; + let outSymbol = token1Symbol; + let inDecimals = token0Decimals; + let outDecimals = token1Decimals; + + if (a0In > 0n && a1Out > 0n) { + // In: token0, Out: token1 + side = "sell"; + amountInRaw = s.amount0In; + amountOutRaw = s.amount1Out; + inSymbol = token0Symbol; + outSymbol = token1Symbol; + inDecimals = token0Decimals; + outDecimals = token1Decimals; + } else if (a1In > 0n && a0Out > 0n) { + // In: token1, Out: token0 + side = "buy"; + amountInRaw = s.amount1In; + amountOutRaw = s.amount0Out; + inSymbol = token1Symbol; + outSymbol = token0Symbol; + inDecimals = token1Decimals; + outDecimals = token0Decimals; + } else { + // Fallback + side = "sell"; + amountInRaw = s.amount0In; + amountOutRaw = s.amount1Out; + inSymbol = token0Symbol; + outSymbol = token1Symbol; + inDecimals = token0Decimals; + outDecimals = token1Decimals; + } + + return { + id: s.id, + timeLabel: formatDateTime(s.blockTimestamp), + side, + amountInRaw, + amountOutRaw, + inSymbol, + outSymbol, + inDecimals, + outDecimals, + wallet: s.to, + txHash: s.createdAtTx, + chainId: s.chainId, + }; + }); + + if (!cancelled) { + setData(rows); + } + } catch (err) { + console.error("Failed to fetch recent swaps", err); + if (!cancelled) setData([]); + } finally { + if (!cancelled) setIsLoading(false); + } + } + + fetchData(); + + return () => { + cancelled = true; + }; + }, [pairAddress, TOKENLIST, imported, pc, metaCache]); + + return { data, isLoading }; +} diff --git a/apps/web/src/lib/indexerClient.ts b/apps/web/src/lib/indexerClient.ts new file mode 100644 index 000000000..7da0f8e6c --- /dev/null +++ b/apps/web/src/lib/indexerClient.ts @@ -0,0 +1,37 @@ +const GRAPHQL_URL = + import.meta.env.VITE_INDEXER_GRAPHQL_URL ?? "http://localhost:8080/v1/graphql"; + +type GraphQLRequestOptions = { + query: string; + variables?: V; +}; + +export async function graphqlRequest>( + options: GraphQLRequestOptions, +): Promise { + const res = await fetch(GRAPHQL_URL, { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify({ + query: options.query, + variables: options.variables ?? {}, + }), + }); +console.log("[GraphQL] Query:", options.query); +console.log("[GraphQL] Variables:", options.variables); + + if (!res.ok) { + throw new Error(`GraphQL request failed with status ${res.status}`); + } + + const json = await res.json(); + + if (json.errors && json.errors.length > 0) { + console.error("GraphQL errors:", json.errors); + throw new Error(json.errors[0].message ?? "GraphQL error"); + } + + return json.data as TData; +} diff --git a/apps/web/src/pages/SwapProPage.tsx b/apps/web/src/pages/SwapProPage.tsx new file mode 100644 index 000000000..7fbbaff56 --- /dev/null +++ b/apps/web/src/pages/SwapProPage.tsx @@ -0,0 +1,61 @@ +import { useRef } from "react"; +import styles from "@ui/styles/Swap.module.css"; +import SwapFeature from "../features/swap"; +import { ProSwapChart } from "../features/swap/components/ProSwapChart"; +import { RecentSwaps } from "../features/swap/components/RecentSwaps"; +import { AIChatPanel } from "../features/swap/components/AIChatPanel"; + +export default function SwapProPage() { + const haloRef = useRef(null); + + const handleMouseMove = (e: React.MouseEvent) => { + if (!haloRef.current) return; + + const rect = (e.currentTarget as HTMLDivElement).getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + + haloRef.current.style.left = `${x}px`; + haloRef.current.style.top = `${y}px`; + haloRef.current.style.opacity = "1"; + }; + + const handleMouseLeave = () => { + if (!haloRef.current) return; + haloRef.current.style.opacity = "0"; + }; + + return ( +
+
+ +
+
+ +
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+
+ ); +} diff --git a/apps/web/src/styles/Layout.module.css b/apps/web/src/styles/Layout.module.css index 5a0d49e2e..0032d421d 100644 --- a/apps/web/src/styles/Layout.module.css +++ b/apps/web/src/styles/Layout.module.css @@ -316,3 +316,58 @@ padding: 10px; font-size: 14px; } +.dropdownWrapper { + position: relative; + display: flex; + align-items: center; +} + +/* Menu container: même style que .navbar / .connectWalletBtn mais en petit */ +.dropdownMenu { + position: absolute; + top: 52px; /* juste sous la navbar de 50px */ + left: 40%; + transform: translateX(-50%); + background-color: var(--black); + color: var(--text-color); + border-radius: 15px; + border: 1px solid var(--black-border); + padding: 4px; + min-width: 110px; + display: flex; + flex-direction: column; + z-index: 100000; + + box-shadow: + inset 0 1px 3px -0.25px rgba(255, 255, 255, 0.12), + inset 0 0.5px 0.25px -0.25px rgba(255, 255, 255, 0.16), + inset 0 -0.75px 0.5px var(--btn-shadow-black), + 0 4px 4px -1px var(--btn-shadow-black), + 0 2px 3px -1px var(--btn-shadow-black), + 0 0.5px 0.5px var(--btn-shadow-black), + 0 0 0 0.75px var(--btn-shadow-black); +} + +/* Items: style proche de linkBase mais en plus compact */ +.dropdownItem { + display: flex; + align-items: center; + height: 36px; + padding: 0 14px; + border-radius: 1.6vh; + font-size: 13px; + text-decoration: none; + color: var(--text-grey); + white-space: nowrap; + transition: background 0.16s ease, color 0.16s ease; +} + +.dropdownItem:hover { + background: rgba(255, 255, 255, 0.06); + color: var(--text-color); +} + +.dropdownItemActive { + background: rgba(255, 255, 255, 0.08); + color: var(--text-color); +} diff --git a/packages/ui/src/styles/Swap.module.css b/packages/ui/src/styles/Swap.module.css index d21302c48..a597fc4e9 100644 --- a/packages/ui/src/styles/Swap.module.css +++ b/packages/ui/src/styles/Swap.module.css @@ -54,7 +54,7 @@ -webkit-backdrop-filter: blur(10px); border: 1px solid rgba(49, 49, 49, 0.3); color: var(--text-color); - border-radius: 30px; + border-radius: 20px; height: auto; width: 500px; z-index: 2; @@ -133,12 +133,12 @@ 0 2px 3px -1px var(--btn-shadow-black), 0 0.5px 0.5px var(--btn-shadow-black), 0 0 0 0.75px var(--btn-shadow-black); - border-radius: 2.5vh; + border-radius: 20px; padding-top: 1.3vh; color: var(--text-grey-input); font-weight: 500; letter-spacing: -0.08vh; - font-size: 1.6vh; + font-size: 18px; padding-bottom: 2vh; margin-bottom: 0.6vh; } @@ -155,7 +155,7 @@ 0 2px 3px -1px var(--btn-shadow-black), 0 0.5px 0.5px var(--btn-shadow-black), 0 0 0 0.75px var(--btn-shadow-black); - border-radius: 2.5vh; + border-radius: 20px; padding-top: 1.3vh; color: var(--text-grey-input); font-weight: 500; @@ -231,8 +231,8 @@ 0 0 0 0.75px var(--btn-shadow-black); margin-left: auto; margin-right: auto; - height: 5.4vh; - width: 5.4vh; + height: 66px; + width: 66px; cursor: pointer; border-radius: 1.3vh; display: flex; @@ -339,4 +339,507 @@ margin-right: 1vh; margin-left: 1vh; } - } \ No newline at end of file + } + + + /* Pro layout */ + +.proContainerBody { + position: relative; + min-height: 900px; + padding-top: 180px; + display: flex; + flex-direction: column; + align-items: center; +} + +.proMain { + width: 95%; + max-width: 1200px; + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(0, 1.3fr); + gap: 24px; + padding: 0 24px; + margin-bottom: 24px; +} + + + +.proRight { + display: flex; + justify-content: flex-end; +} + +.proBottom { + width: 95%; + max-width: 1200px; + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(0, 1.3fr); + gap: 24px; + padding: 0 24px; + margin-bottom: 24px; +} + + + +/* Chart */ + +.proChartRoot { + height: 90%; + min-height: 400px; + border-radius: 20px; + position: relative; + background: rgba(0, 0, 0, 0.624); + border: 1px solid var(--black-border); + box-shadow: + inset 0 1px 3px -0.25px rgba(255, 255, 255, 0.12), + inset 0 0.5px 0.25px -0.25px rgba(255, 255, 255, 0.16), + inset 0 -0.75px 0.5px var(--btn-shadow-black), + 0 4px 4px -1px var(--btn-shadow-black), + 0 2px 3px -1px var(--btn-shadow-black), + 0 0.5px 0.5px var(--btn-shadow-black), + 0 0 0 0.75px var(--btn-shadow-black); + padding: 16px 18px 18px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.proChartHeader { + display: flex; + align-items: center; + justify-content: space-between; +} + +.proChartPair { + display: flex; + align-items: center; + gap: 10px; +} + +.proChartPairIcons { + display: flex; + align-items: center; +} + +.tokenIcon { + width: 30px; + height: 30px; + border-radius: 999px; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: 600; +} + +.tokenIconMain { + background: linear-gradient(135deg, #23d5e3, #3b82f6); + margin-right: -8px; + z-index: 2; +} + +.tokenIconQuote { + background: linear-gradient(135deg, #10b981, #22d3ee); + border: 2px solid #050505; +} + +.proChartPairSymbol { + font-size: 13px; + opacity: 0.8; +} + +.proChartPairPrice { + font-size: 20px; + font-weight: 600; +} + +.proChartTimeframes { + display: inline-flex; + border-radius: 999px; + background: rgb(12, 12, 12); + padding: 2px; +} + +.proChartTfButton { + border: none; + background: transparent; + color: var(--text-grey); + font-size: 11px; + padding: 4px 10px; + border-radius: 999px; + cursor: pointer; +} + +.proChartTfButton:hover { + color: var(--text-color); + background: rgb(116, 116, 116); +} + +.proChartCanvas { + flex: 1; + margin-top: 8px; + border-radius: 16px; + background: radial-gradient(circle at top, #151515, #050505); + border: 1px solid rgba(80, 80, 80, 0.5); + position: relative; +} + +.proChartPlaceholderText { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 12px; + opacity: 0.6; +} + +/* Recent swaps */ + +.recentSwapsRoot { + height: 90%; + border-radius: 20px; + position: relative; + background: rgba(0, 0, 0, 0.624); + border: 1px solid var(--black-border); + box-shadow: + inset 0 1px 3px -0.25px rgba(255, 255, 255, 0.12), + inset 0 0.5px 0.25px -0.25px rgba(255, 255, 255, 0.16), + inset 0 -0.75px 0.5px var(--btn-shadow-black), + 0 4px 4px -1px var(--btn-shadow-black), + 0 2px 3px -1px var(--btn-shadow-black), + 0 0.5px 0.5px var(--btn-shadow-black), + 0 0 0 0.75px var(--btn-shadow-black); + padding: 16px 18px 18px; +} + +.recentSwapsTableScrollable { + max-height: 360px; /* ≈ 20 lignes */ + overflow-y: auto; + overflow-x: hidden; + margin-top: 8px; + padding-right: 6px; /* pour éviter que le scroll se colle au bord */ +} + +/* Optional: custom scrollbar for a cleaner UI */ +.recentSwapsTableScrollable::-webkit-scrollbar { + width: 6px; +} + +.recentSwapsTableScrollable::-webkit-scrollbar-thumb { + background: rgba(255,255,255,0.15); + border-radius: 6px; +} + +.recentSwapsTableScrollable::-webkit-scrollbar-thumb:hover { + background: rgba(255,255,255,0.25); +} + +.recentSwapsTable thead th { + position: sticky; + top: 0; + background: var(--black); + z-index: 5; +} + +.recentSwapsHeader { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; +} + +.recentSwapsTitle { + font-size: 14px; + letter-spacing: -0.4px; +} + +.recentSwapsTableWrapper { + overflow-x: auto; +} + +.recentSwapsTable { + width: 100%; + border-collapse: collapse; + font-size: 12px; +} + +.recentSwapsTable thead th { + text-align: left; + padding: 6px 4px; + font-weight: 500; + border-bottom: 1px solid rgba(80, 80, 80, 0.7); +} + +.recentSwapsTable td { + padding: 6px 4px; + white-space: nowrap; + border-bottom: 1px solid rgba(255,255,255,0.05); + +} + +.recentSwapsTable tbody tr:hover { + background: rgba(255, 255, 255, 0.02); +} + +.swapSideBuy { + color: #22c55e; +} + +.swapSideSell { + color: #ef4444; +} + +/* Responsive */ + +@media (max-width: 1024px) { + .proMain { + grid-template-columns: minmax(0, 1fr); + } + + .proRight { + justify-content: center; + } +} + + + +/* ============================ + BUY / SELL COLORS + ============================*/ +.swapSideBuy { + color: #2ecc71; /* green */ + font-weight: 600; +} + +.swapSideSell { + color: #ff4d4d; /* red */ + font-weight: 600; +} + +/* ============================ + TX HASH LINK + ============================*/ +.txLink { + color: var(--swap-text-color); + text-decoration: none; + font-weight: 500; + transition: opacity 0.15s ease; +} + +.txLink:hover { + opacity: 0.8; +} + +/* ============================ + RESPONSIVE + ============================*/ +@media (max-width: 800px) { + .recentSwapsTitle { + font-size: 14px; + } + + .recentSwapsTable { + font-size: 12px; + } + + .recentSwapsTable thead tr th { + padding: 6px; + } + + .recentSwapsTable td { + padding: 4px; + } +} + + +.proChartCard { + background: rgba(35, 35, 35, 0.2); + border-radius: 16px; + padding: 12px 14px 8px; + border: 1px solid rgba(101, 101, 101, 0.3); + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + display: flex; + flex-direction: column; + height: 100%; +} + +.proChartHeader { + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 8px; +} + +.proChartPair { + font-size: 13px; + font-weight: 500; + color: var(--text-color); + letter-spacing: -0.4px; + margin-bottom: 2px; +} + +.proChartPriceRow { + display: flex; + align-items: baseline; + gap: 8px; +} + +.proChartPrice { + font-size: 20px; + font-weight: 600; + color: var(--text-color); + letter-spacing: -0.6px; +} + +.priceChangePositive { + font-size: 12px; + font-weight: 500; + color: #2ecc71; +} + +.priceChangeNegative { + font-size: 12px; + font-weight: 500; + color: #ff4d4d; +} + +.proChartTimeframeGroup { + display: inline-flex; + gap: 4px; + padding: 3px; + border-radius: 999px; + background: rgba(0, 0, 0, 0.45); + border: 1px solid rgba(255, 255, 255, 0.06); +} + +.proChartTimeframe, +.proChartTimeframeActive { + min-width: 34px; + padding: 3px 6px; + border-radius: 999px; + border: none; + font-size: 10px; + letter-spacing: -0.2px; + cursor: pointer; + transition: all 0.15s ease; +} + +.proChartTimeframe { + background: transparent; + color: var(--text-grey); +} + +.proChartTimeframe:hover { + background: rgba(255, 255, 255, 0.04); + color: var(--text-color); +} + +.proChartTimeframeActive { + background: var(--black); + color: var(--text-color); + box-shadow: + inset 0 1px 2px rgba(255, 255, 255, 0.12), + 0 0 0 1px rgba(255, 255, 255, 0.08); +} + +.proChartBody { + margin-top: 4px; +} + +.proChartEmpty { + height: 240px; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + color: var(--text-grey); + border-radius: 12px; + border: 1px dashed rgba(255, 255, 255, 0.12); +} + +.proSwapGrid { + display: grid; + grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); + gap: 16px; + align-items: stretch; +} + +.proSwapLeft { + display: flex; + flex-direction: column; + gap: 12px; +} + +.proSwapRight { + display: flex; + flex-direction: column; +} + + + + +/* Chat card styling */ +.aiChatRoot { + position: relative; + background: rgba(0, 0, 0, 0.624); + border-radius: 20px; + border: 1px solid var(--black-border); + box-shadow: + inset 0 1px 3px -0.25px rgba(255, 255, 255, 0.12), + inset 0 0.5px 0.25px -0.25px rgba(255, 255, 255, 0.16), + inset 0 -0.75px 0.5px var(--btn-shadow-black), + 0 4px 4px -1px var(--btn-shadow-black), + 0 2px 3px -1px var(--btn-shadow-black), + 0 0.5px 0.5px var(--btn-shadow-black), + 0 0 0 0.75px var(--btn-shadow-black); + padding: 16px 18px 18px; + display: flex; + flex-direction: column; +} + +.aiChatHeader { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 10px; +} + +.aiChatTitle { + font-size: 14px; + letter-spacing: -0.4px; +} + +.aiChatTag { + font-size: 10px; + padding: 2px 8px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.06); + border: 1px solid rgba(255, 255, 255, 0.12); + text-transform: uppercase; + letter-spacing: 0.06em; + opacity: 0.8; +} + +.aiChatBody { + flex: 1; + display: flex; + align-items: center; + justify-content: center; +} + +.aiChatPlaceholder { + font-size: 12px; + opacity: 0.7; + text-align: center; + line-height: 1.4; +} + +/* Responsive: stack bottom panels on narrow screens */ +@media (max-width: 1024px) { + .proBottom { + grid-template-columns: minmax(0, 1fr); + } +} \ No newline at end of file diff --git a/packages/ui/src/styles/TokenSelector.module.css b/packages/ui/src/styles/TokenSelector.module.css index b1fb11c2c..72eb886d8 100644 --- a/packages/ui/src/styles/TokenSelector.module.css +++ b/packages/ui/src/styles/TokenSelector.module.css @@ -1,7 +1,7 @@ .btnTokenSelector { - height: 5.4vh; - border-radius: 1.5vh; - margin-right: 2vh; + height: 68px; + border-radius: 20px; + margin-right: 22px; background-color: var(--black); border: 1px solid var(--black-border); box-shadow: @@ -14,7 +14,7 @@ 0 0 0 0.75px var(--btn-shadow-black); color: var(--text-color); font-weight: 500; - font-size: 1.9vh; + font-size: 23px; letter-spacing: -0.08vh; cursor: pointer; display: flex; @@ -35,12 +35,12 @@ .tokenIcon { - width: 3.2vh; + width: 38px; margin-right: 0.3vh; } .arrowIcone { - width: 2.5vh; + width: 30px; } .titleSearchToken { @@ -244,5 +244,5 @@ .tokenPlaceholder{ display: flex; align-items: center; - width: 10vh; + width: 130px; } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12426d078..b8341eaf3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,47 @@ importers: specifier: ~5.6.2 version: 5.6.3 + apps/indexer: + dependencies: + envio: + specifier: 2.32.3 + version: 2.32.3(bufferutil@4.0.9)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + generated: + specifier: ./generated + version: link:generated + devDependencies: + '@rescript/react': + specifier: ^0.14.0 + version: 0.14.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@types/chai': + specifier: ^4.3.11 + version: 4.3.20 + '@types/mocha': + specifier: 10.0.6 + version: 10.0.6 + '@types/node': + specifier: 20.8.8 + version: 20.8.8 + chai: + specifier: 4.3.10 + version: 4.3.10 + mocha: + specifier: 10.2.0 + version: 10.2.0 + rescript: + specifier: ^11.1.4 + version: 11.1.4 + rescript-envsafe: + specifier: ^5.0.0 + version: 5.0.0(rescript-schema@9.3.0(rescript@11.1.4))(rescript@11.1.4) + ts-mocha: + specifier: ^10.0.0 + version: 10.1.0(mocha@10.2.0) + typescript: + specifier: 5.2.2 + version: 5.2.2 + apps/web: dependencies: '@dynamic-labs/ethereum': @@ -94,9 +135,15 @@ importers: '@trustswap/ui': specifier: workspace:^ version: link:../../packages/ui + apexcharts: + specifier: ^5.3.6 + version: 5.3.6 react: specifier: ^19.1.1 version: 19.1.1 + react-apexcharts: + specifier: ^1.8.0 + version: 1.8.0(apexcharts@5.3.6)(react@19.1.1) react-dom: specifier: ^19.1.1 version: 19.1.1(react@19.1.1) @@ -252,6 +299,9 @@ packages: graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 typescript: ^5.0.0 + '@adraffy/ens-normalize@1.10.0': + resolution: {integrity: sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==} + '@adraffy/ens-normalize@1.10.1': resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} @@ -1560,9 +1610,97 @@ packages: peerDependencies: '@noble/ciphers': ^1.0.0 + '@elastic/ecs-helpers@1.1.0': + resolution: {integrity: sha512-MDLb2aFeGjg46O5mLpdCzT5yOUDnXToJSrco2ShqGIXxNJaM8uJjX+4nd+hRYV4Vex8YJyDtOFEVBldQct6ndg==} + engines: {node: '>=10'} + + '@elastic/ecs-pino-format@1.4.0': + resolution: {integrity: sha512-eCSBUTgl8KbPyxky8cecDRLCYu2C1oFV4AZ72bEsI+TxXEvaljaL2kgttfzfu7gW+M89eCz55s49uF2t+YMTWA==} + engines: {node: '>=10'} + '@emnapi/runtime@1.5.0': resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + '@envio-dev/hyperfuel-client-darwin-arm64@1.2.2': + resolution: {integrity: sha512-eQyd9kJCIz/4WCTjkjpQg80DA3pdneHP7qhJIVQ2ZG+Jew9o5XDG+uI0Y16AgGzZ6KGmJSJF6wyUaaAjJfbO1Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@envio-dev/hyperfuel-client-darwin-x64@1.2.2': + resolution: {integrity: sha512-l7lRMSoyIiIvKZgQPfgqg7H1xnrQ37A8yUp4S2ys47R8f/wSCSrmMaY1u7n6CxVYCpR9fajwy0/356UgwwhVKw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@envio-dev/hyperfuel-client-linux-arm64-gnu@1.2.2': + resolution: {integrity: sha512-kNiC/1fKuXnoSxp8yEsloDw4Ot/mIcNoYYGLl2CipSIpBtSuiBH5nb6eBcxnRZdKOwf5dKZtZ7MVPL9qJocNJw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@envio-dev/hyperfuel-client-linux-x64-gnu@1.2.2': + resolution: {integrity: sha512-XDkvkBG/frS+xiZkJdY4KqOaoAwyxPdi2MysDQgF8NmZdssi32SWch0r4LTqKWLLlCBg9/R55POeXL5UAjg2wQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@envio-dev/hyperfuel-client-linux-x64-musl@1.2.2': + resolution: {integrity: sha512-DKnKJJSwsYtA7YT0EFGhFB5Eqoo42X0l0vZBv4lDuxngEXiiNjeLemXoKQVDzhcbILD7eyXNa5jWUc+2hpmkEg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@envio-dev/hyperfuel-client-win32-x64-msvc@1.2.2': + resolution: {integrity: sha512-SwIgTAVM9QhCFPyHwL+e1yQ6o3paV6q25klESkXw+r/KW9QPhOOyA6Yr8nfnur3uqMTLJHAKHTLUnkyi/Nh7Aw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@envio-dev/hyperfuel-client@1.2.2': + resolution: {integrity: sha512-raKA6DshYSle0sAOHBV1OkSRFMN+Mkz8sFiMmS3k+m5nP6pP56E17CRRePBL5qmR6ZgSEvGOz/44QUiKNkK9Pg==} + engines: {node: '>= 10'} + + '@envio-dev/hypersync-client-darwin-arm64@0.6.6': + resolution: {integrity: sha512-5uAwSNrnekbHiZBLipUPM0blfO0TS2svyuMmDVE+xbT3M+ODuQl4BFoINd9VY6jC5EoKt8xKCO2K/DHHSeRV4A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@envio-dev/hypersync-client-darwin-x64@0.6.6': + resolution: {integrity: sha512-KFMXWpHbyA0q+sRQ6I8YcLIwZFbBjMEncTnRz6IWXNWAXOsIc1GOORz0j5c9I330bEa4cdQdVVWhgCR1gJiBBA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@envio-dev/hypersync-client-linux-arm64-gnu@0.6.6': + resolution: {integrity: sha512-Iiok/+YNtVft37KGWwDPC8yiN4rAZujYTiYiu+j+vfRpJT6DnYj/TbklZ/6LnSafg18BMPZ2fHT804jP0LndHg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@envio-dev/hypersync-client-linux-x64-gnu@0.6.6': + resolution: {integrity: sha512-WgQRjJS1ncdP/f89dGBKD1luC/r+0EJZgvXSJ+8Jy4dnAeMHUgDFCpjJqIqQKxCWX0fmoiJ7a31SzBNV8Lwqbg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@envio-dev/hypersync-client-linux-x64-musl@0.6.6': + resolution: {integrity: sha512-upFn8FfcUP5pTdSiQAsEr06L2SwyxluMWMaeUCgAEYxDcKTxUkg0J2eDq37RGUQ0KVlLoWLthnSsg4lUz7NIXg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@envio-dev/hypersync-client-win32-x64-msvc@0.6.6': + resolution: {integrity: sha512-bVFDkyrddbMnNGYd6o/QwhrviHOa4th/aMjzMPRjXu48GI8xqlamQ6RBxDGy2lg+BoPhs5k3kwOWl/DY29RwUQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@envio-dev/hypersync-client@0.6.6': + resolution: {integrity: sha512-0r4lPFtk49zB94uvZiONV0SWdr9kigdNIYfYTYcSSuZ396E77tjskjMigDwimZsAA5Qf64x6MsIyzUYIzk/KPg==} + engines: {node: '>= 10'} + '@esbuild/aix-ppc64@0.25.9': resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} engines: {node: '>=18'} @@ -2210,6 +2348,9 @@ packages: '@noble/curves@1.2.0': resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + '@noble/curves@1.4.0': + resolution: {integrity: sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==} + '@noble/curves@1.4.2': resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} @@ -2414,6 +2555,10 @@ packages: resolution: {integrity: sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==} engines: {node: '>= 12'} + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + '@openzeppelin/contracts@4.9.6': resolution: {integrity: sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA==} @@ -2472,6 +2617,12 @@ packages: '@reown/appkit@1.7.8': resolution: {integrity: sha512-51kTleozhA618T1UvMghkhKfaPcc9JlKwLJ5uV+riHyvSoWPKPRIa5A6M1Wano5puNyW0s3fwywhyqTHSilkaA==} + '@rescript/react@0.14.0': + resolution: {integrity: sha512-ncOHWK7ujQmff+QMYKRmtwETvJVolzkwRpDa0MFenEXdUz9ZYywNbq+xH9F9RDQeSwC3/4s9JeUQVyTu4fMpHw==} + peerDependencies: + react: '>=19.0.0' + react-dom: '>=19.0.0' + '@rolldown/pluginutils@1.0.0-beta.34': resolution: {integrity: sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==} @@ -2778,6 +2929,31 @@ packages: '@solidity-parser/parser@0.20.2': resolution: {integrity: sha512-rbu0bzwNvMcwAjH86hiEAcOeRI2EeK8zCkHDrFykh/Al8mvJeFmjy3UrE7GYQjNwOgbGUUtCn5/k8CB8zIu7QA==} + '@svgdotjs/svg.draggable.js@3.0.6': + resolution: {integrity: sha512-7iJFm9lL3C40HQcqzEfezK2l+dW2CpoVY3b77KQGqc8GXWa6LhhmX5Ckv7alQfUXBuZbjpICZ+Dvq1czlGx7gA==} + peerDependencies: + '@svgdotjs/svg.js': ^3.2.4 + + '@svgdotjs/svg.filter.js@3.0.9': + resolution: {integrity: sha512-/69XMRCDoam2HgC4ldHIaDgeQf1ViHIsa0Ld4uWgiXtZ+E24DWHe/9Ib6kbNiZ7WRIdlVokUDR1Fg0kjIpkfbw==} + engines: {node: '>= 0.8.0'} + + '@svgdotjs/svg.js@3.2.5': + resolution: {integrity: sha512-/VNHWYhNu+BS7ktbYoVGrCmsXDh+chFMaONMwGNdIBcFHrWqk2jY8fNyr3DLdtQUIalvkPfM554ZSFa3dm3nxQ==} + + '@svgdotjs/svg.resize.js@2.0.5': + resolution: {integrity: sha512-4heRW4B1QrJeENfi7326lUPYBCevj78FJs8kfeDxn5st0IYPIRXoTtOSYvTzFWgaWWXd3YCDE6ao4fmv91RthA==} + engines: {node: '>= 14.18'} + peerDependencies: + '@svgdotjs/svg.js': ^3.2.4 + '@svgdotjs/svg.select.js': ^4.0.1 + + '@svgdotjs/svg.select.js@4.0.3': + resolution: {integrity: sha512-qkMgso1sd2hXKd1FZ1weO7ANq12sNmQJeGDjs46QwDVsxSRcHmvWKL2NDF7Yimpwf3sl5esOLkPqtV2bQ3v/Jg==} + engines: {node: '>= 14.18'} + peerDependencies: + '@svgdotjs/svg.js': ^3.2.4 + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} engines: {node: '>=14'} @@ -3048,6 +3224,9 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/lodash@4.17.20': resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} @@ -3067,6 +3246,9 @@ packages: '@types/mocha@10.0.10': resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} + '@types/mocha@10.0.6': + resolution: {integrity: sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==} + '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} @@ -3079,6 +3261,9 @@ packages: '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + '@types/node@20.8.8': + resolution: {integrity: sha512-YRsdVxq6OaLfmR9Hy816IMp33xOBjfyOgUd77ehqg96CFywxAPbDbXvAsuN2KVg2HOT8Eh6uAfU+l4WffwPVrQ==} + '@types/node@22.7.5': resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} @@ -3466,9 +3651,23 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + '@yr/monotone-cubic-spline@1.0.3': + resolution: {integrity: sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==} + abbrev@1.0.9: resolution: {integrity: sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==} + abitype@1.0.5: + resolution: {integrity: sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + abitype@1.0.8: resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==} peerDependencies: @@ -3491,6 +3690,10 @@ packages: zod: optional: true + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -3578,6 +3781,10 @@ packages: ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + ansi-colors@4.1.1: + resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} + engines: {node: '>=6'} + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -3615,6 +3822,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + apexcharts@5.3.6: + resolution: {integrity: sha512-sVEPw+J0Gp0IHQabKu8cfdsxlfME0e36Wid7RIaPclGM2OUt+O7O4+6mfAmTUYhy5bDk8cNHzEhPfVtLCIXEJA==} + arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -3642,6 +3852,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} @@ -3747,6 +3961,9 @@ packages: resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} engines: {node: '>= 10.0.0'} + bignumber.js@9.1.2: + resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} + bignumber.js@9.3.1: resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} @@ -3757,6 +3974,9 @@ packages: bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + bintrees@1.0.2: + resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==} + blakejs@1.2.1: resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} @@ -3921,6 +4141,10 @@ packages: peerDependencies: chai: '>= 2.1.2 < 6' + chai@4.3.10: + resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} + engines: {node: '>=4'} + chai@4.5.0: resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} engines: {node: '>=4'} @@ -3966,6 +4190,10 @@ packages: resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} engines: {node: '>= 6'} + chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -4364,6 +4592,9 @@ packages: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + dayjs@1.11.13: resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} @@ -4381,6 +4612,15 @@ packages: supports-color: optional: true + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.3.7: resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} @@ -4513,12 +4753,16 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + diff@3.5.0: + resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} + engines: {node: '>=0.3.1'} + diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} - diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + diff@5.0.0: + resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} engines: {node: '>=0.3.1'} difflib@0.2.4: @@ -4661,6 +4905,30 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} + envio-darwin-arm64@2.32.3: + resolution: {integrity: sha512-taRiIvTNKoxWT/8XqJbbsQo4zLUckP00i87L+EtCXpUl/JmhhbZHrePsM6qSNo3pKZOvq0FFo8UMfHNO+34SsA==} + cpu: [arm64] + os: [darwin] + + envio-darwin-x64@2.32.3: + resolution: {integrity: sha512-o1YF+EDDLhJaxMXo7BAZ87nnsge8mMyiKVVkCN+Mf6qaB6dX5K0UTQejJfTwDVT7O8HZY7Mi3mSAGRupWBPlKg==} + cpu: [x64] + os: [darwin] + + envio-linux-arm64@2.32.3: + resolution: {integrity: sha512-YDAu9V6/CARh/ETuB3lSt7Gd7I2IYM3LHIoVlzxAYvDhSf6QwF3OA2c4K9mlF7NxyTLxSP168e6ZwIMDk0o2nw==} + cpu: [arm64] + os: [linux] + + envio-linux-x64@2.32.3: + resolution: {integrity: sha512-V4H4R+p2jBkR4sP9Q0sbJe8Z80RyZN9bMvgoAEXLuFvtqtfMgluAPUMK0pON2zb/fUEXeEAVIs5JAlOFLm8D1Q==} + cpu: [x64] + os: [linux] + + envio@2.32.3: + resolution: {integrity: sha512-kTbBknbjuFit7vVBeKbXz79vzHM9bRdFo2LpsaZ6HRfT6voG0cC/MtWJMRko0CrHuBFNqik/UDWF6ghTqkv6rw==} + hasBin: true + error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -4883,6 +5151,10 @@ packages: resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} engines: {node: '>= 0.8'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + eventemitter2@6.4.9: resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==} @@ -4922,6 +5194,9 @@ packages: resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} engines: {node: '> 0.1.90'} + fast-copy@3.0.2: + resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -4932,6 +5207,10 @@ packages: fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-json-stringify@2.7.13: + resolution: {integrity: sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA==} + engines: {node: '>= 10.0.0'} + fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} @@ -5179,6 +5458,10 @@ packages: resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.0: + resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} + deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -5355,6 +5638,9 @@ packages: heap@0.2.7: resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} + help-me@4.2.0: + resolution: {integrity: sha512-TAOnTB8Tz5Dw8penUuzHVrKNKlCIbwwbHnXraNJxPwf8LRtE2HlM84RYuezMFcwOJmoYOCWVDyJ8TQGxn9PgxA==} + history@4.10.1: resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} @@ -5725,6 +6011,11 @@ packages: peerDependencies: ws: '*' + isows@1.0.4: + resolution: {integrity: sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ==} + peerDependencies: + ws: '*' + isows@1.0.6: resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==} peerDependencies: @@ -5762,6 +6053,10 @@ packages: joi@17.13.3: resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + js-sha3@0.8.0: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} @@ -5810,6 +6105,10 @@ packages: json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -6252,6 +6551,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@5.0.1: + resolution: {integrity: sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==} + engines: {node: '>=10'} + minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} @@ -6287,8 +6590,8 @@ packages: mnemonist@0.38.5: resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==} - mocha@10.8.2: - resolution: {integrity: sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==} + mocha@10.2.0: + resolution: {integrity: sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==} engines: {node: '>= 14.0.0'} hasBin: true @@ -6299,6 +6602,9 @@ packages: ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -6317,6 +6623,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.3: + resolution: {integrity: sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -6449,6 +6760,10 @@ packages: on-exit-leak-free@0.2.0: resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==} + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -6686,13 +7001,30 @@ packages: pino-abstract-transport@0.5.0: resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==} + pino-abstract-transport@1.1.0: + resolution: {integrity: sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==} + + pino-abstract-transport@1.2.0: + resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + + pino-pretty@10.2.3: + resolution: {integrity: sha512-4jfIUc8TC1GPUfDyMSlW1STeORqkoxec71yhxIpLDQapUu8WOuoz2TTCoidrIssyz78LZC69whBMPIKCMbi3cw==} + hasBin: true + pino-std-serializers@4.0.0: resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==} + pino-std-serializers@6.2.2: + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + pino@7.11.0: resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==} hasBin: true + pino@8.16.1: + resolution: {integrity: sha512-3bKsVhBmgPjGV9pyn4fO/8RtoVDR8ssW1ev819FsRXlRNgW8gR/9Kx+gCK4UPWd4JjrRDLWpzd/pb1AyWm3MGA==} + hasBin: true + pkg-dir@7.0.0: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} @@ -7144,6 +7476,17 @@ packages: process-warning@1.0.0: resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} + process-warning@2.3.2: + resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + prom-client@15.0.0: + resolution: {integrity: sha512-UocpgIrKyA2TKLVZDSfm8rGkL13C19YrQBAiG3xo3aDFWcHedxRxI3z+cIcucoxpSO0h5lff5iv/SXoxyeopeA==} + engines: {node: ^16 || ^18 || >=20} + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -7234,6 +7577,12 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-apexcharts@1.8.0: + resolution: {integrity: sha512-MuEp56gc0NMO2UUgY94fxQzoBE4XEjmcCha4xYY0vJdRrc1yfFFZE4QrCekOt2wcS3nibghzca/q/CbgkAgN5w==} + peerDependencies: + apexcharts: '>=4.0.0' + react: '>=0.13' + react-clientside-effect@1.2.8: resolution: {integrity: sha512-ma2FePH0z3px2+WOu6h+YycZcEvFmmxIlAb62cF52bG86eMySciO/EQZeQMXd07kPCYB0a1dWDT5J+KE9mCDUw==} peerDependencies: @@ -7341,6 +7690,10 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -7353,6 +7706,10 @@ packages: resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==} engines: {node: '>= 12.13.0'} + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + rechoir@0.6.2: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} @@ -7464,6 +7821,30 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + rescript-envsafe@5.0.0: + resolution: {integrity: sha512-xSQbNsFSSQEynvLWUYtI7GJJhzicACLTq5aO1tjgK0N2Vcm9qlrkcLSmnU8tTohebEu9zgm1V/xYY+oGeQgLvA==} + peerDependencies: + rescript: 11.x + rescript-schema: 9.x + + rescript-schema@9.3.0: + resolution: {integrity: sha512-NiHAjlhFKZCmNhx/Ij40YltCEJJgVNhBWTN/ZfagTg5hdWWuvCiUacxZv+Q/QQolrAhTnHnCrL7RDvZBogHl5A==} + peerDependencies: + rescript: 11.x + peerDependenciesMeta: + rescript: + optional: true + + rescript@11.1.3: + resolution: {integrity: sha512-bI+yxDcwsv7qE34zLuXeO8Qkc2+1ng5ErlSjnUIZdrAWKoGzHXpJ6ZxiiRBUoYnoMsgRwhqvrugIFyNgWasmsw==} + engines: {node: '>=10'} + hasBin: true + + rescript@11.1.4: + resolution: {integrity: sha512-0bGU0bocihjSC6MsE3TMjHjY0EUpchyrREquLS8VsZ3ohSMD+VHUEwimEfB3kpBI1vYkw3UFZ3WD8R28guz/Vw==} + engines: {node: '>=10'} + hasBin: true + resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -7500,6 +7881,9 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -7583,6 +7967,9 @@ packages: resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} engines: {node: '>=4'} + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + select-hose@2.0.0: resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} @@ -7611,6 +7998,9 @@ packages: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} + serialize-javascript@6.0.0: + resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -7765,6 +8155,9 @@ packages: sonic-boom@2.8.0: resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} + sonic-boom@3.8.1: + resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + sort-css-media-queries@2.2.0: resolution: {integrity: sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==} engines: {node: '>= 6.3.0'} @@ -7847,6 +8240,10 @@ packages: string-format@2.0.0: resolution: {integrity: sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==} + string-similarity@4.0.4: + resolution: {integrity: sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -7880,6 +8277,10 @@ packages: resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} engines: {node: '>=0.10.0'} + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -7956,6 +8357,9 @@ packages: resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} engines: {node: '>=6'} + tdigest@0.1.2: + resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==} + terser-webpack-plugin@5.3.14: resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} engines: {node: '>= 10.13.0'} @@ -7987,6 +8391,9 @@ packages: thread-stream@0.15.2: resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==} + thread-stream@2.7.0: + resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + through2@4.0.2: resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} @@ -8061,6 +8468,13 @@ packages: peerDependencies: typescript: '>=3.7.0' + ts-mocha@10.1.0: + resolution: {integrity: sha512-T0C0Xm3/WqCuF2tpa0GNGESTBoKZaiqdUP8guNv4ZY316AFXlyidnrzQ1LUrCT0Wb1i3J0zFTgOh/55Un44WdA==} + engines: {node: '>= 6.X.X'} + hasBin: true + peerDependencies: + mocha: ^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X || ^11.X.X + ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true @@ -8075,6 +8489,14 @@ packages: '@swc/wasm': optional: true + ts-node@7.0.1: + resolution: {integrity: sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==} + engines: {node: '>=4.2.0'} + hasBin: true + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -8185,6 +8607,11 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' + typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.6.3: resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} engines: {node: '>=14.17'} @@ -8225,6 +8652,9 @@ packages: uncrypto@0.1.3: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + undici-types@5.25.3: + resolution: {integrity: sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==} + undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} @@ -8477,6 +8907,14 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + viem@2.21.0: + resolution: {integrity: sha512-9g3Gw2nOU6t4bNuoDI5vwVExzIxseU0J7Jjx10gA2RNQVrytIrLxggW++tWEe3w4mnnm/pS1WgZFjQ/QKf/nHw==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + viem@2.23.2: resolution: {integrity: sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==} peerDependencies: @@ -8570,6 +9008,9 @@ packages: resolution: {integrity: sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==} engines: {node: '>=8.0.0'} + webauthn-p256@0.0.5: + resolution: {integrity: sha512-drMGNWKdaixZNobeORVIqq7k5DsRC9FnG201K2QjeOoQLmtSDaSsVZdkg6n5jUALJKcAG++zBPJXmv6hy0nWFg==} + webextension-polyfill@0.10.0: resolution: {integrity: sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==} @@ -8677,8 +9118,8 @@ packages: resolution: {integrity: sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==} engines: {node: '>=8.0.0'} - workerpool@6.5.1: - resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} + workerpool@6.2.1: + resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} @@ -8788,6 +9229,10 @@ packages: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} + yargs-parser@20.2.4: + resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} + engines: {node: '>=10'} + yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} @@ -8804,6 +9249,10 @@ packages: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} + yn@2.0.0: + resolution: {integrity: sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==} + engines: {node: '>=4'} + yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -8880,6 +9329,8 @@ snapshots: graphql: 16.11.0 typescript: 5.8.3 + '@adraffy/ens-normalize@1.10.0': {} + '@adraffy/ens-normalize@1.10.1': {} '@adraffy/ens-normalize@1.11.0': {} @@ -9024,7 +9475,7 @@ snapshots: '@babel/traverse': 7.28.3 '@babel/types': 7.28.2 convert-source-map: 2.0.0 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -9076,7 +9527,7 @@ snapshots: '@babel/core': 7.28.3 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 lodash.debounce: 4.0.8 resolve: 1.22.10 transitivePeerDependencies: @@ -9761,7 +10212,7 @@ snapshots: '@babel/parser': 7.28.3 '@babel/template': 7.27.2 '@babel/types': 7.28.2 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -9773,7 +10224,7 @@ snapshots: '@babel/parser': 7.28.4 '@babel/template': 7.27.2 '@babel/types': 7.28.4 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -11337,11 +11788,73 @@ snapshots: dependencies: '@noble/ciphers': 1.3.0 + '@elastic/ecs-helpers@1.1.0': + dependencies: + fast-json-stringify: 2.7.13 + + '@elastic/ecs-pino-format@1.4.0': + dependencies: + '@elastic/ecs-helpers': 1.1.0 + '@emnapi/runtime@1.5.0': dependencies: tslib: 2.8.1 optional: true + '@envio-dev/hyperfuel-client-darwin-arm64@1.2.2': + optional: true + + '@envio-dev/hyperfuel-client-darwin-x64@1.2.2': + optional: true + + '@envio-dev/hyperfuel-client-linux-arm64-gnu@1.2.2': + optional: true + + '@envio-dev/hyperfuel-client-linux-x64-gnu@1.2.2': + optional: true + + '@envio-dev/hyperfuel-client-linux-x64-musl@1.2.2': + optional: true + + '@envio-dev/hyperfuel-client-win32-x64-msvc@1.2.2': + optional: true + + '@envio-dev/hyperfuel-client@1.2.2': + optionalDependencies: + '@envio-dev/hyperfuel-client-darwin-arm64': 1.2.2 + '@envio-dev/hyperfuel-client-darwin-x64': 1.2.2 + '@envio-dev/hyperfuel-client-linux-arm64-gnu': 1.2.2 + '@envio-dev/hyperfuel-client-linux-x64-gnu': 1.2.2 + '@envio-dev/hyperfuel-client-linux-x64-musl': 1.2.2 + '@envio-dev/hyperfuel-client-win32-x64-msvc': 1.2.2 + + '@envio-dev/hypersync-client-darwin-arm64@0.6.6': + optional: true + + '@envio-dev/hypersync-client-darwin-x64@0.6.6': + optional: true + + '@envio-dev/hypersync-client-linux-arm64-gnu@0.6.6': + optional: true + + '@envio-dev/hypersync-client-linux-x64-gnu@0.6.6': + optional: true + + '@envio-dev/hypersync-client-linux-x64-musl@0.6.6': + optional: true + + '@envio-dev/hypersync-client-win32-x64-msvc@0.6.6': + optional: true + + '@envio-dev/hypersync-client@0.6.6': + optionalDependencies: + '@envio-dev/hypersync-client-darwin-arm64': 0.6.6 + '@envio-dev/hypersync-client-darwin-x64': 0.6.6 + '@envio-dev/hypersync-client-linux-arm64-gnu': 0.6.6 + '@envio-dev/hypersync-client-linux-x64-gnu': 0.6.6 + '@envio-dev/hypersync-client-linux-x64-musl': 0.6.6 + '@envio-dev/hypersync-client-win32-x64-msvc': 0.6.6 + '@esbuild/aix-ppc64@0.25.9': optional: true @@ -11430,7 +11943,7 @@ snapshots: '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -11444,7 +11957,7 @@ snapshots: '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -11955,7 +12468,7 @@ snapshots: bufferutil: 4.0.9 cross-fetch: 4.1.0(encoding@0.1.13) date-fns: 2.30.0 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 eciesjs: 0.4.15 eventemitter2: 6.4.9 readable-stream: 3.6.2 @@ -11971,7 +12484,7 @@ snapshots: bufferutil: 4.0.9 cross-fetch: 4.1.0(encoding@0.1.13) date-fns: 2.30.0 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 eciesjs: 0.4.15 eventemitter2: 6.4.9 readable-stream: 3.6.2 @@ -11999,7 +12512,7 @@ snapshots: '@paulmillr/qr': 0.2.1 bowser: 2.12.1 cross-fetch: 4.1.0(encoding@0.1.13) - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 eciesjs: 0.4.15 eth-rpc-errors: 4.0.3 eventemitter2: 6.4.9 @@ -12027,7 +12540,7 @@ snapshots: '@paulmillr/qr': 0.2.1 bowser: 2.12.1 cross-fetch: 4.1.0(encoding@0.1.13) - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 eciesjs: 0.4.15 eth-rpc-errors: 4.0.3 eventemitter2: 6.4.9 @@ -12054,7 +12567,7 @@ snapshots: '@scure/base': 1.2.6 '@types/debug': 4.1.12 '@types/lodash': 4.17.20 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 lodash: 4.17.21 pony-cause: 2.1.11 semver: 7.7.2 @@ -12066,7 +12579,7 @@ snapshots: dependencies: '@ethereumjs/tx': 4.2.0 '@types/debug': 4.1.12 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 semver: 7.7.2 superstruct: 1.0.4 transitivePeerDependencies: @@ -12079,7 +12592,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@types/debug': 4.1.12 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 pony-cause: 2.1.11 semver: 7.7.2 uuid: 9.0.1 @@ -12093,7 +12606,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@types/debug': 4.1.12 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 pony-cause: 2.1.11 semver: 7.7.2 uuid: 9.0.1 @@ -12141,6 +12654,10 @@ snapshots: dependencies: '@noble/hashes': 1.3.2 + '@noble/curves@1.4.0': + dependencies: + '@noble/hashes': 1.4.0 + '@noble/curves@1.4.2': dependencies: '@noble/hashes': 1.4.0 @@ -12238,7 +12755,7 @@ snapshots: '@nomicfoundation/hardhat-ethers@3.1.0(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.9.2))(typescript@5.9.2)(utf-8-validate@5.0.10))': dependencies: - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) hardhat: 2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.9.2))(typescript@5.9.2)(utf-8-validate@5.0.10) lodash.isequal: 4.5.0 @@ -12259,7 +12776,7 @@ snapshots: '@nomicfoundation/ignition-core': 0.15.13(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@nomicfoundation/ignition-ui': 0.15.12 chalk: 4.1.2 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 fs-extra: 10.1.0 hardhat: 2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.9.2))(typescript@5.9.2)(utf-8-validate@5.0.10) json5: 2.2.3 @@ -12300,7 +12817,7 @@ snapshots: '@ethersproject/abi': 5.8.0 '@ethersproject/address': 5.8.0 cbor: 8.1.0 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 hardhat: 2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.9.2))(typescript@5.9.2)(utf-8-validate@5.0.10) lodash.clonedeep: 4.5.0 picocolors: 1.1.1 @@ -12315,7 +12832,7 @@ snapshots: '@ethersproject/address': 5.6.1 '@nomicfoundation/solidity-analyzer': 0.1.2 cbor: 9.0.2 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) fs-extra: 10.1.0 immer: 10.0.2 @@ -12359,6 +12876,8 @@ snapshots: '@nomicfoundation/solidity-analyzer-linux-x64-musl': 0.1.2 '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.2 + '@opentelemetry/api@1.9.0': {} + '@openzeppelin/contracts@4.9.6': {} '@openzeppelin/contracts@5.4.0': {} @@ -12643,6 +13162,11 @@ snapshots: - utf-8-validate - zod + '@rescript/react@0.14.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + '@rolldown/pluginutils@1.0.0-beta.34': {} '@rollup/rollup-android-arm-eabi@4.50.0': @@ -12754,7 +13278,7 @@ snapshots: '@scure/bip32@1.7.0': dependencies: - '@noble/curves': 1.9.1 + '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 @@ -13008,6 +13532,25 @@ snapshots: '@solidity-parser/parser@0.20.2': {} + '@svgdotjs/svg.draggable.js@3.0.6(@svgdotjs/svg.js@3.2.5)': + dependencies: + '@svgdotjs/svg.js': 3.2.5 + + '@svgdotjs/svg.filter.js@3.0.9': + dependencies: + '@svgdotjs/svg.js': 3.2.5 + + '@svgdotjs/svg.js@3.2.5': {} + + '@svgdotjs/svg.resize.js@2.0.5(@svgdotjs/svg.js@3.2.5)(@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.5))': + dependencies: + '@svgdotjs/svg.js': 3.2.5 + '@svgdotjs/svg.select.js': 4.0.3(@svgdotjs/svg.js@3.2.5) + + '@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.5)': + dependencies: + '@svgdotjs/svg.js': 3.2.5 + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.3)': dependencies: '@babel/core': 7.28.3 @@ -13373,6 +13916,9 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/json5@0.0.29': + optional: true + '@types/lodash@4.17.20': {} '@types/mdast@4.0.4': @@ -13389,6 +13935,8 @@ snapshots: '@types/mocha@10.0.10': {} + '@types/mocha@10.0.6': {} + '@types/ms@2.1.0': {} '@types/node-forge@1.3.14': @@ -13399,6 +13947,10 @@ snapshots: '@types/node@17.0.45': {} + '@types/node@20.8.8': + dependencies: + undici-types: 5.25.3 + '@types/node@22.7.5': dependencies: undici-types: 6.19.8 @@ -13518,7 +14070,7 @@ snapshots: '@typescript-eslint/types': 8.42.0 '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.42.0 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 eslint: 9.34.0(jiti@1.21.7) typescript: 5.8.3 transitivePeerDependencies: @@ -13528,7 +14080,7 @@ snapshots: dependencies: '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.8.3) '@typescript-eslint/types': 8.42.0 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -13547,7 +14099,7 @@ snapshots: '@typescript-eslint/types': 8.42.0 '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.8.3) '@typescript-eslint/utils': 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.8.3) - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 eslint: 9.34.0(jiti@1.21.7) ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 @@ -13562,7 +14114,7 @@ snapshots: '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.8.3) '@typescript-eslint/types': 8.42.0 '@typescript-eslint/visitor-keys': 8.42.0 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -14563,8 +15115,15 @@ snapshots: '@xtuc/long@4.2.2': {} + '@yr/monotone-cubic-spline@1.0.3': {} + abbrev@1.0.9: {} + abitype@1.0.5(typescript@5.2.2)(zod@3.25.76): + optionalDependencies: + typescript: 5.2.2 + zod: 3.25.76 + abitype@1.0.8(typescript@5.8.3)(zod@3.25.76): optionalDependencies: typescript: 5.8.3 @@ -14585,6 +15144,10 @@ snapshots: typescript: 5.9.2 zod: 4.0.5 + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -14612,7 +15175,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -14681,6 +15244,8 @@ snapshots: dependencies: string-width: 4.2.3 + ansi-colors@4.1.1: {} + ansi-colors@4.1.3: {} ansi-escapes@4.3.2: @@ -14708,6 +15273,15 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + apexcharts@5.3.6: + dependencies: + '@svgdotjs/svg.draggable.js': 3.0.6(@svgdotjs/svg.js@3.2.5) + '@svgdotjs/svg.filter.js': 3.0.9 + '@svgdotjs/svg.js': 3.2.5 + '@svgdotjs/svg.resize.js': 2.0.5(@svgdotjs/svg.js@3.2.5)(@svgdotjs/svg.select.js@4.0.3(@svgdotjs/svg.js@3.2.5)) + '@svgdotjs/svg.select.js': 4.0.3(@svgdotjs/svg.js@3.2.5) + '@yr/monotone-cubic-spline': 1.0.3 + arg@4.1.3: {} arg@5.0.2: {} @@ -14726,6 +15300,8 @@ snapshots: array-union@2.1.0: {} + arrify@1.0.1: {} + assertion-error@1.1.0: {} astral-regex@2.0.0: {} @@ -14835,6 +15411,8 @@ snapshots: dependencies: bindings: 1.5.0 + bignumber.js@9.1.2: {} + bignumber.js@9.3.1: {} binary-extensions@2.3.0: {} @@ -14843,6 +15421,8 @@ snapshots: dependencies: file-uri-to-path: 1.0.0 + bintrees@1.0.2: {} + blakejs@1.2.1: {} bn.js@4.11.6: {} @@ -15058,6 +15638,16 @@ snapshots: chai: 4.5.0 check-error: 1.0.3 + chai@4.3.10: + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.4 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.1.0 + chai@4.5.0: dependencies: assertion-error: 1.1.0 @@ -15116,6 +15706,18 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 + chokidar@3.5.3: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -15537,6 +16139,8 @@ snapshots: dependencies: '@babel/runtime': 7.28.3 + dateformat@4.6.3: {} + dayjs@1.11.13: {} death@1.1.0: {} @@ -15547,15 +16151,19 @@ snapshots: dependencies: ms: 2.0.0 + debug@4.3.4(supports-color@8.1.1): + dependencies: + ms: 2.1.2 + optionalDependencies: + supports-color: 8.1.1 + debug@4.3.7: dependencies: ms: 2.1.3 - debug@4.4.1(supports-color@8.1.1): + debug@4.4.1: dependencies: ms: 2.1.3 - optionalDependencies: - supports-color: 8.1.1 decamelize@1.2.0: {} @@ -15634,7 +16242,7 @@ snapshots: detect-port@1.6.1: dependencies: address: 1.2.2 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -15642,9 +16250,11 @@ snapshots: dependencies: dequal: 2.0.3 + diff@3.5.0: {} + diff@4.0.2: {} - diff@5.2.0: {} + diff@5.0.0: {} difflib@0.2.4: dependencies: @@ -15804,6 +16414,41 @@ snapshots: env-paths@2.2.1: {} + envio-darwin-arm64@2.32.3: + optional: true + + envio-darwin-x64@2.32.3: + optional: true + + envio-linux-arm64@2.32.3: + optional: true + + envio-linux-x64@2.32.3: + optional: true + + envio@2.32.3(bufferutil@4.0.9)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76): + dependencies: + '@elastic/ecs-pino-format': 1.4.0 + '@envio-dev/hyperfuel-client': 1.2.2 + '@envio-dev/hypersync-client': 0.6.6 + bignumber.js: 9.1.2 + pino: 8.16.1 + pino-pretty: 10.2.3 + prom-client: 15.0.0 + rescript: 11.1.3 + rescript-schema: 9.3.0(rescript@11.1.3) + viem: 2.21.0(bufferutil@4.0.9)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + envio-darwin-arm64: 2.32.3 + envio-darwin-x64: 2.32.3 + envio-linux-arm64: 2.32.3 + envio-linux-x64: 2.32.3 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -15939,7 +16584,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -16124,6 +16769,8 @@ snapshots: '@types/node': 24.3.0 require-like: 0.1.2 + event-target-shim@5.0.1: {} + eventemitter2@6.4.9: {} eventemitter3@4.0.7: {} @@ -16198,6 +16845,8 @@ snapshots: eyes@0.1.8: {} + fast-copy@3.0.2: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.3: @@ -16210,6 +16859,13 @@ snapshots: fast-json-stable-stringify@2.1.0: {} + fast-json-stringify@2.7.13: + dependencies: + ajv: 6.12.6 + deepmerge: 4.3.1 + rfdc: 1.4.1 + string-similarity: 4.0.4 + fast-levenshtein@2.0.6: {} fast-redact@3.5.0: {} @@ -16315,7 +16971,7 @@ snapshots: follow-redirects@1.15.11(debug@4.4.1): optionalDependencies: - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 for-each@0.3.5: dependencies: @@ -16472,6 +17128,15 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + glob@7.2.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -16643,7 +17308,7 @@ snapshots: boxen: 5.1.2 chokidar: 4.0.3 ci-info: 2.0.0 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 enquirer: 2.4.1 env-paths: 2.2.1 ethereum-cryptography: 1.2.0 @@ -16657,7 +17322,7 @@ snapshots: lodash: 4.17.21 micro-eth-signer: 0.14.0 mnemonist: 0.38.5 - mocha: 10.8.2 + mocha: 10.2.0 p-map: 4.0.0 picocolors: 1.1.1 raw-body: 2.5.2 @@ -16814,6 +17479,11 @@ snapshots: heap@0.2.7: {} + help-me@4.2.0: + dependencies: + glob: 8.1.0 + readable-stream: 3.6.2 + history@4.10.1: dependencies: '@babel/runtime': 7.28.3 @@ -16953,7 +17623,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -17159,6 +17829,10 @@ snapshots: dependencies: ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + isows@1.0.4(ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + isows@1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -17227,6 +17901,8 @@ snapshots: '@sideway/formula': 3.0.1 '@sideway/pinpoint': 2.0.0 + joycon@3.1.1: {} + js-sha3@0.8.0: {} js-tokens@4.0.0: {} @@ -17263,6 +17939,11 @@ snapshots: json-stringify-safe@5.0.1: {} + json5@1.0.2: + dependencies: + minimist: 1.2.8 + optional: true + json5@2.2.3: {} jsonfile@4.0.0: @@ -17917,7 +18598,7 @@ snapshots: micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -17979,6 +18660,10 @@ snapshots: dependencies: brace-expansion: 1.1.12 + minimatch@5.0.1: + dependencies: + brace-expansion: 2.0.2 + minimatch@5.1.6: dependencies: brace-expansion: 2.0.2 @@ -18005,33 +18690,36 @@ snapshots: dependencies: obliterator: 2.0.5 - mocha@10.8.2: + mocha@10.2.0: dependencies: - ansi-colors: 4.1.3 + ansi-colors: 4.1.1 browser-stdout: 1.3.1 - chokidar: 3.6.0 - debug: 4.4.1(supports-color@8.1.1) - diff: 5.2.0 + chokidar: 3.5.3 + debug: 4.3.4(supports-color@8.1.1) + diff: 5.0.0 escape-string-regexp: 4.0.0 find-up: 5.0.0 - glob: 8.1.0 + glob: 7.2.0 he: 1.2.0 js-yaml: 4.1.0 log-symbols: 4.1.0 - minimatch: 5.1.6 + minimatch: 5.0.1 ms: 2.1.3 - serialize-javascript: 6.0.2 + nanoid: 3.3.3 + serialize-javascript: 6.0.0 strip-json-comments: 3.1.1 supports-color: 8.1.1 - workerpool: 6.5.1 + workerpool: 6.2.1 yargs: 16.2.0 - yargs-parser: 20.2.9 + yargs-parser: 20.2.4 yargs-unparser: 2.0.0 mrmime@2.0.1: {} ms@2.0.0: {} + ms@2.1.2: {} + ms@2.1.3: {} multicast-dns@7.2.5: @@ -18045,6 +18733,8 @@ snapshots: nanoid@3.3.11: {} + nanoid@3.3.3: {} + natural-compare@1.4.0: {} ndjson@2.0.0: @@ -18163,6 +18853,8 @@ snapshots: on-exit-leak-free@0.2.0: {} + on-exit-leak-free@2.1.2: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -18472,8 +19164,37 @@ snapshots: duplexify: 4.1.3 split2: 4.2.0 + pino-abstract-transport@1.1.0: + dependencies: + readable-stream: 4.7.0 + split2: 4.2.0 + + pino-abstract-transport@1.2.0: + dependencies: + readable-stream: 4.7.0 + split2: 4.2.0 + + pino-pretty@10.2.3: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 3.0.2 + fast-safe-stringify: 2.1.1 + help-me: 4.2.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.2.0 + pump: 3.0.3 + readable-stream: 4.7.0 + secure-json-parse: 2.7.0 + sonic-boom: 3.8.1 + strip-json-comments: 3.1.1 + pino-std-serializers@4.0.0: {} + pino-std-serializers@6.2.2: {} + pino@7.11.0: dependencies: atomic-sleep: 1.0.0 @@ -18488,6 +19209,20 @@ snapshots: sonic-boom: 2.8.0 thread-stream: 0.15.2 + pino@8.16.1: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 1.1.0 + pino-std-serializers: 6.2.2 + process-warning: 2.3.2 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 3.8.1 + thread-stream: 2.7.0 + pkg-dir@7.0.0: dependencies: find-up: 6.3.0 @@ -18966,6 +19701,15 @@ snapshots: process-warning@1.0.0: {} + process-warning@2.3.2: {} + + process@0.11.10: {} + + prom-client@15.0.0: + dependencies: + '@opentelemetry/api': 1.9.0 + tdigest: 0.1.2 + prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -19060,6 +19804,12 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-apexcharts@1.8.0(apexcharts@5.3.6)(react@19.1.1): + dependencies: + apexcharts: 5.3.6 + prop-types: 15.8.1 + react: 19.1.1 + react-clientside-effect@1.2.8(react@19.1.1): dependencies: '@babel/runtime': 7.28.3 @@ -19175,6 +19925,14 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -19183,6 +19941,8 @@ snapshots: real-require@0.1.0: {} + real-require@0.2.0: {} + rechoir@0.6.2: dependencies: resolve: 1.22.10 @@ -19354,6 +20114,23 @@ snapshots: requires-port@1.0.0: {} + rescript-envsafe@5.0.0(rescript-schema@9.3.0(rescript@11.1.4))(rescript@11.1.4): + dependencies: + rescript: 11.1.4 + rescript-schema: 9.3.0(rescript@11.1.4) + + rescript-schema@9.3.0(rescript@11.1.3): + optionalDependencies: + rescript: 11.1.3 + + rescript-schema@9.3.0(rescript@11.1.4): + optionalDependencies: + rescript: 11.1.4 + + rescript@11.1.3: {} + + rescript@11.1.4: {} + resolve-alpn@1.2.1: {} resolve-from@4.0.0: {} @@ -19382,6 +20159,8 @@ snapshots: reusify@1.1.0: {} + rfdc@1.4.1: {} + rimraf@3.0.2: dependencies: glob: 7.2.3 @@ -19516,6 +20295,8 @@ snapshots: extend-shallow: 2.0.1 kind-of: 6.0.3 + secure-json-parse@2.7.0: {} + select-hose@2.0.0: {} selfsigned@2.4.1: @@ -19551,6 +20332,10 @@ snapshots: transitivePeerDependencies: - supports-color + serialize-javascript@6.0.0: + dependencies: + randombytes: 2.1.0 + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -19784,7 +20569,7 @@ snapshots: hardhat: 2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.3.0)(typescript@5.9.2))(typescript@5.9.2)(utf-8-validate@5.0.10) jsonschema: 1.5.0 lodash: 4.17.21 - mocha: 10.8.2 + mocha: 10.2.0 node-emoji: 1.11.0 pify: 4.0.1 recursive-readdir: 2.2.3 @@ -19797,6 +20582,10 @@ snapshots: dependencies: atomic-sleep: 1.0.0 + sonic-boom@3.8.1: + dependencies: + atomic-sleep: 1.0.0 + sort-css-media-queries@2.2.0: {} source-map-js@1.2.1: {} @@ -19819,7 +20608,7 @@ snapshots: spdy-transport@3.0.0: dependencies: - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -19830,7 +20619,7 @@ snapshots: spdy@4.0.2: dependencies: - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 @@ -19872,6 +20661,8 @@ snapshots: string-format@2.0.0: {} + string-similarity@4.0.4: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -19913,6 +20704,9 @@ snapshots: strip-bom-string@1.0.0: {} + strip-bom@3.0.0: + optional: true + strip-final-newline@2.0.0: {} strip-hex-prefix@1.0.0: @@ -19988,6 +20782,10 @@ snapshots: tapable@2.2.3: {} + tdigest@0.1.2: + dependencies: + bintrees: 1.0.2 + terser-webpack-plugin@5.3.14(webpack@5.101.3): dependencies: '@jridgewell/trace-mapping': 0.3.30 @@ -20012,6 +20810,10 @@ snapshots: dependencies: real-require: 0.1.0 + thread-stream@2.7.0: + dependencies: + real-require: 0.2.0 + through2@4.0.2: dependencies: readable-stream: 3.6.2 @@ -20076,6 +20878,13 @@ snapshots: dependencies: typescript: 5.9.2 + ts-mocha@10.1.0(mocha@10.2.0): + dependencies: + mocha: 10.2.0 + ts-node: 7.0.1 + optionalDependencies: + tsconfig-paths: 3.15.0 + ts-node@10.9.2(@types/node@24.3.0)(typescript@5.9.2): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -20094,6 +20903,25 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + ts-node@7.0.1: + dependencies: + arrify: 1.0.1 + buffer-from: 1.1.2 + diff: 3.5.0 + make-error: 1.3.6 + minimist: 1.2.8 + mkdirp: 0.5.6 + source-map-support: 0.5.21 + yn: 2.0.0 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + optional: true + tslib@1.14.1: {} tslib@2.4.1: {} @@ -20166,7 +20994,7 @@ snapshots: typechain@8.3.2(typescript@5.9.2): dependencies: '@types/prettier': 2.7.3 - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 fs-extra: 7.0.1 glob: 7.1.7 js-sha3: 0.8.0 @@ -20200,6 +21028,8 @@ snapshots: transitivePeerDependencies: - supports-color + typescript@5.2.2: {} + typescript@5.6.3: {} typescript@5.8.3: {} @@ -20225,6 +21055,8 @@ snapshots: uncrypto@0.1.3: {} + undici-types@5.25.3: {} + undici-types@6.19.8: {} undici-types@7.10.0: {} @@ -20425,6 +21257,24 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 + viem@2.21.0(bufferutil@4.0.9)(typescript@5.2.2)(utf-8-validate@5.0.10)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.10.0 + '@noble/curves': 1.4.0 + '@noble/hashes': 1.4.0 + '@scure/bip32': 1.4.0 + '@scure/bip39': 1.3.0 + abitype: 1.0.5(typescript@5.2.2)(zod@3.25.76) + isows: 1.0.4(ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + webauthn-p256: 0.0.5 + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.2.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + viem@2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.8.1 @@ -20605,6 +21455,11 @@ snapshots: randombytes: 2.1.0 utf8: 3.0.0 + webauthn-p256@0.0.5: + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + webextension-polyfill@0.10.0: {} webidl-conversions@3.0.1: {} @@ -20786,7 +21641,7 @@ snapshots: reduce-flatten: 2.0.0 typical: 5.2.0 - workerpool@6.5.1: {} + workerpool@6.2.1: {} wrap-ansi@6.2.0: dependencies: @@ -20861,6 +21716,8 @@ snapshots: camelcase: 5.3.1 decamelize: 1.2.0 + yargs-parser@20.2.4: {} + yargs-parser@20.2.9: {} yargs-unparser@2.0.0: @@ -20894,6 +21751,8 @@ snapshots: y18n: 5.0.8 yargs-parser: 20.2.9 + yn@2.0.0: {} + yn@3.1.1: {} yocto-queue@0.1.0: {}