Skip to content

Vault Allocation History #52

Description

@rossgalloway

Vault Allocation History producer contract

Implement the Envio producer-side contract required by Kong's planned Vault Allocation History feature.

This specification supersedes the previous issue body. It deliberately separates deployment provenance from active allocator assignment, distinguishes accounting identity from historical completeness, and prevents archive-RPC failures from halting the shared Yearn Envio indexer.

Decisions

  • Extend the existing Envio project. Use the root configuration, schema, generated code, and handler entrypoint. After cutover there is one active deployment and database. Do not create a second permanent Envio server or nested Envio package.
  • Ethereum is the required first production candidate.
  • Sonic (chainId = 146) is explicitly out of scope and does not need to be indexed, audited, configured, or documented as a gap for this feature.
  • Envio supplies replayable events, historical vault accounting, allocator provenance/assignments, and coverage evidence. Kong decides whether those inputs are sufficient to publish a complete timeline.
  • The event scope is actual allocation state, allocator targets, actor inputs, and intentional idle policy. Explaining every allocator execution-eligibility setting is not part of this issue.
  • Persist all machine-key addresses as lowercase 0x strings. Checksum formatting is a presentation concern.
  • Preserve the existing indexer's typed entities and behavior. The allocation entities and handlers are additive. Deployment must use a candidate revision and a rollback plan because full historical replay may still be required.

Objective and ownership boundary

The shared Envio deployment must expose:

  1. Deterministically ordered, normalized allocation-related events.
  2. Top-level transaction envelope metadata.
  3. Immutable debt-allocator deployment bindings.
  4. Time-ordered RoleManager debt-allocator assignments.
  5. Exact block-end totalAssets, totalDebt, and totalIdle checkpoints at accounting-changing blocks.
  6. Machine-readable evidence describing the historical range that has actually been validated.

Kong will consume these primitives to build:

  • Complete per-strategy allocation states and transitions.
  • Same-block grouping and effects[].
  • Actor and intent classification.
  • Strategy/vault metadata enrichment.
  • DOA matching and proposal status.
  • Bps calculations.
  • Redis-cached VaultAllocationTimeline responses.

Envio must not implement Kong's finished timeline, DOA logic, transition classifications, public REST route, or Redis cache.

Do not add per-strategy archive reads. Kong can replay strategy state from events:

  • Current debt: DebtUpdated.new_debt and StrategyReported.current_debt.
  • Strategy lifecycle: StrategyChanged.
  • Last report: StrategyReported.blockTimestamp.
  • Maximum debt: UpdatedMaxDebtForStrategy.
  • Allocator targets: UpdateStrategyDebtRatios.

Single-deployment and failure-domain requirement

Implement allocation history inside the existing Envio project. It uses one:

  • Root Envio configuration and generated-code pass.
  • Root schema and database/Hasura deployment.
  • Root handler entrypoint, with allocation helpers kept in normal source modules.
  • Envio server after cutover.
  • Health/freshness monitoring and deployment runbook.

The allocation feature is Ethereum-only for its first production candidate, even though the shared indexer supports several chains. Allocation handlers and archive calls must therefore have an explicit chain guard. Existing non-allocation entities and handlers must keep their current multichain behavior.

Archive-RPC reads still fail closed for checkpoint data: never write zeroes, partial values, or an unverified checkpoint. However, a final archive-RPC failure must be caught by the allocation handler. It must write a sanitized, queryable failure record, leave the checkpoint absent, keep safeForTimeline = false, and allow the shared indexer to continue. Never store credentials or RPC URLs in the failure record.

Envio's crossChain: false setting remains required because it isolates Effect cache and rate-limit budgets by chain. It does not provide process isolation, so the handler-level failure boundary is mandatory.

Adding persisted event configuration is not compatible with resuming the currently initialized database. The rollout must therefore use fresh candidate storage for the full replay, keep current production available during validation, and leave one active deployment/database after cutover.

Delivery gates

Implement and validate these in order. Do not claim the full issue complete when an earlier gate passes.

  1. Normalized event contract
  2. Allocator provenance and assignment history
  3. Historical accounting checkpoints
  4. Coverage certification, parity, and rollout contract

Each gate must retain focused unit/golden tests and evidence in the final report.


Gate 1: normalized allocation-event contract

Required event scope

V3 vault

  • Deposit
  • Withdraw
  • DebtUpdated
  • StrategyReported
  • StrategyChanged
  • UpdatedMaxDebtForStrategy
  • DebtPurchased
  • UpdateDefaultQueue
  • UpdateUseDefaultQueue
  • UpdateMinimumTotalIdle
  • UpdateAutoAllocate
  • Shutdown
  • RoleSet
  • RoleStatusChanged
  • UpdateRoleManager
  • UpdateAccountant

Deposit and Withdraw are required because idle-only flows change allocation percentages. UpdateMinimumTotalIdle and UpdateAutoAllocate describe intentional idle policy; Shutdown changes future allocation behavior.

RoleManager

  • AddedNewVault
  • RemovedVault
  • UpdateDebtAllocator

Debt allocator factory

  • NewDebtAllocator

Support each ABI variant actually deployed on an included chain. Do not assume an event name or topic0 proves that indexed/non-indexed parameter layout is identical.

Generic debt allocator

  • UpdateStrategyDebtRatios
  • UpdateKeeper
  • GovernanceTransferred

Operational feasibility settings such as minimum change/wait, maximum acceptable base fee, maximum accepted update loss, pause state, and manager configuration are non-goals for the first contract. Add them only in a separately approved expansion.

Entity contract

Add an entity equivalent to:

type AllocationSourceEvent
  @index(fields: ["chainId", "vaultAddress", "blockNumber", "transactionIndex", "logIndex", "id"]) {
  id: ID!
  chainId: Int! @index
  vaultAddress: String! @index
  sourceAddress: String! @index
  sourceType: String! @index
  eventName: String! @index
  signature: String!
  normalizationVersion: Int!
  abiVariant: String
  blockNumber: Int! @index
  blockTimestamp: BigInt! @index
  blockHash: String!
  transactionHash: String! @index
  transactionIndex: Int!
  logIndex: Int!
  topLevelTransactionFrom: String @index
  topLevelTransactionTo: String @index
  topLevelInputSelector: String
  strategyAddress: String @index
  argsJson: String!
}

Allowed sourceType values:

  • vault
  • roleManager
  • debtAllocatorFactory
  • debtAllocator

Requirements:

  • ID: ${chainId}:${transactionHashLower}:${logIndex}.
  • signature is the lowercase event topic0.
  • Addresses, hashes, selectors, bytes, and topic0 use lowercase 0x encoding.
  • topLevelInputSelector is exactly the first four calldata bytes (0x plus eight hex characters), otherwise null.
  • These transaction fields describe only the top-level envelope. They do not claim to identify internal Safe signers, nested callers, final internal targets, or nested function selectors. Traces and nested calldata decoding are not supplied by this issue.
  • Fetch to and input only for the allocation project's event set; do not expand the primary indexer's global transaction payload.
  • Use normalizationVersion = 1 initially.
  • abiVariant identifies the audited event layout/release/code-hash family when variants exist; it is null only when there is exactly one proven layout.

The normalized argsJson keys for normalization version 1 are fixed as follows. Key order is exactly the order shown:

Source Event Ordered JSON keys
vault Deposit sender, owner, assets, shares
vault Withdraw sender, receiver, owner, assets, shares
vault DebtUpdated strategy, currentDebt, newDebt
vault StrategyReported strategy, gain, loss, currentDebt, protocolFees, totalFees, totalRefunds
vault StrategyChanged strategy, changeType
vault UpdatedMaxDebtForStrategy sender, strategy, newDebt
vault DebtPurchased strategy, amount
vault UpdateDefaultQueue newDefaultQueue
vault UpdateUseDefaultQueue useDefaultQueue
vault UpdateMinimumTotalIdle minimumTotalIdle
vault UpdateAutoAllocate autoAllocate
vault Shutdown no keys; serialize as {}
vault RoleSet account, role
vault RoleStatusChanged role, status
vault UpdateRoleManager roleManager
vault UpdateAccountant accountant
roleManager AddedNewVault vault, debtAllocator, category
roleManager RemovedVault vault
roleManager UpdateDebtAllocator vault, debtAllocator
debtAllocatorFactory NewDebtAllocator allocator, vault, followed by originalAllocator only for a proven ABI variant that emits it
debtAllocator UpdateStrategyDebtRatios strategy, newTargetRatio, newMaxRatio, newTotalDebtRatio
debtAllocator UpdateKeeper keeper, allowed
debtAllocator GovernanceTransferred previousGovernance, newGovernance

All integer-valued fields in this table, including enum-like values such as changeType, role, status, and category, are decimal strings. Address arrays preserve emitted order. ABI variants with different indexed layouts normalize to the same keys; abiVariant identifies which decoder produced them. Do not add or rename keys without incrementing normalizationVersion.

Versioned serializer contract

Do not serialize event.params generically. Define an explicit serializer per supported event and ABI variant. For every serializer, document and golden-test:

  • Exact field names and insertion order.
  • Integers as base-10 strings.
  • Lowercase addresses.
  • Booleans as JSON booleans.
  • Bytes as lowercase 0x strings.
  • Enum representation.
  • Array order.
  • Absent fields versus explicit null.
  • The event signature/topic0 and ABI layout used.

argsJson must be deterministic byte-for-byte for identical input. It must not contain arbitrary RPC responses.

Gate 1 acceptance

  • Every required event/ABI variant has an explicit serializer and golden fixture.
  • Ordering and IDs are deterministic.
  • Top-level transaction metadata is correctly named and extracted.
  • Empty/short calldata yields a null selector.
  • Reprocessing identical input produces identical rows.
  • Existing primary-indexer entities and queries remain unchanged.

Gate 2: allocator provenance and assignment history

NewDebtAllocator is deployment provenance, not complete active-assignment history. Model the facts separately.

Deployment binding

type DebtAllocatorDeployment {
  id: ID! # `${chainId}:${allocatorLower}`
  chainId: Int! @index
  allocatorAddress: String! @index
  vaultAddress: String! @index
  factoryAddress: String! @index
  originalAllocatorAddress: String
  abiVariant: String
  createdBlock: Int!
  createdTimestamp: BigInt!
  createdTransactionHash: String!
  createdEventId: String!
}

Populate this immutable binding from NewDebtAllocator. If an ABI variant does not emit originalAllocatorAddress, leave it null and identify the variant; do not infer it.

RoleManager assignment history

type VaultDebtAllocatorAssignment
  @index(fields: ["chainId", "vaultAddress", "blockNumber", "transactionIndex", "logIndex"]) {
  id: ID! # normalized source event ID
  chainId: Int! @index
  vaultAddress: String! @index
  allocatorAddress: String! @index
  roleManagerAddress: String! @index
  assignmentType: String! # `initial` or `updated`
  implementationRecognition: String! # `knownGenericAllocator`, `other`, or `unknown`
  blockNumber: Int! @index
  blockTimestamp: BigInt!
  blockHash: String!
  transactionHash: String!
  transactionIndex: Int!
  logIndex: Int!
  sourceEventId: String!
}
  • AddedNewVault creates the initial assignment.
  • UpdateDebtAllocator creates later assignments.
  • An assigned address is a RoleManager fact; it is not automatically a recognized GenericDebtAllocator deployment.
  • RemovedVault closes RoleManager membership but must not delete historical assignments.

If a recognized factory deployment binding contradicts a RoleManager assignment, surface the disagreement in a queryable conflict entity or explicit assignment status. Never overwrite one fact with the other.

Dynamic-registration same-block requirement

Envio may retrospectively return logs earlier in the same block from a dynamically registered contract. Before using immediate allocator-to-vault lookup with hard failure:

  1. Audit every supported allocator factory/implementation ABI variant.
  2. Add a fixture proving no required allocator event can precede NewDebtAllocator in the registration block.

If that invariant cannot be proven, implement deterministic block-level/deferred association. In either case:

  • Never silently drop allocator events.
  • Never use a zero-address vault.
  • Never misclassify an arbitrary RoleManager assignee as a known GenericDebtAllocator.
  • Persist/query an explicit unresolved or conflict state if association cannot be completed safely.

Gate 2 acceptance

  • Initial and updated RoleManager assignments replay correctly.
  • Deployment binding and active assignment remain independently queryable.
  • Known allocator events resolve to the correct vault.
  • Unknown role holders remain distinguishable from known allocator implementations.
  • Conflicts and unresolved associations are visible.
  • Same-block registration behavior is proven by fixtures or handled through deferred reconciliation.

Gate 3: historical vault accounting checkpoints

Checkpoint contract

type VaultAccountingCheckpoint
  @index(fields: ["chainId", "vaultAddress", "blockNumber"]) {
  id: ID! # `${chainId}:${vaultLower}:${blockNumber}`
  chainId: Int! @index
  vaultAddress: String! @index
  blockNumber: Int! @index
  blockTimestamp: BigInt! @index
  blockHash: String!
  totalAssets: BigInt!
  totalDebt: BigInt!
  totalIdle: BigInt!
  accountingIdentityHolds: Boolean!
  canonicalBlockVerified: Boolean!
  source: String! # `archive-rpc-effect`
  sourceEventIds: [String!]!
}

Also expose unresolved and resolved archive-read failures:

type VaultAccountingCheckpointFailure
  @index(fields: ["chainId", "vaultAddress", "blockNumber", "resolved"]) {
  id: ID! # `${chainId}:${vaultLower}:${blockNumber}`
  chainId: Int! @index
  vaultAddress: String! @index
  blockNumber: Int! @index
  blockTimestamp: BigInt!
  expectedBlockHash: String!
  reason: String!
  sourceEventIds: [String!]!
  resolved: Boolean! @index
  resolvedCheckpointId: String
}

The reason must be a sanitized category, not a raw provider message. If a later replay or same-block trigger succeeds, mark the failure resolved and point it to the persisted checkpoint.

Do not use accountingComplete. For canonical VaultV3, totalAssets == totalDebt + totalIdle is an accounting identity/RPC-consistency check, not proof of event-history completeness.

Persist a checkpoint only when all three reads succeed and the provider's canonical block is verified. A persisted row should therefore have canonicalBlockVerified = true; retain the field so consumers can assert the contract explicitly. Persist the actual identity result without clamping or reconciliation.

Trigger invariant

Refresh the block-end checkpoint after:

  • Deposit
  • Withdraw
  • DebtUpdated
  • StrategyReported

Multiple trigger events in one block must merge into the same checkpoint. sourceEventIds contains all triggering normalized event IDs, ordered by (transactionIndex, logIndex, id) and deduplicated.

This trigger set is valid only if every successful code path that mutates total_idle or total_debt emits at least one trigger event. Audit this invariant for every supported vault release/code hash and commit the audit evidence. Include companion-event cases such as debt purchase and forced strategy revocation. A new release is not covered until this mutation audit passes.

Do not synthesize or seed a checkpoint before the certified coverage start. Before that block, history is unavailable.

Archive-RPC Effect

Use Envio createEffect and viem to read at the event block:

  • totalAssets()
  • totalDebt()
  • totalIdle()

Effect input/cache identity must include:

  • Chain ID through crossChain: false.
  • Lowercase vault address.
  • Block number.
  • Expected Envio block hash.

Required Effect settings and behavior:

  • cache: true.
  • crossChain: false is mandatory.
  • Enable viem transport batching where supported.
  • Initial per-chain rate limit: 5 Effect executions per second.
  • Per-attempt timeout: 20 seconds.
  • Maximum attempts: 3 total.
  • Retry only transient transport, timeout, and provider-rate-limit errors.
  • Backoff: exponential base 500 ms, factor 2, maximum 2 seconds, with full jitter.
  • Do not retry deterministic contract reverts, unsupported historical-state errors, or canonical-hash mismatches.
  • On final failure the Effect sets context.cache = false and throws to its handler. The handler catches that failure, persists or updates VaultAccountingCheckpointFailure, writes no checkpoint, and continues the shared indexer. Never return zeroes, partial values, or a false-success checkpoint.

Benchmark Ethereum before full replay. Record observed request rate, error rate, replay throughput, and projected duration. If measured provider limits require changing the constants above, update the constants, tests, and runbook together before continuing; do not leave runtime behavior undocumented.

Canonical-state verification

Putting a block hash in the cache key prevents cross-fork cache reuse but does not prove an eth_call by block number used that hash.

For every Effect execution:

  1. Read the archive provider's block hash for blockNumber and compare it with the Envio event block hash.
  2. Prefer EIP-1898/block-hash-pinned state calls when supported by the provider/client.
  3. Otherwise perform the three reads at blockNumber, then fetch/compare the provider block hash again.
  4. If either comparison fails or the hash changes around the reads, set context.cache = false and throw.

Do not cache a result whose canonical association was not proven.

Archive configuration

Use dedicated allocation-history archive variables in the shared project; do not assume the indexer's head/watchdog RPC is archival. Suggested names:

ENVIO_ALLOCATION_ARCHIVE_RPC_URL_ETHEREUM
ENVIO_ALLOCATION_ARCHIVE_RPC_URL_OPTIMISM
ENVIO_ALLOCATION_ARCHIVE_RPC_URL_GNOSIS
ENVIO_ALLOCATION_ARCHIVE_RPC_URL_BASE
ENVIO_ALLOCATION_ARCHIVE_RPC_URL_ARBITRUM
ENVIO_ALLOCATION_ARCHIVE_RPC_URL_POLYGON
ENVIO_ALLOCATION_ARCHIVE_RPC_URL_BERACHAIN
ENVIO_ALLOCATION_ARCHIVE_RPC_URL_KATANA

Only require variables for chains actually approved for allocation history. Do not add a Sonic variable. Missing allocation archive configuration must not affect unrelated chains or handlers. Update example configuration and documentation without credentials. Missing required configuration must produce a chain-specific error without logging the URL.

Gate 3 acceptance

  • Same-block triggers merge deterministically.
  • Reads are performed at the exact historical block.
  • Cache identity includes vault, block number, expected hash, and per-chain scope.
  • Provider hash mismatch before or after reads prevents persistence and caching.
  • Unsupported historical state and final RPC failures emit no checkpoint.
  • Accounting mismatch remains visible through accountingIdentityHolds = false.
  • The supported vault-release mutation audit proves the trigger invariant.
  • A final archive-RPC failure creates an unresolved sanitized failure record, emits no checkpoint, and does not halt the shared Envio indexer. A later successful replay resolves that record.

Gate 4: coverage, query contract, parity, and rollout

Chain scope

Ethereum is required first. Additional chains may be added only when they are both:

  1. Explicitly enabled for allocation-history handlers and archive reads in the shared deployment; and
  2. Approved as required by Kong's allocation-history product.

Potential follow-on chains currently present in the shared configuration include Optimism, Gnosis, Base, Arbitrum, Polygon, Berachain, and Katana. Their presence in the shared config does not enable or certify allocation-history coverage.

Sonic is out of scope. Do not enable allocation-history handlers or archive reads for chain 146, and do not treat its absence as an acceptance blocker.

Machine-readable coverage

Expose a queryable entity equivalent to:

type VaultAllocationCoverage {
  id: ID! # `${coverageRevision}:${chainId}:${vaultLower}`
  chainId: Int! @index
  vaultAddress: String! @index
  coverageStartBlock: Int!
  coverageStartBlockHash: String!
  validatedThroughBlock: Int!
  validatedThroughBlockHash: String!
  vaultDiscoveryComplete: Boolean!
  eventHistoryComplete: Boolean!
  allocatorDeploymentHistoryComplete: Boolean!
  allocatorAssignmentHistoryComplete: Boolean!
  checkpointTriggerAuditComplete: Boolean!
  safeForTimeline: Boolean!
  knownGapsJson: String!
  coverageRevision: String!
  producerCommit: String!
  validatedAt: BigInt!
}

Back this entity with a versioned, checked-in coverage manifest and deterministic publication mechanism. safeForTimeline defaults to false. Set it true only when every required fact is proven for the stated block range. A checkpoint identity match alone must never change it. Any unresolved VaultAccountingCheckpointFailure inside the claimed range prevents certification.

For each included chain/vault, the coverage evidence must pin:

  • As-of block number and hash.
  • Authoritative registry/factory/RoleManager discovery sources.
  • Every historical Yearn V3 vault in scope, not only currently active vaults.
  • Vault deployment block and Envio discovery block.
  • First required indexed event.
  • Allocator deployment/registration and assignment-history start blocks.
  • Vault release/API version or code hash.
  • Earliest safe timeline block.
  • Explicit exclusions and reasons.
  • Producer commit, coverage revision, and validation timestamp.

Also generate a human-readable Markdown matrix from the same manifest. The JSON/entity is authoritative; the Markdown file must not drift independently.

Incremental GraphQL query contract

Document and test the exact Hasura query Kong will use.

Scope:

  • chainId exactly matches the requested chain.
  • vaultAddress is a lowercase exact match.
  • Cursor belongs to the same chain, vault, and coverageRevision.

Ordering:

blockNumber ASC,
transactionIndex ASC,
logIndex ASC,
id ASC

The cursor is exclusive. Its continuation predicate is equivalent to:

blockNumber > cursor.blockNumber
OR (
  blockNumber = cursor.blockNumber
  AND transactionIndex > cursor.transactionIndex
)
OR (
  blockNumber = cursor.blockNumber
  AND transactionIndex = cursor.transactionIndex
  AND logIndex > cursor.logIndex
)
OR (
  blockNumber = cursor.blockNumber
  AND transactionIndex = cursor.transactionIndex
  AND logIndex = cursor.logIndex
  AND id > cursor.id
)

Cursor payload contract:

{
  "v": 1,
  "chainId": 1,
  "vaultAddress": "0x...",
  "coverageRevision": "...",
  "blockNumber": 123,
  "transactionIndex": 4,
  "logIndex": 17,
  "id": "1:0x...:17"
}

Envio/Hasura queries may use these structured variables. If Kong exposes the cursor outside its internal job, Kong should encode it as an opaque versioned value.

  • Default page size: 500.
  • Maximum page size: 2,000.
  • A cursor from another chain, vault, coverage revision, or malformed version is rejected rather than silently restarted.
  • Document the exact query, variables, endpoint/authentication mode, permissions, and error behavior.

For checkpoints, document the exact query for the latest row where (chainId, vaultAddress) match and blockNumber <= targetBlock, ordered by block descending.

Tests and parity

Unit/golden tests are required but do not prove historical coverage. Add both:

  1. Deterministic unit and handler tests using the repository-compatible Envio 3.6 test facilities.
  2. A credentialed read-only parity harness that compares committed Envio fixtures with direct archive reads.

Required focused cases include:

  • Every normalized event serializer and ABI variant.
  • Deterministic IDs, ordering, and cursor continuation at page boundaries inside a transaction/block.
  • Initial and updated allocator assignments.
  • Unknown allocator role holder.
  • Deployment/assignment conflict.
  • Same-block dynamic registration ordering.
  • Multiple checkpoint triggers in one block.
  • Provider hash differs before calls.
  • Provider hash changes after calls.
  • Archive provider has the block but lacks historical state.
  • RPC retry exhaustion produces no checkpoint.
  • Different expected block hash cannot reuse a cached result.
  • Accounting identity mismatch.
  • Full replay determinism.

Committed exact Ethereum fixtures must include:

  • A yvWETH-1 allocation block with multiple strategies.
  • A yvUSDC-1 allocation block containing the historically omitted/outside-optimizer strategy.
  • An UpdateDebtAllocator assignment change, or an explicitly documented proof that no such Ethereum fixture exists in the validated range.
  • A block with multiple relevant events.
  • A deposit/withdraw block that changes idle.
  • A report/debt-reduction block exercising loss-sensitive accounting.

Each fixture pins chain ID, addresses, block number, block hash, transaction/log ordering, expected normalized JSON, exact raw accounting values, and expected assignment/provenance results.

The credentialed parity job must run as a deployment gate or scheduled CI job against the candidate shared-deployment revision. Local runs without credentials may skip cleanly, but the report must say not run, not passed. Never print credentials or RPC URLs.

Shared-deployment rollout

  1. Deploy a candidate revision of the existing Envio project with fresh candidate storage while the current production revision and database remain available.
  2. Replay Ethereum allocation history in that candidate using a dedicated archive RPC.
  3. Keep safeForTimeline = false during replay and validation.
  4. Verify that archive failures create gap records without stopping unrelated indexing.
  5. Run yvWETH/yvUSDC exact parity and cursor tests.
  6. Compare full replay with a fresh incremental continuation.
  7. Measure Effect calls, errors, unresolved gaps, replay duration, database/cache growth, and GraphQL latency.
  8. Certify the Ethereum coverage manifest and only then set eligible vaults safeForTimeline = true in a new immutable coverage revision.
  9. Allow Kong to integrate only against that certified revision.
  10. Cut over to the candidate so that one shared Envio server remains active. Retain the previous deployment revision as the rollback target.
  11. Add another approved chain only by repeating its discovery, ABI, mutation, archive, fixture, and parity audits.

This rollout must not create a second permanent Envio project or database. A temporary candidate revision and fresh candidate storage are allowed for safe replay and validation.

Gate 4 acceptance

  • Ethereum coverage is machine-readable and evidence-backed.
  • safeForTimeline remains false for incomplete or unaudited vault ranges and for any range with unresolved archive-read failures.
  • Exact event/checkpoint queries are documented and tested.
  • Credentialed yvWETH/yvUSDC parity passes against the deployed candidate.
  • Full and incremental ingestion produce equivalent results for the same cutoff.
  • Failure/recovery and backout procedures are documented.
  • Any additional chain has separately passed the same gate.

Global non-goals

Do not implement:

  • Sonic/chain 146 allocation indexing.
  • VaultAllocationTimeline.
  • Allocation transitions or effects[] grouping.
  • Actor-role classification beyond providing top-level inputs.
  • Traces or nested Safe/router/multicall decoding.
  • DOA ingestion, matching, or aging.
  • Redis blobs or Kong REST routes.
  • Strategy names/metadata.
  • Bps or synthetic Unallocated strategies.
  • Uniform timeseries sampling.
  • Per-strategy historical RPC reads.
  • Full allocator execution-feasibility/configuration history.
  • yearn.fi or Powerglove changes.
  • Production cutover to Kong as part of this implementation issue.

Verification and final report

Run and report for the shared deployable project:

  • Envio code generation.
  • TypeScript build/typecheck.
  • Config compatibility checks.
  • Full existing test suite.
  • Focused allocation tests.
  • Lint/format checks.
  • git diff --check.
  • Ethereum replay benchmark.
  • Credentialed deployed-candidate parity status.

The final report must distinguish:

  • Implemented locally.
  • Unit/golden verified.
  • Credentialed parity verified.
  • Deployed but not certified.
  • Certified safeForTimeline coverage.
  • Remaining gaps and owner decisions.

Do not report the feature complete solely because schema generation, build, mock tests, or an HTTP 200 succeeds.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions