# Introduction Source: https://docs.superform.xyz/build Build on Superform: write hooks or join the validator network. The Build section is for developers extending the Superform protocol. You can write permissionless hooks to compose new multi-chain strategies or join the validator network to earn protocol rewards. Build lightweight, composable contracts that perform specific actions — bridge, swap, lend — and chain them together for complex multi-chain strategies. Stake \$UP, attest to price-per-share updates, and earn protocol rewards as part of the decentralized validator network. Ready to join? [Apply to become a validator](https://docs.google.com/forms/d/e/1FAIpQLSc63kVcrBLYYs2dtl16ZMABjNy5lRNBrHHcfOS6RUiARYU-fg/viewform?usp=send_form\&pli=1\&authuser=0). # Configuration Reference Source: https://docs.superform.xyz/build/become-a-validator/configuration-reference Reference for config.toml, chains.yaml, and OCR2 timing presets used by Superform validator nodes. Use this as the technical reference for validator configuration. For setup flow, use [Node Setup](/build/become-a-validator/node-setup). For troubleshooting and operator procedures, use [Operations](/build/become-a-validator/operations). For health endpoints and alerts, use [Monitoring](/build/become-a-validator/monitoring). ## config.toml Copy `config.template.toml` to `config.toml` and fill in your values. ### `[node]` ```toml theme={null} [node] node_id = "oracle-1" # Unique identifier used in logs p2p_listen_addr = "0.0.0.0" # Address to listen on for P2P connections p2p_port = 6690 # TCP port for ragep2p ``` ### `[keys]` Choose one provisioning pattern. **Option 1 — direct hex** ```toml theme={null} [keys] offchain_private_key = "76918f97bfe8..." [keys.onchain_private_key] private_key = "ed6ca3d3ac17..." ``` Use this when you want explicit control over the key material. The offchain key is Ed25519 hex, while the dev/testing onchain key is a secp256k1 private key. The config encryption key is derived automatically from your offchain key. Do not try to configure it separately in `config.toml`. **Option 2 — key file** ```toml theme={null} [keys] key_file_path = "./keystore/ocr2_.json" ``` Use this when you want the node to read the OCR2/offchain bundle from disk. The companion P2P identity must live beside it as `./keystore/p2p_.json`, so mount the whole `./keystore` directory into the container if you run Docker. Keep both files at `0600` permissions. **Option 3 — AWS KMS for the onchain key** ```toml theme={null} [keys] offchain_private_key = "76918f97bfe8..." [keys.onchain_private_key] kms_key_id = "arn:aws:kms:us-east-1:123456789:key/abcd-1234" ``` Use this when you want the offchain/OCR2 identity managed separately from the production onchain signer. Supported KMS identifiers: full ARN, alias, or UUID. The KMS key must be an asymmetric signing key of type `ECC_SECG_P256K1`. In production, authorize only the validator runtime role to call `kms:Sign`. Keep admin principals able to manage the key, but do not grant them signing rights unless they also operate the validator. This keeps operational administration separate from report-signing authority. ### `[p2p]` ```toml theme={null} [p2p] bootstrap_peers = [ "12D3KooWGc9hJKBWks3oq9e7H4oZseoncbrxcZQfkW5NTBH473un@bootstrap-1.supervaults.superform.xyz:6690", "12D3KooWCDXcCoE86B4dfMg5hXpoPr6xj4wGRaYqxHbkgPZW3yer@bootstrap-2.supervaults.superform.xyz:6690", ] # announce_addresses = ["203.0.113.42:6690"] ``` Set `announce_addresses` if your node listens on one address but other validators must dial a different public endpoint. Do not front the P2P port with Cloudflare or any HTTP proxy. ragep2p expects direct TCP reachability. ### `[database]` ```toml theme={null} [database] url = "postgresql://oracle:password@localhost:5432/oracle_db?sslmode=disable" max_connections = 10 max_idle_conns = 5 conn_max_lifetime = 3600 ``` ### `[monitoring]` ```toml theme={null} [monitoring] metrics_port = 9090 health_port = 8080 log_level = "info" log_format = "json" health_check_interval = "1h" max_pps_deviation_ppb = 10000000 ``` ### `[ocr2]` ```toml theme={null} [ocr2] config_version = 3 # config_sync_chain_id = 1 ``` `config_sync_chain_id` defaults to Ethereum mainnet (`1`). This is the chain where `SuperGovernor` emits validator config updates, so every node must watch the same source chain. All nodes must use the same `config_version`. A mismatch produces a different config digest and prevents consensus. ### `chain_config_path` ```toml theme={null} chain_config_path = "./chains.yaml" ``` *** ## chains.yaml `chains.yaml` maps chain ID to the chain configuration your node should observe. ```yaml theme={null} 1: name: ethereum chain_id: 1 https_rpc: "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY" wss_rpc: "wss://eth-mainnet.g.alchemy.com/v2/YOUR_KEY" block_time: 12s supported_strategies: - "0xVault1Address" - "0xVault2Address" ecdsa_oracle_address: "0x..." super_governor_address: "0x..." ``` ### Field reference | Field | Type | Description | | ------------------------ | --------- | ------------------------------------------------------------- | | `name` | string | Human-readable chain label used in logs and metrics. | | `chain_id` | integer | EVM chain ID; must match the top-level map key. | | `https_rpc` | string | HTTPS RPC endpoint for reads and tx submission. | | `wss_rpc` | string | WebSocket RPC endpoint for event subscriptions. | | `block_time` | duration | Expected block time in Go duration format. | | `supported_strategies` | string\[] | Vault addresses this node will observe. | | `ecdsa_oracle_address` | string | `ECDSAPPSOracle` contract address supplied during onboarding. | | `super_governor_address` | string | `SuperGovernor` contract address supplied during onboarding. | ### RPC requirements * HTTPS is required on every configured chain. * WSS is required for event subscriptions. * Public rate-limited endpoints are not suitable for production. ### Scope rules Only include chains that Superform assigned to your validator deployment and for which you have reliable RPC access. A broken chain config creates observation errors for every strategy on that chain. Typical supported deployments include Ethereum, Base, Arbitrum, Optimism, and Polygon, but only configure the chains Superform assigned to your validator. *** ## OCR2 timing presets The versioned `config/vN.json` files define the OCR2 timing profile. Every validator in the network must use the same version. ### v2 — high-frequency testing | Parameter | Value | | -------------------- | ----- | | `delta_round` | 2s | | `delta_progress` | 120s | | `delta_resend` | 30s | | `delta_grace` | 5s | | `delta_stage` | 10s | | `r_max` | 3 | | `max_stale_duration` | 5m | ### v3 — hourly production default | Parameter | Value | | -------------------- | ----- | | `delta_round` | 30m | | `delta_progress` | 10m | | `delta_resend` | 5m | | `delta_grace` | 1m | | `delta_stage` | 2m | | `r_max` | 5 | | `max_stale_duration` | 5m | ### Freshness controls | Layer | Config location | Purpose | | -------------------- | ----------------------- | ------------------------------------------ | | Round cadence | `delta_round` | How often the protocol attempts a report | | Report freshness | `max_stale_duration` | Rejects reports that finalize too late | | Node health alerting | `health_check_interval` | Warns when no onchain confirmation arrives | | Contract enforcement | `maxStaleness` | Auto-pauses a strategy if updates stop | For the production `v3` profile, that contract-side `maxStaleness` window is designed around a 24-hour safety boundary even though rounds attempt far more frequently. ### Switching versions Update `config_version` on all nodes and restart them in a coordinated window. Mixed versions split the network until every validator converges on the same config digest. # Economics Source: https://docs.superform.xyz/build/become-a-validator/economics Validator rewards, staking, slashing, insurance, and rollout phases for the Superform validator network. Validator economics are not final yet. Phase 1 is permissioned and does not include the full staking and slashing model described here. This page covers the validator incentive model and rollout path. For protocol mechanics, use [How It Works](/build/become-a-validator/how-it-works). ## Why validators get paid Validators provide the PPS updates SuperVaults need to function safely. If updates stop or become dishonest, vault operations degrade quickly. Rewards exist to keep high-quality operators online and aligned with the network. ## Rewards Vault curators pay upkeep fees in **\$UP** for active oracle coverage. Those fees are distributed to participating validators. The intended model is: * more stake increases reward weight * better participation preserves reward eligibility * missed rounds reduce your effective payout Exact thresholds and rates are governance parameters and have not been published yet. ## Staking Phase 2 is expected to require validators to bond **\$UP**. That bond is intended to: * serve as collateral for slashing * weight participation and rewards * enforce a minimum commitment to stay active ## Slashing Slashing is designed to penalize two categories of failure: * **misreporting** — signing materially incorrect or invalid PPS data * **insufficient participation** — staying below required uptime / contribution thresholds Exact slash sizes and observation windows are still pending. ## Insurance fund A portion of validator economics is expected to seed an insurance fund intended to backstop depositors if oracle failures cause verified harm. It is not meant to cover: * unrelated smart contract bugs * strategy market losses * immaterial losses below a governance-defined threshold ## Rollout phases | Phase | Model | Status | | ------- | ---------------------------------------------- | ------- | | Phase 1 | Permissioned validator set | Live | | Phase 2 | Open bonding, slashing, insurance fund seeding | Planned | | Phase 3 | Governance-managed validator rotation | Planned | ## Practical takeaway for operators Treat the current program as operationally serious even before the full economic model is live. Phase 1 may be permissioned, but uptime, correctness, and incident response still matter because the vaults depend on your node behaving like production infrastructure. # How It Works Source: https://docs.superform.xyz/build/become-a-validator/how-it-works How the Superform validator network reaches OCR2 consensus and what your node does in each round. This page explains the protocol. It does not repeat setup or runbook instructions. For the rollout model and validator incentives, use [Economics](/build/become-a-validator/economics). ## The problem validators solve SuperVaults rely on price-per-share (PPS) updates to value positions correctly. A single trusted updater would be a central point of failure. The validator network replaces that with an OCR2 quorum: multiple operators observe PPS independently, agree offchain, and submit one signed report onchain. ## Core components * **Validator nodes** observe PPS values on assigned chains. * **OCR2** coordinates offchain consensus. * **`ECDSAPPSOracle`** accepts quorum-backed PPS reports onchain. * **`SuperGovernor`** is the source of truth for validator membership and network config. ## OCR2 round flow A production `v3` deployment attempts a round every 30 minutes. ```text theme={null} Leader selected │ ├─ Query Leader broadcasts round context ├─ Observation Validators fetch PPS independently ├─ Report Leader aggregates observations ├─ Finalize Leader shares the candidate report ├─ Accept Validators validate freshness and safety ├─ Sign Validators sign the finalized report └─ Transmit Designated transmitter posts onchain ``` An epoch is a group of rounds led by the same leader. After `r_max` rounds, or when the leader stalls for longer than `delta_progress`, OCR2 advances to a new epoch and deterministically rotates leadership. ## What your node does ### Observation Your node calls `pricePerShare()` for each configured strategy, on each configured chain, using the RPC endpoints in `chains.yaml`. That observation is packaged for OCR2 messaging and signed with your offchain identity before it is shared across the validator mesh. ### Report assembly If your node is the round leader, it: 1. filters invalid or missing observations 2. requires enough valid observations to satisfy quorum 3. computes the median PPS per vault 4. builds the ABI-encoded report payload If a vault does not receive enough valid observations, it is dropped from that round instead of poisoning the whole report. ### Acceptance checks Before signing, every validator checks: * report freshness against `max_stale_duration` * optional deviation guard versus its own observation * whether the onchain nonce changed before acceptance ### Transmission The designated transmitter submits the report to `ECDSAPPSOracle.transmit()`. If quorum is valid, the contract updates PPS and emits `PPSValidated`. Onchain verification checks the submitted validator signatures against the registered signer set before the PPS update is accepted. ## Quorum and fault tolerance The network is configured so it can tolerate a bounded number of faulty validators while still producing correct results. For OCR2 this is the Byzantine constraint that matters: with `N` validators and fault tolerance `F`, the config must satisfy `F * 3 < N`. In practice Superform chooses an odd quorum so `F = (quorum - 1) / 2`, then builds the validator set around that limit. | Validators (`N`) | Quorum | Max faulty (`F`) | | :--------------: | :----: | :--------------: | | 4 | 3 | 1 | | 7 | 5 | 2 | | 10 | 7 | 3 | | 13 | 9 | 4 | If fewer than `quorum` validators sign a round, that round does not produce an onchain report. ## How validator identity maps into the config Every validator contributes one aligned identity across three places in the active config: * `validators[i]` is the Ethereum address derived from that validator's onchain public key * `validatorPublicKeys[i]` is the 65-byte uncompressed ECDSA public key used for onchain signature verification * `OracleIdentity[i]` carries the same validator's `validatorPublicKey`, `peerID`, `offchainPublicKey`, and `configEncryptionPublicKey` Those arrays are not loosely related metadata. They are positional invariants. If the same validator is not represented at the same index across all three, config loading breaks. ## What happens when something breaks ### One validator is offline Consensus still proceeds as long as the network remains within its fault-tolerance threshold. ### The leader is offline Other validators wait for `delta_progress`, then a new epoch starts with a new leader. ### Too many validators are unavailable The network cannot reach quorum. Rounds fail, `PPSValidated` stops advancing, monitoring turns stale, and strategies eventually hit onchain `maxStaleness` protections. ## Config digests Every node independently derives a config digest from the active validator set, their keys, quorum, and OCR2 timing parameters. If your digest differs from the rest of the network, the usual causes are: * mismatched `config_version` * observing a different `ValidatorConfigSet` event * running against the wrong deployment inputs That is an operational issue, not a protocol mystery. Fix the inputs and the digest should converge. # Become a Validator Source: https://docs.superform.xyz/build/become-a-validator/index Understand the validator program, operator requirements, and the fastest path to a live Superform validator node. The validator program is currently permissioned. Ready to join? [Apply to become a validator](https://docs.google.com/forms/d/e/1FAIpQLSc63kVcrBLYYs2dtl16ZMABjNy5lRNBrHHcfOS6RUiARYU-fg/viewform?usp=send_form\&pli=1\&authuser=0). Before you start, confirm you have an onboarding contact at Superform and access to the validator artifacts you plan to run (published image or source repository), bootstrap peers, and the chain-specific contract addresses for your assigned deployment. Ready to join? [Apply to become a validator](https://docs.google.com/forms/d/e/1FAIpQLSc63kVcrBLYYs2dtl16ZMABjNy5lRNBrHHcfOS6RUiARYU-fg/viewform?usp=send_form\&pli=1\&authuser=0). ## What this program is The SuperVaults Validator Network is the oracle layer that keeps vault price-per-share (PPS) data fresh and tamper-resistant. Validators run an OCR2-based node that: * observes PPS on each configured chain * signs consensus reports with a dedicated onchain key * helps submit a single quorum-backed update onchain * earns validator rewards for reliable participation ## Who should run a validator This program is designed for operators who can run production infrastructure consistently, manage keys safely, and respond to incidents quickly. It is not a casual self-serve node today. ## Before you touch a server Have these inputs lined up first: * **Program access** — confirmation from Superform that your organization is approved to join * **Runtime artifacts** — access to the published container image or the source repository and templates * **Infrastructure** — a Linux or macOS host, PostgreSQL, stable outbound connectivity, and reliable RPC providers * **Network metadata** — bootstrap peers, the current config version, and the contract addresses Superform wants you to monitor * **Operator contact path** — a place to submit your public key bundle and coordinate registration If any of those are missing, stop there. Most validator setup failures come from missing onboarding inputs, not from the software install itself. ## Read this section in order The shortest safe path from approval to a running node. Canonical install and runtime guide for Docker or source deployments. Config reference for config.toml, chains.yaml, and OCR2 timing presets. Health endpoints, metrics, and alert rules for production operators. Registration, upgrades, key rotation, monitoring checks, and troubleshooting. ## Supporting context OCR2 rounds, quorum, failure modes, and validator config mechanics. Rewards, staking, slashing, insurance, and rollout phases. ## Operator model at a glance | Topic | Current model | | ---------------------- | ---------------------------------------------------------- | | Access | Permissioned onboarding through Superform | | Validator registration | Superform submits `setValidatorConfig()` | | Node reconfiguration | Automatic from `ValidatorConfigSet` events | | Key custody | Operator-managed; onchain key should use KMS in production | | Rewards and slashing | Phase 2 design, not live in Phase 1 | ## Safe order of operations 1. Confirm access and receive the required network metadata. 2. Stand up the node and validate local health. 3. Export and submit your public key bundle. 4. Wait for Superform to register your validator onchain. 5. Verify your node loads the new config and starts participating. 6. Put monitoring and incident response in place before treating the node as production. # Infrastructure Sizing Source: https://docs.superform.xyz/build/become-a-validator/infrastructure Decision guide for sizing compute, database, key custody, and monitoring for a Superform validator node. Use this page to choose the infrastructure tier for a Superform validator. The node is lightweight: it observes SuperVault PPS values, participates in OCR2 consensus, signs reports, and persists round state. It does **not** run an archive node, index chains, or replace your RPC provider. In production, most operator cost comes from RPC endpoints, managed Postgres, monitoring, and signing-key custody rather than the validator binary itself. ## How much infrastructure do validators need? For most approved operators, start with the recommended baseline: * 2 vCPU / 2 GB RAM validator host * static public endpoint for ragep2p on TCP `6690` * managed PostgreSQL 15+ * production RPC provider coverage for every assigned chain * KMS-backed onchain signing where supported * Prometheus-compatible metrics, alerts, and basic logs The validator can run on AWS, GCP, Azure, Hetzner, OVH, or bare metal. Pick the platform you can operate reliably. A cheap VPS with weak backups, no alerting, and a rotating IP is worse than a slightly more expensive managed setup. ## Recommended production baseline Reference production deployment for a single validator: | Component | Recommended baseline | | ----------------- | ---------------------------------------------------------------------------------- | | Compute | 1 validator host, 2 vCPU ARM/x86, 2 GB RAM | | Disk | 16–20 GB encrypted SSD | | Public networking | Static IP or stable DNS name; direct TCP `6690` reachability | | Database | Managed PostgreSQL 15+, single-AZ, 20 GB, TLS, 7-day backups | | Key custody | KMS-backed secp256k1 onchain signer; local or keystore-managed OCR2/P2P identities | | Monitoring | Prometheus metrics, Grafana dashboards/alerts, log retention | | RPC | Paid HTTPS + WSS endpoints for each assigned chain | A reference AWS deployment maps this to one `t4g.small` validator host in `eu-west-1`, a 16 GB encrypted gp3 root disk, one Elastic IP, RDS PostgreSQL 16 on `db.t4g.micro` with TLS and deletion protection, an AWS KMS secp256k1 CMK for the onchain signer, AWS Managed Prometheus/Grafana, and alert routing through SNS or an on-call channel. The validator, optional `snapshotd` IPC sidecar, and metrics collector can run as separate containers on the same host, with localhost-only Prometheus endpoints. This baseline is enough because the node is mostly stateless outside Postgres. OCR2 round state is small and recoverable; RPC quality and network reachability matter more than raw CPU. ## Sizing tiers | Component | Minimum | Recommended | High-Availability | | ------------ | ---------------------------- | ----------------------------- | -------------------------------------------- | | Use case | Testnet, staging, evaluation | Production validator | Operators with SLA requirements | | Node CPU | 2 vCPU | 2 vCPU | 4 vCPU | | Node RAM | 2 GB | 2 GB | 4–8 GB | | Node disk | 20 GB SSD | 16–20 GB encrypted SSD | 20+ GB encrypted SSD | | Architecture | x86\_64 or arm64 | arm64 preferred | arm64 preferred | | Public IP | Dynamic acceptable for tests | Static IP / stable DNS | Static IP / stable DNS with failover plan | | Postgres | Local or self-hosted | Managed single-AZ, 20 GB | Managed multi-AZ, 20–50 GB | | Key custody | Local encrypted keystore | Cloud KMS for onchain key | Cloud KMS or HSM-backed signer | | Metrics | Local Prometheus | Prometheus + Grafana alerts | Managed metrics + paging | | Backups | Manual | 7-day database backups | 7-day backups + cross-region snapshot policy | | Network | Non-residential preferred | Datacenter network, 100 Mbps+ | Datacenter network, 1 Gbps / SLA-backed | Minimum is not a production recommendation. Use it to evaluate the software, not to operate a live validator with meaningful stake or uptime expectations. ## AWS cost estimate Approximate AWS `eu-west-1` on-demand reference pricing for the recommended baseline. These are maintainable planning numbers, not a quote. They exclude Savings Plans, Reserved Instances, taxes, support plans, and RPC provider fees. | Item | Reference spec | Approx. monthly cost | | --------------------------- | ------------------------------- | -------------------- | | EC2 `t4g.small` | 730 h × public on-demand rate | \~\$12 | | EBS gp3 root disk | 16 GB encrypted gp3 | \~\$1.5 | | RDS PostgreSQL | `db.t4g.micro`, single-AZ | \~\$13 | | RDS storage + backups | 20 GB gp3 + 7-day backups | \~\$3 | | AWS Managed Prometheus | \~10M allowlisted samples/month | \~\$9 | | KMS key + signing API calls | 1 asymmetric secp256k1 CMK | \~\$1–3 | | Secrets Manager | \~5 secrets | \~\$2 | | ECR / SSM / CloudWatch logs | Light operational usage | \~\$1–3 | | Data transfer | ragep2p + RPC client traffic | \~\$2–5 | | **Total before RPC fees** | Single validator | **\~\$45–55/month** | RPC fees are separate and may be the largest operating line item. Budget for reliable HTTPS and WSS endpoints on every assigned chain through Alchemy, QuickNode, or an equivalent provider. Adding a separate optional snapshot service host typically adds about **\$20–25/month** for another small EC2 host, EIP, and extra metric ingestion. Redis or other cache costs depend on the deployment shape and whether the service is shared. ## Cloud and bare-metal equivalents Rough equivalents for the recommended tier: | Provider | Compute | Database | | ------------- | ------------------------------------------------ | ----------------------------------------------------------------- | | AWS | `t4g.small` | RDS PostgreSQL `db.t4g.micro`, single-AZ | | GCP | `t2a-standard-1` or small x86 VM | Cloud SQL PostgreSQL small shared/burstable tier | | Azure | `B2pls v2` or comparable 2 vCPU VM | Azure Database for PostgreSQL burstable tier | | Hetzner Cloud | `CAX11` or comparable 2 vCPU VM | Managed Postgres if available, otherwise self-hosted with backups | | OVHcloud | Small 2 vCPU public cloud or ARM bare-metal host | Public Cloud Database for PostgreSQL | | Bare metal | Any reliable 2 vCPU / 2 GB server with SSD | Self-hosted PostgreSQL 15+ with tested backups | Hetzner, OVH, and bare-metal deployments can be cheaper than AWS. The tradeoff is operational ownership: backups, monitoring, failover, OS patching, network reliability, and key custody become your responsibility. ## Optional snapshot service Some vaults need a price provider for cross-asset PPS conversion, such as Pendle, Spectra, or staking-style vaults that report shares in a different unit than the deposit asset. When those vaults are assigned, operators may run `snapshotd` as either: * **IPC sidecar**: same host as the validator, communicating over a Unix socket. Lowest cost and simplest when one validator consumes it. * **Standalone HTTP service**: separate host with JWT-gated HTTPS, useful when multiple validators or services share one snapshot endpoint. Both modes can share the same Redis-compatible cache for vault metadata, so a validator restart does not need to re-fetch every vault domain separator. If all assigned vaults are standard ERC-4626 and do not require cross-asset conversion, the validator can read directly from configured vault contracts and the snapshot service may not be needed. ## What to read next Fast end-to-end checklist once you are approved. Install the node, configure runtime files, and verify startup. Full `config.toml`, `chains.yaml`, KMS, and OCR2 timing reference. Health endpoints, priority metrics, alerts, and operating habits. # Monitoring Source: https://docs.superform.xyz/build/become-a-validator/monitoring Health endpoints, Prometheus metrics, and alerting guidance for Superform validator operators. Use this page when you need validator observability and alerting guidance. For config fields, use [Configuration Reference](/build/become-a-validator/configuration-reference). For operator procedures, use [Operations](/build/become-a-validator/operations). ## Health endpoints Both ports are configured under `[monitoring]` in `config.toml`. | Endpoint | Port | Purpose | | -------------- | -------------- | ------------------------------------------------------ | | `GET /metrics` | `metrics_port` | Prometheus scrape target | | `GET /healthz` | `health_port` | Process + DB liveness | | `GET /readyz` | `health_port` | Same checks today; reserved for future readiness gates | Quick check: ```bash theme={null} curl -s localhost:8080/healthz curl -s localhost:8080/readyz curl -s localhost:9090/metrics | grep ^ocr2_ | head -20 ``` ## Priority signals If you only alert on a handful of metrics, start here: * `ocr2_up` — process liveness * `ocr2_strategy_last_update_seconds` — confirms PPS updates are still landing onchain * `ocr2_strategy_update_stale` — fastest signal that a strategy has stopped updating * `ocr2_transmit_total` labeled by `status` — shows whether transmit attempts are succeeding * `ocr2_plugin_observations_total` labeled by `result` — reveals RPC or observation failures * `ocr2_config_version` and `ocr2_config_signers` — confirm the node loaded the expected active network config ## Metrics reference ### Onchain transmission health * `ocr2_strategy_last_update_seconds` (labels: `chain_id`, `strategy`) — gauge with the Unix timestamp of the latest observed `PPSValidated` event * `ocr2_strategy_update_stale` (labels: `chain_id`, `strategy`) — gauge set to `1` when no `PPSValidated` event arrives within `health_check_interval` ### Transmitter * `ocr2_transmit_total` (labels: `chain_id`, `status`) — counter of transmission attempts by outcome * `ocr2_transmit_strategies_total` (label: `chain_id`) — counter of strategies packed into submitted transactions * `ocr2_transmit_pack_errors_total` (label: `chain_id`) — counter of report packing failures * `ocr2_transmit_duration_seconds` (label: `result`) — histogram of end-to-end transmit latency ### Plugin phases * `ocr2_plugin_phase_duration_seconds` (labels: `phase`, `result`) — histogram of phase latency * `ocr2_plugin_phase_total` (labels: `phase`, `result`) — counter of phase invocation outcomes * `ocr2_plugin_observations_total` (labels: `chain_id`, `result`) — counter of per-strategy observation outcomes * `ocr2_plugin_insufficient_observations_total` (label: `chain_id`) — counter of vaults dropped because quorum observations were not available ## Alerting baseline For production, alert on the smallest set of signals that prove the node can observe, participate, and transmit: | Signal | Suggested action | | ----------------------------------------------------------- | ------------------------------------------------------------------ | | `ocr2_up` missing or `0` | Page or notify the operator immediately | | `ocr2_strategy_update_stale == 1` | Treat as an incident; check RPC, config, and transmission logs | | `ocr2_transmit_total{status="failure"}` increasing | Investigate transmitter key, RPC health, gas, and contract reverts | | `ocr2_plugin_observations_total{result="error"}` increasing | Check per-chain RPC health and vault configuration | | Unexpected `ocr2_config_version` | Confirm the node loaded the intended onchain config | Email, Slack, or SNS-style notifications are enough for most operators. Operators with SLA requirements should route these alerts into PagerDuty, Opsgenie, or an equivalent on-call system. ## Recommended operating habit After every config change, restart, or upgrade, verify three things in this order: 1. `/healthz` is healthy 2. `ocr2_config_version` and `ocr2_config_signers` match expectations 3. `ocr2_strategy_last_update_seconds` continues advancing # Node Setup Source: https://docs.superform.xyz/build/become-a-validator/node-setup Canonical install guide for running a Superform validator node with Docker or from source. This is the canonical setup guide. Use it when you need the full install flow, not just the short checklist. Size production hosts first with [Infrastructure Sizing](/build/become-a-validator/infrastructure). ## Before you install Make sure you already have: * approval to join the permissioned validator program * the bootstrap peer entries supplied by Superform * the chain-specific contract addresses and current config version for your deployment * reliable HTTPS and WSS RPC endpoints for each assigned chain * PostgreSQL 15+ If you do not have those yet, stop and get them first. The software is the easy part. ## System requirements | Requirement | Baseline | | ----------- | -------------------------------------------------------------------- | | OS | Linux or macOS | | CPU | 2 vCPU minimum | | RAM | 2 GB minimum, 4 GB recommended | | Disk | 20 GB minimum | | Database | PostgreSQL 15+ | | Network | Stable outbound connectivity, direct TCP `6690`, and reliable uptime | ## Choose a deployment method Recommended for most operators. No Go toolchain required. Use this if you need direct source control over the runtime. Useful when validating local code changes before deployment. ## Published Image Images are published to GitHub Container Registry: ```text theme={null} ghcr.io/superform-xyz/validator-network ``` ### 1. Authenticate to GHCR ```bash theme={null} echo $GITHUB_TOKEN | docker login ghcr.io -u --password-stdin ``` You need a GitHub token with `read:packages`. ### 2. Pull the image ```bash theme={null} docker pull ghcr.io/superform-xyz/validator-network:latest # or pin a versioned release tag when available docker pull ghcr.io/superform-xyz/validator-network:v1.2.3 ``` Prefer pinned tags in production. ### 3. Prepare local config files ```bash theme={null} cp config.template.toml config.toml cp chains.template.yaml chains.yaml cp .env.template .env ``` If you do not have the templates locally, get them from the validator repository or your onboarding bundle before continuing. ### 4. Fill in the required values In `config.toml`, set: * `[keys]` offchain private key for direct-hex or KMS-backed setups, or `key_file_path` if you are loading from `./keystore/ocr2_.json` * `[keys.onchain_private_key]` dev private key or production `kms_key_id` * `[database].url` * `[p2p].bootstrap_peers` * optional `announce_addresses` if you are behind NAT * `chain_config_path` In `chains.yaml`, every assigned chain needs both a reliable HTTPS RPC endpoint for reads/tx submission and a WSS endpoint for event subscriptions. Do not treat WSS as optional. In `chains.yaml`, set the RPC endpoints and the contract addresses provided by Superform. ### 5. Run the container ```bash theme={null} docker run -d \ --name oracle-node \ -v $(pwd)/config.toml:/app/config.toml:ro \ -v $(pwd)/chains.yaml:/app/chains.yaml:ro \ -v $(pwd)/keystore:/app/keystore:ro \ -p 6690:6690 -p 9090:9090 -p 8080:8080 \ ghcr.io/superform-xyz/validator-network:latest ``` If `config.toml` uses `key_file_path = "./keystore/ocr2_.json"`, the `./keystore` mount is mandatory because the node also expects `./keystore/p2p_.json` beside it. Direct-hex configs do not need the mount. KMS only replaces the onchain signer, so you still need either direct offchain hex or the local keystore/P2P files. ### 6. Verify basic health ```bash theme={null} docker logs -f oracle-node curl -s localhost:8080/healthz curl -s localhost:9090/metrics | grep ^ocr2_ | head -20 ``` ## Build from Source ### 1. Clone the repository ```bash theme={null} git clone https://github.com/superform-xyz/supervaults-validator-network.git cd supervaults-validator-network ``` ### 2. Install dependencies ```bash theme={null} make deps ``` ### 3. Set up PostgreSQL ```bash theme={null} make setup-db ``` ### 4. Generate your keys ```bash theme={null} ./bin/oracle-node keys generate --keystore ./keystore ``` Back up the keystore immediately. ### 5. Create configuration files ```bash theme={null} cp config.template.toml config.toml cp chains.template.yaml chains.yaml ``` Populate the same runtime values listed in the container workflow above. ### 6. Build and run ```bash theme={null} make build make run-local ``` For more verbose logs: ```bash theme={null} ./bin/oracle-node --config config.toml --log-level debug ``` ## Docker (Build Locally) Use this when testing local code changes. ### 1. Clone the repository ```bash theme={null} git clone https://github.com/superform-xyz/supervaults-validator-network.git cd supervaults-validator-network ``` ### 2. Prepare the local Docker environment ```bash theme={null} make docker-setup-local ``` ### 3. Configure secrets and runtime values ```bash theme={null} nano .env nano config.toml nano chains.yaml ``` ### 4. Start services ```bash theme={null} make run-docker ``` ### 5. Useful lifecycle commands ```bash theme={null} make docker-logs make docker-down make docker-clean ``` *** ## Optional snapshot service Some assigned vaults may require `snapshotd` for cross-asset PPS conversion. If Superform tells you to run it, deploy it either as: * an IPC sidecar on the validator host using a Unix socket, or * a standalone HTTPS service protected by JWT when multiple validators or services share it. Skip it unless your assigned vault set requires it. Standard ERC-4626 vaults can be observed directly from configured contracts. ## Key management rules that matter during setup * Generate keys once per validator identity. * Back up the keystore before first production use. * Never commit `keystore/`, `.env`, or `config.toml`. * Use AWS KMS for the onchain signing key in production if supported by your environment. * KMS only covers the onchain signer; the OCR2/offchain key and P2P identity remain independently managed. * If you use key files, keep `ocr2_.json` and `p2p_.json` at `0600` permissions. ## Bootstrap peers Both Superform bootstrap peers must be configured in `[p2p].bootstrap_peers`. Your node uses them to join the mesh and discover the rest of the validator set. Those peer endpoints must resolve directly to the validator hosts. Cloudflare proxying or any other HTTP proxy on the P2P port will break ragep2p connectivity. The peer IDs are expected to remain stable across normal infrastructure replacements. If Superform ever rotates one, treat that as a real config update and replace the entry before your next restart. Networking rule that matters in production: * outbound TCP `6690` is mandatory so your node can reach the Superform bootstrap peers and the wider mesh * inbound TCP `6690` is required when other validators must dial the endpoint you advertise directly * outbound-only can work in some NATed topologies if your announced endpoint is still reachable through another path, but do not treat that as the default production assumption If you are behind NAT or a load balancer, set `announce_addresses` so other validators can dial you back correctly. Verify connectivity before startup: ```bash theme={null} nc -zv bootstrap-1.supervaults.superform.xyz 6690 nc -zv bootstrap-2.supervaults.superform.xyz 6690 ``` ## Healthy startup sequence On a clean boot you should see a progression close to: ```text theme={null} INFO Starting Supervaults PPS Oracle Node INFO Connecting to database... INFO Database connected INFO Loading keystore... INFO Keystore loaded INFO Creating OCR2 oracle... INFO OCR2 oracle instance created successfully INFO Starting OCR2 oracle... INFO OCR2 oracle started successfully INFO Oracle node started successfully ``` ## What success looks like before registration Before Superform registers your node, you should already have: * a healthy process * successful database connectivity * working `/healthz` and `/metrics` endpoints * no local config parse errors * clean outbound connectivity to the bootstrap peers and your RPC providers Once that is true, move to [Operations](/build/become-a-validator/operations) for registration and production runbook tasks. # Operations Source: https://docs.superform.xyz/build/become-a-validator/operations Runbook for registration, upgrades, key rotation, health checks, and troubleshooting validator nodes. This page is the operator runbook. It covers what happens after installation: registration, steady-state checks, upgrades, key rotation, and incident response. ## First production checklist Once the node is locally healthy, complete these steps in order. ### 1. Export your public keys ```bash theme={null} ./bin/oracle-node keys export ``` You need these public values: * onchain public key * derived Ethereum address * offchain public key * config encryption public key * peer ID * public P2P endpoint `keys export` is converting your local key material into the values Superform inserts into the live validator config: `validatorPublicKeys[]`, `validators[]`, and the ABI-encoded `OracleIdentity[]` entries inside `offchainConfig`. ### 2. Self-validate the bundle Before sending anything to Superform, verify: * `onchain_public_key` is 65 bytes / 130 hex chars and starts with `04` * `offchain_public_key` is 32 bytes / 64 hex chars * `config_encryption_public_key` is 32 bytes / 64 hex chars * the exported Ethereum address matches the onchain public key * `peer_id` is a valid libp2p base58 string, typically starting with `12D3Koo` * `p2p_endpoint` resolves publicly to the host that is actually serving your node on TCP `6690` * your node is reachable on the endpoint you plan to advertise * if you are behind NAT or a load balancer, `announce_addresses` is set to the same public `host:port` you plan to submit Use the smallest real preflight instead of guessing: ```bash theme={null} nc -zv 6690 ``` From another machine or network if possible. Do not put Cloudflare or any other HTTP proxy in front of this port. ### 3. Submit the public key bundle Send the bundle to your Superform onboarding contact. Never send private keys. ```json theme={null} { "validator_name": "Your Organization", "onchain_public_key": "04...", "ethereum_address": "0x...", "offchain_public_key": "...", "config_encryption_public_key": "...", "peer_id": "12D3KooW...", "p2p_endpoint": "your-validator.example.com:6690" } ``` Mapping reminder: * `onchain_public_key` → `validatorPublicKeys[]` * `ethereum_address` → `validators[]` * `peer_id` → `OracleIdentity.peerID` * `offchain_public_key` → `OracleIdentity.offchainPublicKey` * `config_encryption_public_key` → `OracleIdentity.configEncryptionPublicKey` ### 4. Wait for onchain registration During Phase 1, Superform calls `SuperGovernor.setValidatorConfig()` on your behalf. That payload has five parts: * `version` — must be strictly newer than the previous config * `validators[]` — one Ethereum address per validator * `validatorPublicKeys[]` — the matching 65-byte uncompressed ECDSA public keys * `quorum` — the report threshold for the validator set size * `offchainConfig` — the ABI-encoded `OracleIdentity[]` entries for the same validators For each validator, `validators[i]`, `validatorPublicKeys[i]`, and `OracleIdentity[i]` all have to describe the same operator identity. In practice, the config only loads cleanly when: * all three arrays have the same length * `validators[i]` is the Ethereum address derived from `validatorPublicKeys[i]` * `OracleIdentity.validatorPublicKey` byte-matches the same `validatorPublicKeys[i]` * the offchain and config encryption keys are both 32 bytes Most registration failures reduce to one of three things: array misalignment, address/public-key mismatch, or malformed `offchainConfig` bytes. If your node sees the event but refuses to load the config, reason about the failure in that order first. Once the transaction confirms, your node should pick up the new validator set automatically. ### 5. Confirm the node joined the active config Look for logs like: ```text theme={null} Received ValidatorConfigSet event blockNumber=... chainID=1 Config loaded successfully configDigest=0x... signers=N F=... ``` Within the next round, you should also see observation activity. Example healthy progression: ```text theme={null} INFO Starting PPS observation epoch=1 round=1 INFO Observation complete vaultsObserved=N ``` *** ## Steady-state health checks These are the minimum checks worth automating. ### Local endpoints ```bash theme={null} curl -s localhost:8080/healthz curl -s localhost:8080/readyz curl -s localhost:9090/metrics | grep ^ocr2_ ``` ### Metrics that matter most * `ocr2_up` * `ocr2_strategy_last_update_seconds` * `ocr2_strategy_update_stale` * `ocr2_transmit_total{status}` * `ocr2_plugin_observations_total{result}` * `ocr2_config_version` * `ocr2_config_signers` If `ocr2_strategy_last_update_seconds` stops advancing or `ocr2_strategy_update_stale` flips to `1`, treat it as a real incident. *** ## Key rotation Rotate keys when compromise is suspected, your custody model changes, or Superform asks you to re-register. ```bash theme={null} ./bin/oracle-node keys generate --keystore ./keystore-new ``` ```bash theme={null} ./bin/oracle-node keys export --keystore ./keystore-new ``` Superform must publish a new validator config onchain before the rotation is active. Replace the relevant private key values in `config.toml`, or update your KMS key reference. Restart only after the updated config is ready to use. ## Upgrades ### Source deployment ```bash theme={null} git pull origin main make build make run-local ``` ### Docker deployment Pull the target image tag, then restart the container or compose stack. ```bash theme={null} docker pull ghcr.io/superform-xyz/validator-network:latest ``` After any upgrade, verify: * the process starts cleanly * `/healthz` and `/metrics` work * `ocr2_config_version` is unchanged unless the network intentionally rotated config * observations and transmissions resume *** ## Common failure modes ### Database connection failure ```text theme={null} Error: failed to initialize database: failed to ping database ``` Check PostgreSQL availability and the `[database].url` value. ### Keys not found ```text theme={null} Error: failed to load OCR2 key bundle ``` Generate keys or fix the configured path / embedded key material. ### Bootstrap peers unreachable If the node never participates, verify: 1. outbound TCP `6690` is open 2. `bootstrap_peers` matches the current values from Superform 3. `announce_addresses` is set correctly if you are behind NAT 4. debug logs do not show repeated ragep2p dial failures ### Config digest mismatch If your `configDigest` differs from the rest of the network, check: * `config_version` in `config.toml` * whether you observed the same `ValidatorConfigSet` event as the rest of the network * whether Superform submitted aligned `validators[]`, `validatorPublicKeys[]`, and `OracleIdentity[]` arrays * whether `validators[i]` still derives from the paired `validatorPublicKeys[i]` and byte-matches the paired `OracleIdentity.validatorPublicKey` * whether your local binary matches the expected network release ### Observations succeed but no onchain transmissions land Check: 1. `ocr2_transmit_total{status="failure"}` and `{status="error"}` 2. whether the registered transmitter address matches your onchain key 3. RPC health on the transmit chain 4. debug logs for revert or nonce mismatch details *** ## Error reference | Error | Cause | Action | | --------------------------------------------------------- | -------------------------------------- | -------------------------------------------------------- | | `validator N (pubkey ...) not found in offchainConfig` | Onchain/offchain identity mismatch | Re-export keys and ask Superform to republish the config | | `derived F=N from quorum=N is invalid: F*3=N must be < N` | Invalid quorum for the validator count | Superform must adjust the config | | `failed to unpack offchain config` | Malformed `offchainConfig` bytes | Superform must republish the config | | `no ValidatorConfigSet events found` | Registration has not happened yet | Wait for Superform confirmation | | `could not import github.com/smartcontractkit/libocr` | Dependencies missing in source build | Run `make deps` | Full config, metrics, health endpoint, and alerting reference. # Quickstart Source: https://docs.superform.xyz/build/become-a-validator/quickstart The shortest safe path from validator approval to a healthy Superform node. Use this page when you want the fastest end-to-end path. It intentionally skips deep explanation. If you need full detail, jump to [Infrastructure Sizing](/build/become-a-validator/infrastructure), [Node Setup](/build/become-a-validator/node-setup), [Configuration Reference](/build/become-a-validator/configuration-reference), or [Operations](/build/become-a-validator/operations). Ready to join? [Apply to become a validator](https://docs.google.com/forms/d/e/1FAIpQLSc63kVcrBLYYs2dtl16ZMABjNy5lRNBrHHcfOS6RUiARYU-fg/viewform?usp=send_form\&pli=1\&authuser=0). Do not start here until Superform has confirmed you are approved to join and you have the required network inputs: bootstrap peers, chain-specific contract addresses, current config version, and a place to submit your public key bundle. Ready to join? [Apply to become a validator](https://docs.google.com/forms/d/e/1FAIpQLSc63kVcrBLYYs2dtl16ZMABjNy5lRNBrHHcfOS6RUiARYU-fg/viewform?usp=send_form\&pli=1\&authuser=0). **Time required:** \~30 minutes for setup, plus registration wait time. Make sure you have: * a Linux or macOS host * at least 2 vCPU, 2 GB RAM, and 20 GB disk; use [Infrastructure Sizing](/build/become-a-validator/infrastructure) before production * PostgreSQL 15+ * Docker **or** Go 1.24+ * reliable HTTPS and WSS RPC endpoints for every assigned chain * outbound TCP access to the Superform bootstrap peers on port `6690` * access to the validator runtime artifacts you plan to use ```bash theme={null} nc -zv bootstrap-1.supervaults.superform.xyz 6690 nc -zv bootstrap-2.supervaults.superform.xyz 6690 ``` If either check fails, fix firewall or routing before doing anything else. The P2P port is raw TCP ragep2p traffic. Do **not** put Cloudflare or any other HTTP proxy in front of port `6690`. ```bash theme={null} echo $GITHUB_TOKEN | docker login ghcr.io -u --password-stdin docker pull ghcr.io/superform-xyz/validator-network:latest ``` Use a pinned release tag in production when one is available. ```bash theme={null} git clone https://github.com/superform-xyz/supervaults-validator-network.git cd supervaults-validator-network make deps make build ``` ```bash theme={null} ./bin/oracle-node keys generate --keystore ./keystore ``` This creates your validator identity and writes two artifacts in `./keystore`: `ocr2_.json` for the OCR2/offchain + onchain bundle and `p2p_.json` for the ragep2p identity. Keep both files at `0600` permissions and back up the keystore before continuing. ```bash theme={null} cp config.template.toml config.toml cp chains.template.yaml chains.yaml ``` If you are using the published image, fetch `config.template.toml` and `chains.template.yaml` from the validator repo or your onboarding bundle before running those copy commands. Fill in: * your offchain and onchain keys if you are using direct hex * the generated key file path if you are loading from `./keystore/ocr2_.json` * the `kms_key_id` if you are using AWS KMS for the onchain signer * PostgreSQL connection string * Superform bootstrap peers * RPC endpoints for each assigned chain * the chain-specific `ecdsa_oracle_address` and `super_governor_address` values provided during onboarding Use [Node Setup](/build/become-a-validator/node-setup) for the full install flow and [Configuration Reference](/build/become-a-validator/configuration-reference) for every field. ```bash theme={null} docker run -d \ --name oracle-node \ -v $(pwd)/config.toml:/app/config.toml:ro \ -v $(pwd)/chains.yaml:/app/chains.yaml:ro \ -v $(pwd)/keystore:/app/keystore:ro \ -p 6690:6690 -p 9090:9090 -p 8080:8080 \ ghcr.io/superform-xyz/validator-network:latest docker logs -f oracle-node ``` Mount `./keystore` only when `config.toml` uses `key_file_path = "./keystore/ocr2_.json"`. Direct-hex configs do not need the extra volume, and KMS-backed setups still need the local offchain/P2P identity available to the container. ```bash theme={null} make setup-db make run-local ``` Then verify: ```bash theme={null} curl -s localhost:8080/healthz curl -s localhost:9090/metrics | grep ocr2_up ``` You want a healthy process before asking Superform to register it. ```bash theme={null} ./bin/oracle-node keys export ``` Send Superform the public values only: ```json theme={null} { "validator_name": "Your Organization", "onchain_public_key": "04...", "ethereum_address": "0x...", "offchain_public_key": "...", "config_encryption_public_key": "...", "peer_id": "12D3KooW...", "p2p_endpoint": "your-host.example.com:6690" } ``` `keys export` gives Superform exactly what they need to build the onchain config: `validatorPublicKeys[]` gets your onchain public key, `validators[]` gets the derived Ethereum address, and `OracleIdentity[]` gets your `peer_id`, `offchain_public_key`, and `config_encryption_public_key`. Never send private keys. For the full registration payload shape, validation invariants, and failure-mode debugging, use [Operations](/build/become-a-validator/operations). After Superform calls `SuperGovernor.setValidatorConfig()`, look for: ```text theme={null} Received ValidatorConfigSet event blockNumber=... chainID=1 Config loaded successfully configDigest=0x... signers=N F=... ``` Then, within the next round: ```text theme={null} INFO Starting PPS observation epoch=1 round=1 INFO Observation complete vaultsObserved=N ``` If your node never reaches these logs, go straight to [Operations](/build/become-a-validator/operations). ## Exit criteria * [ ] Bootstrap peers reachable on TCP `6690` * [ ] `config.toml` and `chains.yaml` filled with real values * [ ] `/healthz` returns healthy * [ ] `ocr2_up` is exposed in Prometheus metrics * [ ] Public key bundle submitted to Superform * [ ] `Config loaded successfully` appears after registration * [ ] First OCR2 observation logs appear # Write a Hook Source: https://docs.superform.xyz/build/hooks Step-by-step guide to building custom hooks on Superform, from repo setup through implementation and testing. Hooks are lightweight, permissionless contracts which perform a specific action (*bridge, swap, lend*, etc.). They are designed to be composable and can be chained together to create complex transaction flows which can achieve any multi-chain strategy. Hook chains can be fulfilled by any ERC-7579 smart account with the SuperValidator and SuperExecutor modules installed. ## Key Concepts Hooks implement transient storage during the execution of a transaction to temporarily hold state changes. This allows for efficient inter-hook communication that avoids the high gas costs associated with permanent storage writes. If any hook in a chain fails, the entire transaction is reverted, ensuring atomicity. ### Hook Lifecycle The execution of hooks is a three-step sequence bundled in the hook's `build()` function: `preExecute` → `execution(s)` → `postExecute` 1. **Build** - Frontend selects the addresses and calldata of the hook(s) in a chain. 2. **Bundle** - SuperBundler splits hooks per chain and wraps each into an ERC-4337 UserOp, storing a Merkle tree. 3. **Validate** - Smart Account delegates to SuperValidator onchain to perform Merkle proof verification. 4. **Execute** - SuperExecutor iterates through hooks, performs each action and updates SuperLedger. ### Hook Classifications Hooks are classified based on `HookType` and `HookSubType`. `HookType` must be defined as `NONACCOUNTING`, `INFLOW`, or `OUTFLOW` depending on the hook's expected interaction with the accounting system. `HookSubTypes` designate which action category the hook implements. * **NONACCOUNTING** - Utility hooks that do not update the accounting system. These hooks include `HookSubTypes` like `SWAP`, `STAKE`, `BRIDGE`. * **INFLOW** - Hooks that process additions to positions and typically encode some sort of `deposit` action. They include a `HookSubType` corresponding to the underlying vault being deposited into like `ERC4626`, `ERC7540`, etc. * **OUTFLOW** - Hooks that process reductions to positions and typically encode some sort of `redeem` action. They include similar `HookSubType` to `INFLOW` hooks. ## Quick Start **Requirements** * `Foundry ≥ v1.3.0` * `Solidity 0.8.30` * `pnpm` * `yarn` **Repo Setup** Clone the public repo and its submodules: ```bash theme={null} git clone --recursive https://github.com/superform-xyz/v2-core.git cd v2-core ``` Install dependencies: ```bash theme={null} forge install cd lib/modulekit pnpm install cd ../.. cd lib/safe7579 pnpm install cd ../.. cd lib/nexus yarn cd ../.. ``` Copy the environment file: ```bash theme={null} cp .env.example .env ``` **Build & Testing** Compile the contracts: ```bash theme={null} forge build ``` Run tests: ```bash theme={null} # Supply your node rpc directly in the makefile # Run all tests make ftest # Run a specific test # Replace the test in the `make test-vvv` command with the test to be run make test-vvv ``` ## Anatomy of a Hook 1. Every hook must inherit [BaseHook](https://github.com/superform-xyz/v2-core/blob/dev/src/hooks/BaseHook.sol) to obtain core hook functionality, along with any specific interfaces your hook may require from [ISuperHook](https://github.com/superform-xyz/v2-core/blob/dev/src/interfaces/ISuperHook.sol). 2. Each hook contract begins with natspec detailing the layout of the hook data, this ensures that SuperBundler is able to encode the data required for hook execution. 1. The natspec of the data structure must be placed after the line saying `@dev data has the following structure` and the natspec must follow the format of `@notice DataType Name Offset`. 2. An example of a hook with a simple encoding structure can be found [here](https://github.com/superform-xyz/v2-core/blob/dev/src/hooks/loan/morpho/MorphoSupplyAndBorrowHook.sol#L19). [This](https://github.com/superform-xyz/v2-core/blob/dev/src/hooks/bridges/debridge/DeBridgeSendOrderAndExecuteOnDstHook.sol#L16) is a more complex example of encoding natspec. 3. The constructor sets the `HookType` and `HookSubtype` as well as any immutable state variables such as the target contract address to call during execution. For a breakdown of different `HookType` options see [Hook Classifications](#hook-classifications), and the `HookSubTypes` are found in [this library](https://github.com/superform-xyz/v2-core/blob/dev/src/libraries/HookSubTypes.sol). If your hook will perform an action type that is not yet in the library, add it there as `bytes32 public constant CATEGORY_NAME`. 4. It is necessary to decode and validate the encoded hook data used by the hook. It is recommended to create some internal decoder and validation helpers. The decoding must align with the data locations denoted in the natspec as that is the order in which data will be encoded. There are two libraries you can use during the decoding process: 1. `BytesLib` - This necessary library can be found in the `src/vendor` directory, you need only import it to gain access to its functionality. 2. `HookDataDecoder` - This library lives in the `src/libraries` directory. It must be imported and the statement `using HookDataDecoder for bytes` must be placed at the top of the hook contract. 5. The `_buildHookExecutions()` function takes decoded hook data and returns an array of Executions. 1. There are 3 parameters for this function: 1. `prevHook` - The address of the prior hook in a chain, `address(0)` if this hook is unchained or the first in a chain. Note: If you intend for this hook to be after another hook in a chain `bool usePrevHookAmount` must be added as a parameter to be encoded in the hook data structure natspec, and `ISuperHookResult` is required. 2. `account` - The smart account address if needed for the Execution payload. 3. `data` - The bytes hook data (encoded in the structure defined by the natspec) 2. The payload of an Execution is as follows: 1. `Target` - The address to call (contract or another module) 2. `Value` - Native ETH amount to forward as `msg.value` 3. `Calldata` - The encoded function selector to be called and any arguments ## Hook Contract Outline ```solidity theme={null} /// @title MyHook /// @author SomeDev /// @dev data has the following structure /// @notice DataType Name Offset contract MyHook is BaseHook { // Constructor: set hook type + subtype constructor() BaseHook(HookType.INFLOW, HookSubTypes.ERC4626) {} // 1️⃣ Build the array of Execution structs function _buildHookExecutions( address prevHook, address account, bytes calldata data ) internal view override returns (Execution[] memory execs) { … } // 2️⃣ Validate & prepare (state‑changing) function _preExecute( address prevHook, address account, bytes calldata data ) internal override { … } // 3️⃣ Finalise, set outAmount / usedShares function _postExecute( address prevHook, address account, bytes calldata data ) internal override { … } } ``` Key fields inherited from `BaseHook`: | Variable | Purpose | | -------------------------------- | ------------------------------------------------- | | `hookType` | Enum for accounting engine | | `subType` | 32-byte tag for analytics / routing | | `usedShares`, `spToken`, `asset` | Transient storage outputs consumed by later hooks | | `setOutAmount(uint256,address)` | Records how many units this hook produced | See the full base contract for inline docs [here](https://github.com/superform-xyz/v2-core/raw/dev/src/hooks/BaseHook.sol). ## Writing your First Hook: Approve-and-Deposit ERC-4626 We'll re-create the example at `src/hooks/vaults/4626/ApproveAndDeposit4626VaultHook.sol` ([GitHub](https://github.com/superform-xyz/v2-core/raw/dev/src/hooks/vaults/4626/ApproveAndDeposit4626VaultHook.sol)). Approve the vault, deposit `amount` into it, and emit the number of shares received so that downstream hooks can compose on it. ### Hook Data Layout | Offset | Bytes | Field | | ------ | ----- | ---------------------------------- | | 0 | 32 | `yieldSourceOracleId` *(optional)* | | 32 | 20 | `yieldSource` (`IERC4626` vault) | | 52 | 20 | `token` (underlying) | | 72 | 32 | `amount` (uint256) | | 104 | 1 | `usePrevHookAmount` (bool) | ### Implementation Highlights ```solidity theme={null} /// 1. Create the hook natspec /// @title ApproveAndDeposit4626VaultHook Tutorial /// @dev data has the following structure /// @notice bytes32 yieldSourceOracleId = bytes32(BytesLib.slice(data, 0, 32), 0); /// @notice address yieldSource = BytesLib.toAddress(data, 32); /// @notice address token = BytesLib.toAddress(data, 52); /// @notice uint256 amount = BytesLib.toUint256(data, 72); /// @notice bool usePrevHookAmount = _decodeBool(data, 104); contract ApproveAndDeposit4626VaultHook is BaseHook, // 2. Implement inheritance ISuperHookInflowOutflow, ISuperHookContextAware { using HookDataDecoder for bytes; // 3. Import and setup decoder libraries uint256 private constant AMOUNT_POS = 72; uint256 private constant USE_PREV_POS = 104; // 4. Set categories in constructor constructor() BaseHook(HookType.INFLOW, HookSubTypes.ERC4626) { } /* ---------- build() internals ---------- */ // 5. Create the build executions function _buildHookExecutions( address prev, address account, bytes calldata data ) internal view override returns (Execution[] memory ex) { // 5.1 Decode data address yieldSource = data.extractYieldSource(); address token = BytesLib.toAddress(data, 52); uint256 amount = _decodeAmount(data); bool usePrevHookAmount = _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION); // 5.2 Hook chaining functionality if (usePrevHookAmount) { amount = ISuperHookResult(prevHook).getOutAmount(account); } // Note: re-use prevHook's output when usePrevHookAmount = true // 5.3 Data validation if (amount == 0) revert AMOUNT_NOT_VALID(); if (yieldSource == address(0) || token == address(0)) revert ADDRESS_NOT_VALID(); // 5.4.1 Define Executions // 4 calls = 4 executions: reset approval → approve → deposit → reset approval executions = new Execution[](4); // 5.4.2 Populate execution elements executions[0] = Execution({ target: token, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, 0)) }); executions[1] = Execution({ target: token, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, amount)) }); executions[2] = Execution({ target: yieldSource, value: 0, callData: abi.encodeCall(IERC4626.deposit, (amount, account)) }); executions[3] = Execution({ target: token, value: 0, callData: abi.encodeCall(IERC20.approve, (yieldSource, 0)) }); // Security note: zero allowances before/after deposit to avoid "variable-approval" exploit } /* ---------- pre / post ---------- */ // 6. Validate & prepare any state-changing amounts required function _preExecute( address, address account, bytes calldata data ) internal override { _setOutAmount(_vaultBal(account, data), account); // snapshot spToken = data.extractYieldSource(); } // 7. Finalise, set outAmount/usedShares function _postExecute( address, address account, bytes calldata data ) internal override { uint256 afterBal = _vaultBal(account, data); _setOutAmount(afterBal - getOutAmount(account), account); } /* ---------- helpers ---------- */ function _decodeAmount(bytes memory data) private pure returns (uint256) { return BytesLib.toUint256(data, AMOUNT_POS); } function _vaultBal(address acct, bytes memory d) private view returns (uint256) { return IERC4626(d.extractYieldSource()).balanceOf(acct); } } ``` To test the newly created hook follow the steps outlined in the [Quick Start](#quick-start) section above. # Curate a SuperVault Source: https://docs.superform.xyz/build/manage-a-supervault On-chain vault creation, manager roles, upkeep, strategy management, fee configuration, and redemption fulfillment via SuperVaultAggregator. While SuperVault deployment is permissionless, only SuperVaults whose managers perform KYC and stake \$UP in the Aggregator contract will be displayed on the Superform UI as valid vaults. For a guided curation experience, use the institutional dashboard at [institutions.superform.xyz](https://institutions.superform.xyz). See the full [Curate Documentation](/curate) for the complete operational guide. Once a manager becomes a validated curator, they gain full access to the tooling on the [curators.superform.xyz](https://curators.superform.xyz) site. ## Creating and Setting Up a Vault Vaults can be created in SuperVaultAggregator by calling `createVault` using the following parameters. ```solidity theme={null} function createVault(VaultCreationParams calldata params) external returns (address superVault, address strategy, address escrow) {} struct VaultCreationParams { address asset; // Address of the underlying asset string name; // Name of the vault token string symbol; // Symbol of the vault token address mainStrategist; // Address of the vault mainStrategist uint256 minUpdateInterval; // Minimum time interval between PPS updates uint256 maxStaleness; // Maximum time allowed between PPS updates before staleness FeeConfig feeConfig; } struct FeeConfig { uint256 managementFeeBps; // Management fee in basis points uint256 performanceFeeBps; // Performance fee in basis points address recipient; // Fee recipient address } ``` ## Primary Managers The primary manager has full control over strategy operations, including the list below as well as the functionality granted to secondary managers. * Create vaults * Update secondary managers * Pause and unpause strategies * Update PPS expiration thresholds * Add and remove authorized callers * Add hooks to the strategy hooks root * Update vault performance and management fees ### Control of Primary Managers If a primary manager is found to be malicious, they can be replaced by either the secondary managers or `SUPER_GOVERNOR` through the following functions of SuperVaultAggregator. ```solidity theme={null} /// @notice Change the primary manager for a strategy /// @dev Only SuperGovernor can call this function directly /// @param strategy The strategy address /// @param newManager The new primary manager address /// @param feeRecipient The new fee recipient address function changePrimaryManager(address strategy, address newManager, address feeRecipient) external; /// @notice Proposes a change to the primary manager /// @notice Caller can either be secondary or primary manager /// @param strategy Address of the strategy /// @param newManager Address of the proposed new primary manager /// @param feeRecipient Address of the new fee recipient function proposeChangePrimaryManager(address strategy, address newManager, address feeRecipient) external; /// @notice Executes a previously proposed change to the primary manager after timelock /// @param strategy Address of the strategy function executeChangePrimaryManager(address strategy) external; ``` ## Secondary Managers The primary manager can add multiple secondary managers as backup accounts who have partial control over strategy operations. * Skim performance fees * Whitelist yield sources * Update primary manager * Call `executeHooks()` to interact with underlying yield sources * Call `executeHooks()` to perform emergency withdrawals * Fulfill redeem requests * Fulfill cancel redeem requests The list of secondary managers for a given strategy can be maintained as follows. ```solidity theme={null} /// @notice Adds a secondary manager to a strategy /// @notice Either primary or secondary managers can append secondary managers /// @param strategy Address of the strategy /// @param manager Address of the manager to add function addSecondaryManager(address strategy, address manager) external; /// @notice Removes a secondary manager from a strategy /// @notice Only the primary manager can remove secondary managers /// @param strategy Address of the strategy /// @param manager Address of the manager to remove function removeSecondaryManager(address strategy, address manager) external; ``` ## Upkeep Mechanism Managers must deposit \$UP as upkeep to ensure that validators are compensated for the cost of updating onchain PPS for their vaults. This is done in SuperVaultAggregator. ```solidity theme={null} /// @notice Deposits upkeep tokens for strategy upkeep /// @param strategy Address of the strategy to deposit for /// @param amount Amount of upkeep tokens to deposit function depositUpkeep(address strategy, uint256 amount) external; ``` Upkeep withdrawals are two-step and have a 24 hour timelock. ```solidity theme={null} /// @notice Proposes withdrawal of upkeep tokens from strategy upkeep balance (starts 24h timelock) /// @dev Only the main manager can propose. Withdraws full balance at time of proposal. /// @param strategy Address of the strategy to withdraw from function proposeWithdrawUpkeep(address strategy) external; /// @notice Executes a pending upkeep withdrawal after 24h timelock /// @dev Anyone can execute, but funds go to the main manager of the strategy /// @param strategy Address of the strategy to withdraw from function executeWithdrawUpkeep(address strategy) external; ``` ## Strategy Management Once vaults are created, managers automatically benefit from the list of hooks that are in the global hooks root. However, if more functionality is to be added, primary managers can propose a new strategy hooks root. ```solidity theme={null} function proposeStrategyHooksRoot(address strategy, bytes32 newRoot) external validStrategy(strategy) ``` Once the hooks required to perform a given yield strategy are available in the hooks root, the managers can run them through `executeHooks()`. The [curators dashboard](https://curators.superform.xyz/) supports hook execution simulations. It is advised to run these before actual execution. ```solidity theme={null} /// @notice Structure for hook execution arguments struct ExecuteArgs { /// @notice Array of hooks to execute address[] hooks; /// @notice Calldata for each hook (must match hooks array length) bytes[] hookCalldata; /// @notice Expected output amounts or output shares uint256[] expectedAssetsOrSharesOut; /// @notice Global Merkle proofs for hook validation (must match hooks array length) bytes32[][] globalProofs; /// @notice Strategy-specific Merkle proofs for hook validation (must match hooks array length) bytes32[][] strategyProofs; } /// @notice Execute hooks for general strategy management (rebalancing, etc.). /// @notice Any manager can call this function. /// @param args Execution arguments containing hooks, calldata, proofs, expectations. function executeHooks(ExecuteArgs calldata args) external payable; ``` ### Strategy Pausing Both primary and secondary managers can manually pause strategies in the event of an emergency. ```solidity theme={null} /// @notice Manually pauses a strategy /// @param strategy Address of the strategy to pause /// @dev Only the main or secondary manager of the strategy can pause it function pauseStrategy(address strategy) external validStrategy(strategy) {} /// @notice Manually unpauses a strategy /// @param strategy Address of the strategy to unpause /// @dev unpausing marks PPS stale until a fresh oracle update function unpauseStrategy(address strategy) external validStrategy(strategy) {} ``` ## Fee Mechanism ### Fee Configuration Managers can alter fee parameters through the following functions. ```solidity theme={null} /// @notice Change the fee recipient when the primary manager is changed /// @param newRecipient New fee recipient function changeFeeRecipient(address newRecipient) external; /// @notice Propose or execute a hook root update /// @notice Propose changes to vault-specific fee configuration /// @param performanceFeeBps New performance fee in basis points /// @param managementFeeBps New management fee in basis points /// @param recipient New fee recipient /// @dev IMPORTANT: Before executing the proposed update (via executeVaultFeeConfigUpdate), /// manager should call skimPerformanceFee() to collect performance fees on existing profits /// under the current fee structure to avoid losing profit or incorrect fee calculations. function proposeVaultFeeConfigUpdate( uint256 performanceFeeBps, uint256 managementFeeBps, address recipient ) external; /// @notice Execute the proposed vault fee configuration update after timelock /// @dev IMPORTANT: Manager should call skimPerformanceFee() before executing this update /// to collect performance fees on existing profits under the current fee structure. /// Otherwise, profit earned under the old fee percentage will be lost or incorrectly calculated. /// @dev This function will reset the High Water Mark (vaultHwmPps) to the current PPS value /// to avoid incorrect fee calculations with the new fee structure. function executeVaultFeeConfigUpdate() external; ``` ### Fee Skimming Performance fees on realized profit are obtained by periodically calling `skimPerformanceFee()`. ```solidity theme={null} /// @notice Skim performance fees based on per-share High Water Mark (PPS-based) /// @dev Can be called by any manager when vault PPS has grown above HWM PPS /// @dev Uses PPS growth to calculate profit: (currentPPS - hwmPPS) * totalSupply / PRECISION /// @dev HWM is only updated during this function, not during deposits/redemptions function skimPerformanceFee() external; ``` The ideal frequency at which to skim fees depends on the TVL and PPS of a vault, but generally 24 hours is optimal. More precise simulations can be found on the curators dashboard and [here](https://github.com/superform-xyz/v2-periphery/tree/dev/research). ## User Redemptions User redemption and cancel redemption requests must be fulfilled manually by any authorized manager. It is advised to do so through the [curators dashboard](https://curators.superform.xyz/) where there is tooling to aid controller sorting and `totalAssetsOut` calculations. ```solidity theme={null} /// @notice Fulfills pending cancel redeem requests by making shares claimable /// @dev Processes all controllers with pending cancellation flags /// @dev Can only be called by authorized managers /// @param controllers Array of controller addresses with pending cancel requests function fulfillCancelRedeemRequests(address[] memory controllers) external; /// @notice Fulfills pending redeem requests with exact total assets per controller (pre-fee). /// @dev PRE: Off-chain sort/unique controllers. Call executeHooks(sum(totalAssetsOut)) first. /// @dev Social: totalAssetsOut[i] = theoreticalGross[i] (full). Selective: totalAssetsOut[i] < theoreticalGross[i]. /// @dev NOTE: totalAssetsOut includes fees - actual net amount received is calculated internally after fee /// deduction. /// @param controllers Ordered/unique controllers with pending requests. /// @param totalAssetsOut Total PRE-FEE assets available for each controller[i] (from executeHooks). function fulfillRedeemRequests(address[] calldata controllers, uint256[] calldata totalAssetsOut) external; ``` # Curate Source: https://docs.superform.xyz/curate Operator documentation for creating, activating, automating, and safely managing SuperVaults. Curate is the institutional control plane for SuperVaults. It lets managers create and launch vaults, authorize execution, operate strategies, and use controls when conditions change. ## What Curate Controls Deploy a SuperVault, attach provider/listing metadata, fund upkeep, and complete the setup checklist. Configure hook permissions, generate merkle trees, sync roots onchain, and publish proof artifacts. Build rule-based deposit, withdrawal, and rebalance strategies with lane ordering and OMS-backed intent execution. Pause vaults, pause keeper services, and arm per-yield-source emergency liquidity exits. ## Operator Jobs Review [Prerequisites](/curate/prerequisites), [Authentication](/curate/authentication), and [Permissions](/curate/permissions). Use the four-step [Create Vault](/curate/ui/create-vault) wizard to configure identity, managers, fees, and listing metadata. Work through the [Setup Checklist](/curate/ui/setup-checklist): created, upkeep funded, hooks configured, yield sources whitelisted, strategy created, and catalog listed/available. Monitor [Dashboard](/curate/ui/dashboard), [Strategy Canvas](/curate/ui/strategy), [Yield Sources](/curate/ui/yield-sources), and [Merkle Trees](/curate/ui/merkle-trees). Use [Pause Operations](/curate/ui/pause), [Alerts](/curate/ui/alerts), audit logs, and notification channels to halt or drain risk when needed. ## API Surface Curate is backed by three main services: * [Erebor](/curate/api/erebor) — vault management, settings, registries, merkle workflows, upkeep, users, notifications, and pause controls. * [Strategy Engine](/curate/api/strategy) — strategies, ordering, live shards, intent projections, fills, and emergency locks. * [OMS](/curate/api/oms) — DeFiX intent execution, events, hook transactions, and session-key readiness. See [Curate API Overview](/curate/api/overview) for the control-plane map. Curate prepares transactions and displays operational state. Protocol state changes still happen through signed onchain transactions from the authorized manager wallet. # Erebor API Reference Source: https://docs.superform.xyz/curate/api/erebor Management API for vaults, settings, merkle authorization, registries, upkeep, notifications, session keys, and pause controls. Erebor is the SuperVaults management API. It powers vault reads, settings preparation, registries, merkle workflows, upkeep, users, pause controls, notification channels, and public vault data. **Base URL:** `https://erebor.superform.xyz` **Auth:** `Authorization: Bearer ` except public endpoints For onchain changes, Erebor prepares calldata and the manager wallet signs the transaction. The REST request is not the state change; the signed transaction is. ## Auth | Endpoint | Method | Description | | --------------------- | ------ | ------------------------------------- | | `/api/v1/auth/me` | GET | Current user, wallet, and vault roles | | `/api/v1/auth/verify` | GET | Lightweight JWT verification | ## Vaults and Public Discovery | Endpoint | Method | Description | | ------------------------------------------- | ------ | ------------------------ | | `/api/v1/public/vaults` | GET | Public vault list | | `/api/v1/vaults` | GET | Authenticated vault list | | `/api/v1/vaults/{chain_id}/{vault_address}` | GET | Full vault detail | Vault detail includes onchain identity, managers, fees, PPS, balances, TVL, upkeep information, activation state, and allocation data when available. Authenticated list responses may include `activation`; public vault responses include `upkeep_info` and `activation` when Erebor can compute them. Public discovery depends on more than deployment. Erebor only surfaces vaults with known activation when `activation.is_discoverable` is true. If activation is unknown because a subgraph dependency is unavailable, the vault can still be returned with `activation` unset rather than treated as deleted. ## Vault Creation | Endpoint | Method | Description | | --------------------------------- | ------ | ---------------------------------------------------- | | `/api/v1/vaults/create/prepare` | POST | Prepare vault creation transaction | | `/api/v1/vaults/create/confirm` | POST | Confirm creation transaction hash and start indexing | | `/api/v1/vaults/create/jobs/{id}` | GET | Poll creation job | `ConfirmCreateVaultRequest` can include `vault_config.redeem_timelock_seconds` for post-indexing settings. Listing metadata is handled by the dashboard/catalog layer after vault address resolution. Onchain creation and catalog availability are separate states. ## Settings and Upkeep | Endpoint | Method | Description | | ----------------------------------------------------------- | ------ | ----------------------------------- | | `/api/v1/settings/upkeep` | POST | Prepare upkeep settings update | | `/api/v1/settings/upkeep/deposit` | POST | Prepare upkeep deposit | | `/api/v1/settings/upkeep/withdraw/propose` | POST | Prepare upkeep withdrawal proposal | | `/api/v1/settings/upkeep/withdraw/execute` | POST | Prepare upkeep withdrawal execution | | `/api/v1/settings/redeem-timelock` | POST | Prepare redeem timelock update | | `/api/v1/redeem-timelocks` | GET | List redeem timelocks | | `/api/v1/redeem-timelocks/bulk` | GET | Bulk redeem timelock lookup | | `/api/v1/vaults/{chain_id}/{vault_address}/redeem-timelock` | GET | Read one vault's redeem timelock | Upkeep data drives activation and discoverability in the dashboard. Operators should treat insufficient upkeep as a launch blocker. Current upkeep responses use: | Field | Description | | ---------------------------------------- | ---------------------------------------------------------------- | | `upkeep_info.balance` | Current upkeep balance in raw token units. | | `upkeep_info.token` | Upkeep token address on settings responses. | | `upkeep_info.threshold.required_balance` | Required upkeep balance for activation. | | `upkeep_info.threshold.shortfall` | Remaining amount needed; `0` means the threshold is satisfied. | | `activation.is_active` | Whether the vault is eligible for keeper operations. | | `activation.is_discoverable` | Whether the vault is surfaced on public listing responses. | | `activation.status` | `active` or `insufficient_upkeep`. | | `activation.reason` | Optional explanation when inactive. | | `withdrawal_status` | Pending withdrawal lifecycle; separate from `activation.status`. | ## Merkle Authorization | Endpoint | Method | Description | | --------------------------------------------- | ------ | ------------------------------ | | `/api/v1/merkle/configs/versions` | GET | Config version history | | `/api/v1/merkle/configs/version` | GET | Single config version | | `/api/v1/merkle/configs/active` | GET | Active config | | `/api/v1/merkle/configs` | POST | Create config | | `/api/v1/merkle/configs/{id}` | GET | Get config by ID | | `/api/v1/merkle/configs/activate` | PUT | Activate config | | `/api/v1/merkle/trees` | GET | List trees | | `/api/v1/merkle/trees/{id}` | GET | Get tree | | `/api/v1/merkle/proofs` | GET | List proofs | | `/api/v1/merkle/proofs/lookup` | GET | Lookup proof | | `/api/v1/merkle/generate` | POST | Generate tree | | `/api/v1/merkle/jobs/{id}` | GET | Poll generation job | | `/api/v1/merkle/jobs` | GET | List jobs | | `/api/v1/merkle/sync` | GET | Determine onchain sync action | | `/api/v1/merkle/publish` | POST | Publish active proof artifact | | `/api/v1/merkle/publish/jobs/{id}` | GET | Poll publish job | | `/api/v1/merkle/published/{chain_id}/{vault}` | GET | Public published tree metadata | ## Registry | Endpoint | Method | Description | | ------------------------------------------------------ | ---------- | -------------------------------------- | | `/api/v1/registry/yield-sources` | GET/POST | Browse or create yield sources | | `/api/v1/registry/yield-sources/{chain_id}/{address}` | GET/PUT | Read or update source by chain/address | | `/api/v1/registry/yield-sources/{id}` | DELETE | Delete source | | `/api/v1/registry/yield-sources/sync` | POST | Import provider sources | | `/api/v1/registry/oracles` | GET/POST | Browse or create oracles | | `/api/v1/registry/oracles/{chain_id}/{oracle_address}` | GET/PUT | Read or update oracle | | `/api/v1/registry/oracles/{id}` | DELETE | Delete oracle | | `/api/v1/registry/hooks` | GET/POST | Browse or create hooks | | `/api/v1/registry/hooks/{id}` | PUT/DELETE | Update or delete hook | | `/api/v1/registry/token-assets` | GET/PUT | Browse or upsert token metadata | | `/api/v1/registry/token-assets/{chain_id}/{address}` | GET | Read token metadata | Yield source registry entries include active/inactive state and can carry external provider metadata. ## Yield Source Whitelist | Endpoint | Method | Description | | -------------------------------------------------- | ------ | -------------------------------------- | | `/api/v1/yield-sources/prepare-add` | POST | Prepare add transaction | | `/api/v1/yield-sources/prepare-remove` | POST | Prepare remove transaction | | `/api/v1/yield-sources/prepare-update-oracle` | POST | Prepare oracle update transaction | | `/api/v1/yield-sources/prepare-bulk-add` | POST | Prepare bulk add transaction | | `/api/v1/yield-sources/prepare-bulk-remove` | POST | Prepare bulk remove transaction | | `/api/v1/yield-sources/prepare-bulk-update-oracle` | POST | Prepare bulk oracle update transaction | ## Users and Permissions | Endpoint | Method | Description | | ------------------------------------------------ | ------ | -------------------------------------------- | | `/api/v1/users` | GET | List users for a vault | | `/api/v1/users/secondary-manager/prepare` | POST | Prepare add-secondary-manager transaction | | `/api/v1/users/secondary-manager/remove/prepare` | POST | Prepare remove-secondary-manager transaction | | `/api/v1/users/view-only` | POST | Add view-only user | | `/api/v1/users/view-only/{wallet}` | DELETE | Remove view-only user | | `/api/v1/users/{wallet}/display-name` | PATCH | Update display name | ## Session Keys | Endpoint | Method | Description | | ------------------------------------------------------- | ------ | --------------------------------- | | `/api/v1/session-keys` | GET | List manager-visible session keys | | `/api/v1/vaults/{chain_id}/{vault_address}/session-key` | GET | Read vault session key | | `/api/v1/vaults/session-key/prepare` | POST | Prepare session-key grant | | `/api/v1/vaults/session-key/revoke/prepare` | POST | Prepare session-key revoke | OMS owns additional readiness and rotation endpoints for strategy automation. ## Notification Channels | Endpoint | Method | Description | | ------------------------------------------------------------------------------ | -------- | ----------------------------- | | `/api/v1/notification-channels` | GET/POST | List or create channels | | `/api/v1/vaults/{chain_id}/{vault_address}/notification-channels` | GET/POST | List or attach vault channels | | `/api/v1/vaults/{chain_id}/{vault_address}/notification-channels/{channel_id}` | DELETE | Detach vault channel | Providers include Slack, webhook, and websocket channels. ## Pause and Services | Endpoint | Method | Description | | ---------------------------------------------------------- | ------ | ---------------------------- | | `/api/v1/vaults/pause-status` | GET | Batch pause status | | `/api/v1/vaults/emergency-pause/prepare` | POST | Prepare onchain pause | | `/api/v1/vaults/emergency-unpause/prepare` | POST | Prepare onchain unpause | | `/api/v1/vaults/{chain_id}/{vault}/services` | GET | List keeper services | | `/api/v1/vaults/{chain_id}/{vault}/services/{name}/pause` | PUT | Pause service | | `/api/v1/vaults/{chain_id}/{vault}/services/{name}/resume` | PUT | Resume service | | `/api/v1/vaults/{chain_id}/{vault}/services/enabled` | PUT | Bulk enable/disable services | | `/api/v1/vaults/{chain_id}/{vault}/services/{name}` | PUT | Update service config | | `/api/v1/services/bulk` | PUT | Bulk service update | ## Analytics, Audit, and Admin Analytics endpoints live under: ```bash theme={null} /api/v1/vaults/{chain_id}/{address}/analytics/* ``` Audit endpoints: | Endpoint | Method | Description | | ------------------------- | ------ | ----------------- | | `/api/v1/audit/logs` | GET | Query audit trail | | `/api/v1/audit/logs/{id}` | GET | Single audit row | | `/api/v1/audit/export` | GET | Export audit rows | Admin endpoints include APY, TVL, and event backfills for registry maintainers. # Getting SuperVaults Merkle Tree Source: https://docs.superform.xyz/curate/api/merkle-publish Fetch the published merkle tree and proofs for a SuperVault. No authentication required. The merkle publish endpoint returns the latest published merkle tree metadata for any vault. It is public — no authentication required. Use it to retrieve the proof artifact, verify the root hash, and check whether the published tree reflects the manager's current active configuration. ## Endpoint ``` GET /api/v1/merkle/published/{chain_id}/{vault_address} ``` **Base URL:** `https://erebor.superform.xyz`\ **Auth:** None ### Path Parameters | Parameter | Type | Description | | --------------- | ------- | -------------------------------------------------- | | `chain_id` | integer | Chain ID (e.g., `8453` for Base, `1` for Ethereum) | | `vault_address` | string | Vault contract address (checksummed or lowercase) | ## Response ### Published tree ```json theme={null} { "success": true, "data": { "is_stale": false, "published": true, "published_at": "2026-04-28T08:47:56.558012Z", "root_hash": "0xfadbeed8d842eabf62ae6388506ca506f716ab5722c21af61899baf04b2ed6dc", "s3_url": "https://erebor-merkle-trees-231862524346.s3.eu-north-1.amazonaws.com/vaults/8453/0x01c427580a2dfbdd894dabc4bff6c1dce7aab53c/e31ee747-c953-4a9d-ba28-0296c12397ec.json", "total_leaves": 4, "tree_id": "e31ee747-c953-4a9d-ba28-0296c12397ec" } } ``` ### Not yet published ```json theme={null} { "success": false, "published": false, "message": "no published tree found for this vault" } ``` ### Response Fields | Field | Type | Description | | -------------- | ------- | ---------------------------------------------------------------------------------------------------------- | | `published` | boolean | Whether the manager has published a tree for this vault | | `published_at` | string | ISO 8601 timestamp of the most recent publish | | `root_hash` | string | 32-byte merkle root. Verify this against the onchain root in `SuperVaultAggregator` before using any proof | | `s3_url` | string | Direct URL to the full proof JSON artifact | | `total_leaves` | integer | Number of authorized (hook, params) combinations in the tree | | `tree_id` | string | UUID of the published tree version | | `is_stale` | boolean | Whether the published tree is out of date — see below | ## Understanding `is_stale` `is_stale: true` when the published tree is not the vault's current active tree (including the case where no current active tree exists). The published artifact at `s3_url` was generated from a config version that no longer matches the vault's current state. When `is_stale` is `true`: * The `root_hash` in this response may no longer match the current onchain root * The proof artifact may not cover all currently authorized operations * Wait for the manager to re-publish before relying on these proofs for execution When `is_stale` is `false`: * The published tree was generated from the manager's current active config * The artifact is current, but still verify `root_hash` against the onchain `SuperVaultAggregator` state — publishing updates the off-chain record; the onchain root is updated separately through the proposal-timelock-execute cycle Even when `is_stale: false`, always verify `root_hash` against the onchain SuperVaultAggregator before passing proofs to `executeHooks()`. The off-chain publish and the onchain root sync are independent operations. ## Fetching the Proof Artifact The `s3_url` is a direct public URL to a JSON file containing the complete merkle tree with all leaf proofs. ```bash cURL theme={null} # 1. Get metadata curl https://erebor.superform.xyz/api/v1/merkle/published/8453/0x01c427580a2dfbdd894dabc4bff6c1dce7aab53c # 2. Fetch the proof artifact (use s3_url from response) curl https://erebor-merkle-trees-231862524346.s3.eu-north-1.amazonaws.com/vaults/8453/0x01c427580a2dfbdd894dabc4bff6c1dce7aab53c/e31ee747-c953-4a9d-ba28-0296c12397ec.json ``` ```python Python theme={null} import requests EREBOR_URL = "https://erebor.superform.xyz" CHAIN_ID = 8453 VAULT_ADDRESS = "0x01c427580a2dfbdd894dabc4bff6c1dce7aab53c" resp = requests.get(f"{EREBOR_URL}/api/v1/merkle/published/{CHAIN_ID}/{VAULT_ADDRESS}") resp.raise_for_status() body = resp.json() data = body.get("data") if not data or not data.get("published"): raise RuntimeError("No published tree for this vault") if data["is_stale"]: print("Warning: published tree is stale — manager has a newer config") # Fetch the full proof artifact tree = requests.get(data["s3_url"]).json() print(f"Root: {data['root_hash']}") print(f"Leaves: {data['total_leaves']}") ``` ```typescript TypeScript theme={null} const EREBOR_URL = "https://erebor.superform.xyz"; const CHAIN_ID = 8453; const VAULT_ADDRESS = "0x01c427580a2dfbdd894dabc4bff6c1dce7aab53c"; const metaResp = await fetch( `${EREBOR_URL}/api/v1/merkle/published/${CHAIN_ID}/${VAULT_ADDRESS}` ); if (!metaResp.ok) throw new Error(`Request failed: ${metaResp.status}`); const body = await metaResp.json(); const data = body.data; if (!data?.published) throw new Error("No published tree for this vault"); if (data.is_stale) console.warn("Published tree is stale — manager has a newer config"); const tree = await fetch(data.s3_url).then((r) => r.json()); console.log(`Root: ${data.root_hash}`); console.log(`Leaves: ${data.total_leaves}`); ``` ## Proof Artifact Shape The JSON at `s3_url` matches the `MerkleTreeWithLeaves` shape returned by `GET /api/v1/merkle/proofs`. Each leaf contains the hook address and name, encoded argument values, leaf hash, index in the tree, and the merkle proof path. ```json theme={null} { "root_hash": "0xfadbeed8d842eabf62ae6388506ca506f716ab5722c21af61899baf04b2ed6dc", "leaves": [ { "index": 0, "leaf_hash": "0x...", "hook_address": "0x...", "hook_name": "depositHook", "encoded_args": "0x...", "proof": ["0x...", "0x..."] } ] } ``` Pass `leaf.proof` as `strategyProofs[i]` when calling `executeHooks()`. ## Caching Responses are cached — 60 seconds on a successful hit, 30 seconds on a negative (unpublished) response. Build polling intervals of at least 60 seconds around this endpoint. ## Error Codes | Code | Meaning | | ----- | ---------------------------------------------------- | | `200` | Success — check `data.published` and `data.is_stale` | | `404` | Vault not registered in Erebor | | `500` | Internal error | # OMS API — DeFiX Intents & Session Keys Source: https://docs.superform.xyz/curate/api/oms Submit, track, cancel, and execute DeFiX intents, including session-key readiness for strategy automation. The OMS implements the DeFiX intent execution path. The Strategy Engine publishes intents; OMS validates authorization, builds execution, submits hook transactions, and emits lifecycle events / fills back to operators. **Base URL:** `https://oms.superform.xyz` **Auth:** `Authorization: Bearer ` In normal dashboard operations, managers do not submit intents directly. They configure strategies and monitor the resulting intent history. ## Intent Lifecycle OMS intent state is explicit and event-driven: ```text theme={null} RECEIVED → ACKED → STAGED → PLANNED → EXECUTING → COMPLETED ↘ ABORTED ↘ EXPIRED ↘ FAILED ↘ CANCELED ``` State values: | State | Meaning | | ----------- | ------------------------------------------ | | `RECEIVED` | OMS received the intent | | `ACKED` | Intent accepted for processing | | `STAGED` | Intent staged for planning/execution | | `PLANNED` | Route or execution plan built | | `EXECUTING` | Transaction execution in progress | | `COMPLETED` | Execution completed | | `ABORTED` | OMS aborted before success | | `EXPIRED` | Intent expired before execution | | `FAILED` | Execution failed | | `CANCELED` | Intent cancelled before terminal execution | Terminal states cannot be modified. ## Submit Intent ```bash theme={null} POST /defix/intents ``` Typical requests include: * `vault_id` * `strategy_id` * `action` * `objective` * `action_config` * `auth` * merkle proof / root data * account mode (`OMS`, `EIP4337`, `EIP4337_COMPANION`, or `EIP7702`) OMS validates the intent and authorization before execution. ## Get Intent ```bash theme={null} GET /defix/intents/{intent_id} ``` Returns the current OMS state, latest sequence number, timestamps, and completion timestamp when available. Fields such as `completed_at` may be `null` before terminal execution. ## Cancel Intent ```bash theme={null} POST /defix/intents/{intent_id}/cancel ``` Cancellation is only valid before the intent reaches a terminal state. ## Intent Events ```bash theme={null} GET /defix/intents/{intent_id}/events ``` Events are the source of truth for lifecycle reconstruction. Each event has: * `event_id` * `intent_id` * `execution_id` * `message_type` * `seq_num` * event-specific `payload` * `created_at` The Strategy Engine consumes these events and projects them into operator-friendly intent history. ## Fills and Mined Transactions Fills represent confirmed onchain execution evidence. In the operator UI, fills may include: * `tx_hash` * `chain_id` * `amount_in` * `amount_out` * `token_in` * `token_out` * `venue` * `gas_used` * mined / fill timestamp Pre-receipt rows can have `null` gas or missing fill fields until the transaction is mined and backfilled. ## Hook Transactions and Account Modes OMS supports native hook transaction execution and smart-account/user-operation modes. Account mode controls how execution is signed and submitted: | Mode | Meaning | | ------------------- | --------------------------------- | | `OMS` | OMS-managed delegated signer path | | `EIP4337` | ERC-4337 user operation path | | `EIP4337_COMPANION` | Companion 4337 path | | `EIP7702` | EIP-7702 account path | The hook transaction adapter handles execution against SuperVault hooks once session-key and merkle authorization are valid. ## Session Keys Strategy automation requires a valid session key for the vault. ### Readiness ```bash theme={null} GET /defix/vaults/{vault_id}/session-key/readiness ``` Returns whether the vault has a valid session key and whether a rotation is pending. ### Grant ```bash theme={null} GET /defix/vaults/{vault_id}/session-key/grant ``` Returns calldata to grant a session key. The manager signs and submits the transaction. ### Rotate / Renew ```bash theme={null} GET /defix/vaults/{vault_id}/session-key/rotation POST /defix/vaults/{vault_id}/session-key/rotation/complete POST /defix/vaults/{vault_id}/session-key/rotation/cancel ``` Complete rotation after the grant/revoke transactions have mined. ### Revoke ```bash theme={null} GET /defix/vaults/{vault_id}/session-key/revoke ``` Returns calldata to revoke the current session key. Session-key endpoints return transaction data for wallet signing. They do not bypass onchain permission checks. ## Endpoint Inventory | Endpoint | Method | Description | | -------------------------------------------------------- | ------ | -------------------------- | | `/defix/intents` | POST | Submit intent | | `/defix/intents/{intent_id}` | GET | Current intent state | | `/defix/intents/{intent_id}/cancel` | POST | Cancel non-terminal intent | | `/defix/intents/{intent_id}/events` | GET | Intent event log | | `/defix/vaults/{vault_id}/session-key/readiness` | GET | Session-key readiness | | `/defix/vaults/{vault_id}/session-key/grant` | GET | Prepare grant calldata | | `/defix/vaults/{vault_id}/session-key/rotation` | GET | Prepare rotation calldata | | `/defix/vaults/{vault_id}/session-key/rotation/complete` | POST | Complete rotation | | `/defix/vaults/{vault_id}/session-key/rotation/cancel` | POST | Cancel pending rotation | | `/defix/vaults/{vault_id}/session-key/revoke` | GET | Prepare revoke calldata | # Curate API Overview Source: https://docs.superform.xyz/curate/api/overview How Erebor, Strategy Engine, OMS, and merkle publishing fit together for SuperVault operations. Curate is the institutional control plane for SuperVaults. Its APIs split by operational responsibility. ## Services | Service | Owns | Docs | | ---------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------- | | Erebor | Vault reads, settings, registries, merkle config, upkeep, users, pause controls | [Erebor API](/curate/api/erebor) | | Strategy Engine | Strategy lifecycle, ordering, live shards, intent projections, emergency locks | [Strategy API](/curate/api/strategy) | | OMS | DeFiX intent execution, events, session-key readiness, hook transactions | [OMS API](/curate/api/oms) | | Public merkle artifact | Published proof data for keepers and integrators | [Merkle Publishing](/curate/api/merkle-publish) | ## Auth Authenticated endpoints use Dynamic-issued bearer JWTs: ```bash theme={null} Authorization: Bearer ``` Public endpoints, such as published merkle artifacts and public vault listings, do not require a manager JWT. ## Prepare, Sign, Confirm Most protocol mutations follow this model: 1. API prepares calldata. 2. Wallet signs and submits the transaction. 3. API / indexer reads resulting state or confirms a transaction hash. Do not treat a successful prepare request as a completed setting change. ## Vault Identity and Listing State A vault can be: * Created onchain. * Indexed in Erebor. * Active / discoverable after upkeep is funded. * Listed and available in the Superform catalog. Those are related but separate states. The dashboard setup flow tracks them together so operators do not route users into a half-configured vault. ## Strategy Execution Flow 1. Manager configures hooks and publishes merkle authorization. 2. Manager whitelists yield sources and pairs oracles. 3. Manager creates and orders strategies. 4. Strategy Engine evaluates rules and submits DeFiX intents. 5. OMS validates authorization and executes hook transactions. 6. Events and fills flow back into intent history. ## API Reference Pages * [Erebor API](/curate/api/erebor) * [Strategy Engine API](/curate/api/strategy) * [OMS API](/curate/api/oms) * [Merkle Publishing](/curate/api/merkle-publish) # Strategy Engine API Reference Source: https://docs.superform.xyz/curate/api/strategy Create, order, execute, and inspect SuperVault strategies, intents, fills, and emergency locks. The Strategy Engine evaluates manager-defined rules and dispatches DeFiX intents to the OMS. It owns strategy lifecycle state, lane ordering, live shard state, intent history projections, and emergency locks. **Base URL:** `https://strategy.superform.xyz` **Auth:** `Authorization: Bearer ` `vault_id` uses `{chainId}:{vaultAddress}` format, for example `8453:0xdef...`. Erebor often uses separate `chain_id` and `vault_address` fields. ## Strategies ### List Strategies ```bash theme={null} GET /api/v1/strategies?vault_id={vault_id}&limit=50 ``` Filters: * `kind` — `USER_DEFINED` or `EMERGENCY_EXIT`. * `state` — lifecycle state. * `lane` — `DEPOSIT`, `WITHDRAWAL`, or `REBALANCE`. * `target_address` — lowercased target yield source / route address. * `limit`, `offset`. Emergency strategies are separated by `kind=EMERGENCY_EXIT`; normal editor flows use `USER_DEFINED` by default. ### Create Strategy ```bash theme={null} POST /api/v1/strategies ``` Required fields include `id`, `vault_id`, `name`, `indicators`, `rules`, `action_config`, and `max_concurrent`. Priority is not part of create. Use the reorder endpoint after creation. ### Update Strategy ```bash theme={null} PUT /api/v1/strategies/{strategy_id} ``` `version` is required for optimistic concurrency. A stale version returns `409 Conflict`. Priority is not part of update. Use the reorder endpoint. ### Transition State ```bash theme={null} PATCH /api/v1/strategies/{strategy_id}/state { "target_state": "RUNNING", "version": 12 } ``` Allowed target states include `IDLE`, `RUNNING`, `ARCHIVED`, and `ERROR` depending on current state and policy. For emergency exits, arm requests may include `emergency_arm_reason`. ### Reorder Strategies ```bash theme={null} POST /api/v1/strategies/reorder { "vault_id": "8453:0xdef...", "lane": "DEPOSIT", "ordered_strategy_ids": ["strategy-a", "strategy-b"] } ``` The backend atomically assigns priorities `1..N` inside the `(vault_id, lane)` partition. Rules: * Include every non-archived strategy in that lane exactly once. * Do not include archived strategies. * The lane must match the lane derived from each strategy action. * Reorder is the only priority persistence path. ## Strategy Kinds | Kind | Description | | ---------------- | ------------------------------------------------------------------------- | | `USER_DEFINED` | Manager-authored strategy visible in normal canvas flows | | `EMERGENCY_EXIT` | System-managed withdrawal strategy paired to one whitelisted yield source | `EMERGENCY_EXIT` strategies are controlled from [Pause Operations](/curate/ui/pause). They are read-only in normal strategy editing flows. ## Vault Config and Shards ```bash theme={null} GET /api/v1/vaults/{vault_id} PATCH /api/v1/vaults/{vault_id} GET /api/v1/engine/status GET /api/v1/engine/shards/{vault_id}/state ``` Vault state includes kill-switch / rate-limit settings. Shard state includes live indicator values, active strategy state, yield-source allocations, and active `emergency_locks`. An emergency lock has: ```json theme={null} { "strategy_id": "emergency-8453:0xdef-0xsource...", "target_address": "0xsource...", "started_at": "2026-05-09T14:21:00Z", "reason": "Oracle anomaly" } ``` ## Intent History The Strategy Engine maintains an operator-facing projection of OMS intent execution. ### Search Intents ```bash theme={null} GET /api/v1/intents ``` Common filters: * `vault_id` * `strategy_id` * `action` * `state` * `chain_id` * `tx_hash` * `token_in`, `token_out` * created/completed time ranges * amount-in ranges * `error_code` * free-text search across `intent_id`, `execution_id`, `error_message`, and fill transaction hash Cursor pagination is preferred for large histories. Default sort is newest first. ### Intent Facets ```bash theme={null} GET /api/v1/intents/facets ``` Returns counts by state, action, chain, vault, and strategy for filter UIs. ### Events and Fills ```bash theme={null} GET /api/v1/intents/{intent_id}/events GET /api/v1/intents/{intent_id}/fills GET /api/v1/fills ``` Use events for lifecycle reconstruction and fills for mined transaction evidence. Intent rows may include nullable fields while OMS execution is still in flight. For example, `completed_at`, `terminal_at`, `gas_used_total`, and fill data may be `null` until execution events and receipts arrive. ## Important Schemas ### StrategyResponse Important fields: * `id` * `vault_id` * `name` * `version` * `state` * `kind` * `indicators` * `rules` * `action_config` * `priority` * `cooldown_ms` * `max_concurrent` ### IntentLogEntry Important fields: * `intent_id` * `execution_id` * `strategy_id` * `vault_id` * `action` * `state` * `source` * `fills_count` * `last_event_seq` * lifecycle timestamps such as `acked_at`, `executing_at`, `completed_at`, `terminal_at` * `error_code`, `error_message` * `total_amount_in`, `total_amount_out` * `gas_used_total` ### IntentFillEntry Important fields: * `fill_id` * `intent_id` * `execution_id` * `chain_id` * `tx_hash` * `amount_in`, `amount_out` * `token_in`, `token_out` * `gas_used` * `fill_time` * `venue` ## Endpoint Inventory | Endpoint | Method | Description | | ---------------------------------------- | --------- | --------------------------------------- | | `/api/v1/strategies` | GET | List strategies | | `/api/v1/strategies` | POST | Create strategy | | `/api/v1/strategies/{id}` | GET | Get strategy | | `/api/v1/strategies/{id}` | PUT | Update strategy | | `/api/v1/strategies/{id}` | DELETE | Archive strategy | | `/api/v1/strategies/{id}/state` | PATCH | Transition state | | `/api/v1/strategies/reorder` | POST | Persist lane order | | `/api/v1/intents` | GET | Search intent projections | | `/api/v1/intents/facets` | GET | Intent facets | | `/api/v1/intents/{intent_id}/events` | GET | OMS event log | | `/api/v1/intents/{intent_id}/fills` | GET | Fills for one intent | | `/api/v1/fills` | GET | Search fills | | `/api/v1/vaults/{vault_id}` | GET/PATCH | Vault engine config and emergency locks | | `/api/v1/engine/status` | GET | Engine status | | `/api/v1/engine/shards/{vault_id}/state` | GET | Live shard state | # Authentication Source: https://docs.superform.xyz/curate/authentication How JWT auth works in SuperVaults: wallet connection, token storage, and programmatic access. ## Auth Flow SuperVaults uses [Dynamic.xyz](https://dynamic.xyz) for wallet connection and JWT issuance. Open [institutions.superform.xyz](https://institutions.superform.xyz) and click **Connect Wallet**. Dynamic.xyz presents a wallet selection modal supporting MetaMask, Rabby, WalletConnect, and others. Your wallet signs a non-transactional message. Dynamic.xyz verifies the signature and issues a JWT tied to your wallet address. The institutional dashboard stores auth state in `localStorage`: * `curator_jwt` — the Bearer token used on all API requests * `curator_address` — connected wallet address * `curator_is_manager` — boolean flag * `curator_vaults` — array of vault addresses you curate The app calls `GET /api/v1/auth/me` on load to fetch your vault roles and confirm the JWT is valid. ## Primary Auth Endpoint ```bash theme={null} GET /api/v1/auth/me Authorization: Bearer ``` Response: ```json theme={null} { "wallet_address": "0xabc...", "is_manager": true, "vaults": [ { "vault_address": "0xdef...", "chain_id": 8453, "role": "primary_manager" } ] } ``` `GET /api/v1/auth/me` is the primary authentication path. It returns the current user with all assigned vault roles. Use this endpoint when building integrations. ## JWT Verification Endpoint ```bash theme={null} GET /api/v1/auth/verify Authorization: Bearer ``` Returns: ```json theme={null} { "verified": true, "walletAddress": "0xabc...", "isManager": true, "vaults": ["0xdef..."] } ``` ## Token Lifecycle * JWTs expire after a session period defined by Dynamic.xyz * Any `401 Unauthorized` from Erebor triggers automatic logout via `authService.handleAutoLogout()` * Reconnecting your wallet re-issues a fresh JWT ## Public Endpoints (No Auth) | Endpoint | Description | | ----------------------------------- | ------------------------------------------- | | `GET /api/v1/public/vaults` | All SuperVault deployments with TVL and APY | | `GET /api/v1/registry/token-assets` | Token asset metadata | ## Programmatic Authentication For automation scripts, generate a JWT without the browser: 1. Sign an EIP-712 auth message with your wallet's private key 2. Submit the signature to Dynamic.xyz to receive a JWT 3. Pass the JWT as `Authorization: Bearer ` on all requests ```python Python theme={null} import requests EREBOR_URL = "https://erebor.superform.xyz" JWT = "" headers = {"Authorization": f"Bearer {JWT}"} resp = requests.get(f"{EREBOR_URL}/api/v1/auth/me", headers=headers) resp.raise_for_status() user = resp.json() print(f"Connected as: {user['wallet_address']}") print(f"Manages {len(user['vaults'])} vault(s)") ``` ```typescript TypeScript theme={null} const EREBOR_URL = "https://erebor.superform.xyz"; const JWT = ""; const headers = { Authorization: `Bearer ${JWT}` }; const resp = await fetch(`${EREBOR_URL}/api/v1/auth/me`, { headers }); if (!resp.ok) throw new Error(`Auth failed: ${resp.status}`); const user = await resp.json(); console.log(`Connected as: ${user.wallet_address}`); console.log(`Manages ${user.vaults.length} vault(s)`); ``` Never hardcode JWTs in source code. Use environment variables (for example, `CURATOR_JWT`) and rotate tokens regularly. JWTs grant full manager-level access to your vaults. # Monitoring Vault State Source: https://docs.superform.xyz/curate/automation/monitoring Poll endpoints to track PPS, service health, and alert conditions for your vault. SuperVaults uses a polling-first monitoring model. No webhook system is implemented yet. Use these intervals as a baseline: | Data | Endpoint | Interval | | -------------------------------- | ------------------------------------------------ | -------- | | Vault state (PPS, pause, supply) | `GET /api/v1/vaults/{chain_id}/{address}` | 15s | | Analytics overview (APY, TVL) | `GET .../analytics/overview` | 60s | | Risk metrics | `GET .../analytics/risk` | 120s | | Service health | `GET /api/v1/vaults/{chain_id}/{vault}/services` | 30s | | Infrastructure health | `GET /health` | 30s | ## Key Metrics ### PPS Staleness ```python theme={null} def check_pps_staleness(vault): remaining = vault.get("remaining_staleness_time", 0) if remaining < 3600: # <1 hour alert("WARNING: PPS staleness — less than 1 hour remaining") if remaining < 600: # <10 minutes alert("CRITICAL: PPS about to expire") ``` ### Service Success Rate ```python theme={null} def check_service_health(services): for svc in services: rate = svc["success_rate"] if rate < 0.80: alert(f"CRITICAL: {svc['name']} success rate {rate:.0%}") elif rate < 0.95: alert(f"WARNING: {svc['name']} success rate {rate:.0%}") ``` ### Redemption Queue Depth ```python theme={null} def check_redemption_queue(executions): pending = [e for e in executions if e["status"] == "pending"] if len(pending) > 10: alert(f"WARNING: {len(pending)} pending redemptions in queue") ``` ## Full Monitoring Loop ```python Python theme={null} import requests import time import logging EREBOR_URL = "https://erebor.superform.xyz" JWT = "" CHAIN_ID = 8453 VAULT = "0xYourVault..." headers = {"Authorization": f"Bearer {JWT}"} log = logging.getLogger(__name__) def fetch_vault_state(): r = requests.get( f"{EREBOR_URL}/api/v1/vaults/{CHAIN_ID}/{VAULT}", headers=headers, timeout=10 ) r.raise_for_status() return r.json() def fetch_service_health(): r = requests.get( f"{EREBOR_URL}/api/v1/vaults/{CHAIN_ID}/{VAULT}/services", headers=headers, timeout=10 ) r.raise_for_status() return r.json() def alert(severity: str, message: str): """Replace with Slack/PagerDuty integration.""" log.warning(f"[{severity}] {message}") def run_checks(): # Vault state try: vault = fetch_vault_state() if vault.get("is_paused"): alert("CRITICAL", f"Vault {VAULT} is paused onchain") remaining = vault.get("remaining_staleness_time", 9999) if remaining < 600: alert("CRITICAL", f"PPS expires in {remaining}s") elif remaining < 3600: alert("WARNING", f"PPS expires in {remaining // 60}min") tvl = vault.get("tvl_usd", 0) log.info(f"TVL: ${tvl:,.0f} | PPS: {vault.get('current_pps')} | Staleness: {remaining}s") except Exception as e: alert("ERROR", f"Failed to fetch vault state: {e}") # Services try: services = fetch_service_health() for svc in services: rate = svc.get("success_rate", 1.0) if rate < 0.80: alert("CRITICAL", f"Service {svc['name']}: {rate:.0%} success rate") elif rate < 0.95: alert("WARNING", f"Service {svc['name']}: {rate:.0%} success rate") except Exception as e: alert("ERROR", f"Failed to fetch service health: {e}") if __name__ == "__main__": logging.basicConfig(level=logging.INFO) while True: run_checks() time.sleep(30) ``` ```typescript TypeScript theme={null} const EREBOR_URL = "https://erebor.superform.xyz"; const JWT = process.env.CURATOR_JWT!; const CHAIN_ID = 8453; const VAULT = "0xYourVault..."; const headers = { Authorization: `Bearer ${JWT}` }; async function fetchWithTimeout(url: string, timeoutMs = 10000) { const controller = new AbortController(); const id = setTimeout(() => controller.abort(), timeoutMs); try { const resp = await fetch(url, { headers, signal: controller.signal }); if (!resp.ok) throw new Error(`${resp.status} ${resp.statusText}`); return resp.json(); } finally { clearTimeout(id); } } function alert(severity: string, message: string) { console.warn(`[${severity}] ${message}`); } async function runChecks() { try { const vault = await fetchWithTimeout( `${EREBOR_URL}/api/v1/vaults/${CHAIN_ID}/${VAULT}` ); if (vault.is_paused) alert("CRITICAL", `Vault ${VAULT} is paused`); const remaining = vault.remaining_staleness_time ?? 9999; if (remaining < 600) alert("CRITICAL", `PPS expires in ${remaining}s`); else if (remaining < 3600) alert("WARNING", `PPS expires in ${Math.floor(remaining / 60)}min`); console.log(`TVL: $${vault.tvl_usd?.toLocaleString()} | Staleness: ${remaining}s`); } catch (e) { alert("ERROR", `Vault state fetch failed: ${e}`); } try { const services = await fetchWithTimeout( `${EREBOR_URL}/api/v1/vaults/${CHAIN_ID}/${VAULT}/services` ); for (const svc of services) { if (svc.success_rate < 0.80) alert("CRITICAL", `${svc.name}: ${(svc.success_rate * 100).toFixed(0)}% success`); else if (svc.success_rate < 0.95) alert("WARNING", `${svc.name}: ${(svc.success_rate * 100).toFixed(0)}% success`); } } catch (e) { alert("ERROR", `Service health fetch failed: ${e}`); } } setInterval(runChecks, 30_000); ``` ## Alert Integrations Replace the `alert()` stub with your notification system: ### Slack ```python theme={null} import httpx def send_slack(message: str): httpx.post( os.environ["SLACK_WEBHOOK_URL"], json={"text": message} ) ``` ### PagerDuty ```python theme={null} import httpx def pagerduty_alert(summary: str, severity: str = "warning"): httpx.post( "https://events.pagerduty.com/v2/enqueue", json={ "routing_key": os.environ["PAGERDUTY_ROUTING_KEY"], "event_action": "trigger", "payload": { "summary": summary, "severity": severity, "source": "supervaults-monitor" } } ) ``` # Building Institutional Automation Source: https://docs.superform.xyz/curate/automation/overview Programmatic auth, safety patterns, and an end-to-end example for automated vault operations. Automated institutional operators monitor vault state 24/7, define rule-based strategies via the Strategy Canvas, pause services on anomaly detection, and execute operations without human latency. SuperVaults supports full programmatic control via the Erebor management API and the Strategy Engine. ## Authentication for Automation Generate a JWT without the browser: 1. Sign an EIP-712 auth message with your wallet's private key 2. Submit the signature to Dynamic.xyz to receive a JWT 3. Pass the JWT as `Authorization: Bearer ` on all requests Verify against Erebor: ```python Python theme={null} import requests EREBOR_URL = "https://erebor.superform.xyz" JWT = "" headers = {"Authorization": f"Bearer {JWT}"} resp = requests.get(f"{EREBOR_URL}/api/v1/auth/me", headers=headers) resp.raise_for_status() user = resp.json() print(f"Authenticated as: {user['wallet_address']}") print(f"Vaults: {[v['vault_address'] for v in user['vaults']]}") ``` ```typescript TypeScript theme={null} const EREBOR_URL = "https://erebor.superform.xyz"; const JWT = process.env.CURATOR_JWT!; const headers = { Authorization: `Bearer ${JWT}` }; const resp = await fetch(`${EREBOR_URL}/api/v1/auth/me`, { headers }); if (!resp.ok) throw new Error(`Auth failed: ${resp.status}`); const user = await resp.json(); console.log(`Authenticated as: ${user.wallet_address}`); console.log(`Vaults: ${user.vaults.map((v: any) => v.vault_address)}`); ``` ## Safety Principles Automated scripts have full manager-level access. A bug can modify live vault configuration. Apply these safeguards before deploying. ### Test on Base First Base (chain ID: 8453) has lower gas costs and faster confirmation times. Validate all automation against Base vaults before Ethereum mainnet. ### Rate Limit Polling | Data | Recommended Interval | | ------------------ | -------------------- | | Vault state | 15s | | Analytics overview | 60s | | Service health | 30s | ### Circuit Breakers Stop automation on consecutive API failures: ```python theme={null} MAX_CONSECUTIVE_ERRORS = 5 consecutive_errors = 0 def safe_api_call(fn): global consecutive_errors try: result = fn() consecutive_errors = 0 return result except Exception as e: consecutive_errors += 1 if consecutive_errors >= MAX_CONSECUTIVE_ERRORS: alert_and_stop(f"Circuit breaker: {consecutive_errors} consecutive errors") raise ``` ### Validate Before Writing Before pushing strategy or configuration changes: 1. Fetch current state 2. Validate the proposed change is meaningful 3. Confirm yield sources are all still whitelisted 4. Apply change 5. Re-fetch and verify the change was applied ## Example: Strategy-Driven Vault Monitoring Fetches vault state from Erebor and checks key health indicators: ```python Python theme={null} import requests import time EREBOR_URL = "https://erebor.superform.xyz" JWT = "" CHAIN_ID = 8453 VAULT_ADDRESS = "0xYourVault..." headers = {"Authorization": f"Bearer {JWT}"} def get_vault_state(): resp = requests.get( f"{EREBOR_URL}/api/v1/vaults/{CHAIN_ID}/{VAULT_ADDRESS}", headers=headers ) resp.raise_for_status() return resp.json() def check_vault_health(): vault = get_vault_state() # Check PPS staleness remaining = vault.get("remaining_staleness_time", 9999) if remaining < 3600: print(f"WARNING: PPS expires in {remaining // 60} minutes") # Check pause status if vault.get("is_paused"): print("CRITICAL: Vault is paused onchain") # Check TVL tvl = vault.get("tvl_usd", 0) print(f"TVL: ${tvl:,.0f} | PPS staleness: {remaining}s") if __name__ == "__main__": while True: try: check_vault_health() except Exception as e: print(f"Error: {e}") time.sleep(60) ``` ```typescript TypeScript theme={null} const EREBOR_URL = "https://erebor.superform.xyz"; const JWT = process.env.CURATOR_JWT!; const CHAIN_ID = 8453; const VAULT_ADDRESS = "0xYourVault..."; const headers = { Authorization: `Bearer ${JWT}` }; async function getVaultState() { const resp = await fetch( `${EREBOR_URL}/api/v1/vaults/${CHAIN_ID}/${VAULT_ADDRESS}`, { headers } ); if (!resp.ok) throw new Error(`Vault fetch failed: ${resp.status}`); return resp.json(); } async function checkVaultHealth() { const vault = await getVaultState(); const remaining = vault.remaining_staleness_time ?? 9999; if (remaining < 3600) { console.warn(`WARNING: PPS expires in ${Math.floor(remaining / 60)} minutes`); } if (vault.is_paused) { console.error("CRITICAL: Vault is paused onchain"); } console.log(`TVL: $${vault.tvl_usd?.toLocaleString()} | Staleness: ${remaining}s`); } setInterval(checkVaultHealth, 60_000); ``` ## Next Steps Poll vault state and build alert conditions. Audit logs as interim event stream. # Notifications and Event Streams Source: https://docs.superform.xyz/curate/automation/webhooks Use notification channels, audit logs, and polling to stream SuperVault operational events. Curate exposes two event surfaces for operators: 1. **Notification channels** for routed operational alerts. 2. **Audit logs and polling** for integration-grade event reconstruction. Use notification channels for humans and incident destinations. Use audit logs / state polling for deterministic integrations. ## Notification Channels Create a channel, then bind it to the vaults that should send alerts. ```bash theme={null} # List or create channels GET /api/v1/notification-channels POST /api/v1/notification-channels # List or bind channels for one vault GET /api/v1/vaults/{chain_id}/{vault_address}/notification-channels POST /api/v1/vaults/{chain_id}/{vault_address}/notification-channels/{channel_id} # Unbind a channel DELETE /api/v1/vaults/{chain_id}/{vault_address}/notification-channels/{channel_id} ``` Recommended alert categories: * Upkeep shortfall or activation loss. * PPS staleness. * Failed keeper runs. * Session-key readiness changes. * Emergency pause / unpause. * Emergency liquidity exit arm / stop. * Merkle generation or publish failure. * Repeated OMS execution failure. See [Alerts](/curate/ui/alerts) for the operator flow. ## Audit Logs as an Event Stream The audit log endpoint records vault operations and manager actions: ```bash theme={null} GET /api/v1/audit/logs Authorization: Bearer # Query params: # ?vault_address={addr}&chain_id={id} # &limit=100&offset=0 # &action_type={type} # &from_timestamp={iso}&to_timestamp={iso} ``` ```json theme={null} { "logs": [ { "id": "uuid", "timestamp": "2026-05-09T10:00:00Z", "action_type": "yield_source_add", "vault_address": "0x...", "chain_id": 8453, "actor_wallet": "0xManager...", "details": { "yield_source": "0xMorphoVault...", "oracle": "0xOracle..." } } ], "total": 48, "has_more": false } ``` Export when you need a point-in-time compliance artifact: ```bash theme={null} GET /api/v1/audit/export Authorization: Bearer ``` ## Polling Pattern ```python theme={null} import requests import time from datetime import datetime, timezone EREBOR_URL = "https://erebor.superform.xyz" headers = {"Authorization": "Bearer "} CHAIN_ID = 8453 VAULT = "0xYourVault..." def poll_audit_logs(since: datetime): resp = requests.get( f"{EREBOR_URL}/api/v1/audit/logs", headers=headers, params={ "vault_address": VAULT, "chain_id": CHAIN_ID, "from_timestamp": since.isoformat(), "limit": 100, }, timeout=15, ) resp.raise_for_status() return resp.json().get("logs", []) last_check = datetime.now(timezone.utc) while True: new_events = poll_audit_logs(since=last_check) for event in new_events: print(f"New event: {event['action_type']} by {event['actor_wallet']}") # Process event... if new_events: last_check = datetime.now(timezone.utc) time.sleep(60) ``` ## State Polling Map | Need | Source | | ------------------- | --------------------------------------------------- | | Vault state changes | `GET /api/v1/vaults/{chain_id}/{address}` | | Service degradation | `GET /api/v1/vaults/{chain_id}/{vault}/services` | | Operation events | `GET /api/v1/audit/logs` | | PPS staleness | Vault detail PPS timestamps and remaining staleness | | Intent execution | `GET /api/v1/intents` and OMS event/fill endpoints | | Emergency locks | `GET /api/v1/engine/shards/{vault_id}/state` | See [Monitoring Vault State](/curate/automation/monitoring) for production-ready polling examples. # Core Concepts Source: https://docs.superform.xyz/curate/concepts The institution role, ERC-7540 vault mechanics, the hook system, merkle authorization, keeper workers, and the DeFiX intent protocol. ## The Institution Role An institution curates a SuperVault's yield strategy: which protocols receive capital, in what order, under what conditions, and with what risk parameters. The institution does not hold depositor funds — the vault contract does. **What institutions control:** yield source selection, oracle assignment, allocation priority, reserve ratio, strategy rules, merkle authorization, fee configuration (at creation), redeem timelock, service controls, emergency pause, and access management. **What the protocol enforces:** merkle-verified execution, timelocked root updates, fee bounds (SuperGovernor), PPS staleness enforcement, \$UP upkeep requirements, high-water mark fee accrual, and protocol revenue share. **Trust model:** Depositors trust the institution to select sound yield sources and maintain vault health. Depositors do NOT need to trust the institution for fund custody (held by contract), honest execution (merkle-verified), accurate PPS (oracle-enforced), or fee limits (SuperGovernor-enforced). ### Institution Access Tiers | Role | On-chain? | Capabilities | | --------------------- | --------- | --------------------------------------------------------- | | **Primary Manager** | Yes | Full vault control. Set at creation, immutable. | | **Secondary Manager** | Yes | All operations except vault settings and user management. | | **View Only** | No | Read-only dashboard access. | The primary manager key is the root of trust for the vault. If compromised, an attacker could modify yield sources, update merkle authorization, and pause operations. Use hardware wallets and consider multi-sig for production vaults. *** SuperVaults combine onchain vault contracts with offchain automation services. Understanding how these layers interact is essential for effective institutional operations. ## ERC-7540 Vault Standard SuperVault contracts are live onchain and fully non-custodial. The institutional dashboard at [institutions.superform.xyz](https://institutions.superform.xyz) is an execution layer — it never holds keys or assets. All vault interactions are direct onchain transactions signed by your wallet. SuperVaults implement ERC-7540, the asynchronous tokenized vault standard. ERC-7540 extends ERC-4626 compatibility (standard deposit/withdraw interface) with asynchronous request-fulfillment flows for deposits and redemptions. **Why async matters:** Cross-chain yield sources, illiquid positions, and redemption timelocks all require time between a user's request and its fulfillment. ERC-7540 formalizes this into a two-phase flow: 1. **Request** — Depositor submits a deposit or redemption request onchain 2. **Fulfillment** — Keeper workers process the request when conditions are met (liquidity available, timelock expired, strategy rules satisfied) Vault shares are ERC-20 tokens representing a depositor's pro-rata claim on vault assets. Price-per-share (PPS) tracks the value of each share in terms of the underlying asset. ## Price-Per-Share (PPS) PPS is the core accounting primitive. It determines: * How many shares a depositor receives for a given deposit * How many underlying assets a depositor receives for redeeming shares * When performance fees accrue (PPS exceeds high-water mark) * Whether the vault is operational (PPS must be updated within the staleness window) | PPS Metric | Description | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Current PPS** | Last onchain recorded value | | **Calculated PPS** | Off-chain computed value (may differ during update intervals) | | **HWM PPS** | All-time high-water mark. Performance fees accrue only above this level. | | **Staleness countdown** | Time remaining before PPS expires. If PPS goes stale, vault operations are blocked until an update is pushed. The staleness window is configurable per vault and defaults to 24 hours. | Validators compute PPS offchain, sign the result as EIP-712 typed data, and any party can submit the update onchain via `ECDSAPPSOracle.updatePPS()`. A PPS update is accepted only after at least two-thirds of weighted validator stake has signed off, with signature, staleness, and deviation checks enforced onchain. ## The Hook System Hooks are onchain modules that keepers invoke to interact with external protocols. Each hook type corresponds to an operation: | Hook Type | Operation | | ------------ | --------------------------------------- | | `DEPOSIT` | Deploy vault assets into a yield source | | `WITHDRAWAL` | Unwind a position from a yield source | | `REBALANCE` | Move capital between yield sources | Hooks are registered in the global Hook Registry with typed parameters (`inspectParams`) that define what arguments each invocation requires: yield source addresses, token addresses, oracle addresses, or freeform values. A keeper can only call a hook if the exact combination of hook address + parameters matches a leaf in the vault's onchain merkle tree. ## Merkle Authorization Merkle authorization is the access control layer for keeper operations. The flow: Create a hook configuration specifying which hooks are authorized with which parameters. Each (hook, params) combination becomes one leaf. Erebor computes a merkle tree from the active configuration. The tree root is a single 32-byte hash that encodes all authorized operations. Submit the new root to the SuperVaultStrategy contract. A timelock period begins. After the timelock expires, execute the root update onchain. Keepers can now use proofs from the new tree. **Why timelocks:** The timelock between proposal and execution gives depositors time to exit if they disagree with an institution's authorization changes. This is a standard DeFi governance safety pattern. When a keeper executes a hook, the onchain contract verifies the merkle proof. If the proof is invalid (the hook + params combination was not authorized), the transaction reverts. SuperVaults use a dual-merkle model for hook authorization. The **global hooks root** is governance-controlled and contains generic hooks without beneficiary-specific arguments. The **strategy hooks root** is proposed by the vault's main manager and contains the full strategy-specific authorization set, including beneficiary-specific hooks. Both update flows use the aggregator's hook-root timelock, which defaults to 15 minutes and can be updated by governance. A hook must be present in either tree to execute. ## Yield Sources and Oracles A yield source is an external protocol contract where the vault deploys capital to generate returns. SuperVaults support two yield source types: | Type | Description | Example | | ----------- | ----------------------- | -------------------------------- | | `erc4626` | Standard ERC-4626 vault | Morpho vaults, Aave wrappers | | `pendle_pt` | Pendle Principal Token | Fixed-rate yield via PT purchase | ERC-7540 yield sources are also on the roadmap as an upcoming yield source type. Each yield source must be paired with an oracle that prices the position back into the vault's underlying asset: | Oracle Type | Pricing Method | | --------------------- | --------------------------------------------------- | | `erc4626` | Share price of the underlying ERC-4626 vault | | `pendle_pt` | Spot market price of the PT | | `pendle_pt_amortized` | Linear decay to par value as PT approaches maturity | Adding or removing yield sources and updating oracle assignments are onchain operations that follow the prepare pattern (Erebor constructs calldata, institution signs). ## Keeper Workers Seven Celery-based keeper workers run continuously against each vault's configuration: | Worker | Responsibility | | --------------------------- | ---------------------------------------------------------------------------------------- | | `fulfill_deposits` | Allocates free vault assets to yield sources based on the configured allocation strategy | | `fulfill_redeems` | Processes pending ERC-7540 redemption requests | | `fulfill_claims` | Claims accumulated reward tokens from external protocols | | `fulfill_swaps` | Swaps non-underlying tokens back to the vault's base asset | | `redemption_queue_engine` | Monitors the subgraph for new redemption requests | | `cleanup_processed_redeems` | Reconciles pending redemptions with onchain state | | `tx_manager` | Broadcasts transactions, monitors confirmations, handles reorgs | Workers execute on configurable intervals. Institutions control: * **Global enable/disable** per vault (stops all workers) * **Per-service pause/resume** (targeted worker control) * **Schedule adjustment** (change execution frequency) ## The Strategy Engine The Strategy Engine adds rule-based automation on top of the keeper system. Institutions define strategies via the Strategy Canvas with: * **Indicators** — named variables evaluated each tick (e.g., `morpho_apy`, `free_assets`) * **Rules** — boolean logic trees (`EXPR`, `AND`, `OR`, `NOT`, `VOTE`) that determine when to act * **Action config** — what to do when rules trigger (deposit size, target hook, slippage limits) * **Conviction config** — how many consecutive ticks must pass before dispatching an intent Strategies evaluate on configurable tick intervals and dispatch **DeFiX intents** to the OMS when conditions are met. ## DeFiX v0.1 Intent Protocol DeFiX is the standardized intent interface between the Strategy Engine and the OMS (Order Management System). An intent represents a desired vault operation: ``` Strategy triggers → Intent submitted to OMS → OMS validates merkle proof → OMS executes onchain ``` Intent lifecycle: | Status | Description | | ------------ | ----------------------------------------- | | `Processing` | Queued or being executed | | `Completed` | Successfully executed onchain | | `Failed` | Execution failed (error details attached) | | `Cancelled` | Cancelled before execution | The OMS handles nonce management, gas estimation, transaction broadcasting, and merkle proof validation. Failed intents include error messages and may be retried by the Strategy Engine depending on the failure type. ## The Prepare Pattern All onchain operations in SuperVaults follow a consistent two-phase flow: 1. **Prepare** — Call a `prepare-*` endpoint on Erebor. It returns raw EVM calldata (`{ to, data, value }`). 2. **Sign** — Pass the calldata to your wallet for signature and broadcast. 3. **Confirm** (where applicable) — Notify Erebor of the transaction hash for indexing. This pattern keeps private keys off the server. Erebor constructs the correct calldata; your wallet controls signing authority. Used for: vault creation, yield source management, user management, merkle root proposals, emergency pause/unpause. # Curation Policy Source: https://docs.superform.xyz/curate/curation-policy Superform's Flagship SuperVault policy covering portfolio construction, risk limits, liquidity, and yield underwriting. ## Overview Flagship SuperVaults are curated to preserve capital, preserve redemption reliability, and earn onchain yield, in that order. They are not for experimental strategies, opportunistic yield chasing, or discretionary style drift. Every position in the policy has to be defensible before entry, monitorable when live, and, depending on sleeve, exitable under stress. If an exposure cannot satisfy those requirements, it does not belong in the portfolio, regardless of quoted APY, incentive support, or recent performance. The governing standard remains conservative, explicit, and enforceable. ### Curation objective and risk profile The objective is repeatable, risk-adjusted yield with capital preservation and redemption reliability ahead of yield maximization. Most capital should remain in exposures with durable economics, clear monitoring, and credible withdrawal paths. Organic yield from lending carry, fixed-rate carry, and similar legible sources should anchor the vault. PT is permitted, but it stays capped and actively monitored. Liquidity remains a live operating constraint. Incentives can contribute, but only as supplemental income. Speculative rewards, points, and unpriced optionality do not support the core policy. Risk has to be underwritten across the full stack: * smart contract quality * audit depth * oracle design and price-source dependence * governance and parameter risk * liquidity depth and withdrawal mechanics * concentration by protocol, curator, market, and collateral type * collateral correlation and shared failure modes * chain-level dependence and operational complexity * clear monitoring and simulation capability ## Vault Construction The vault is composed of three risk sleeves. At all times, the majority of the vault is allocated to the anchor sleeve, which is the lowest-risk and most liquid sleeve. A minority of the vault is allocated to the tactical and PT sleeves, both of which are controlled supplements that can increase the headline yield profile without compromising overall redemption capacity or risk profile. An exposure is Flagship-eligible only if it can be monitored, simulated, and exited with confidence. If any of those fail, the exposure is not eligible. Flagship exposure should come from audited protocols with meaningful production history and operating scale. ### Sleeve Composition | Sleeve | Eligible exposure set | Policy role | Target | Hard cap | | --------------- | ------------------------------------------------------------------------------------ | ------------------------------- | ------ | -------- | | Anchor sleeve | Aave, Morpho, and Yearn vaults that satisfy Superform's anchor underwriting standard | Core allocation sleeve | | 100% | | Tactical sleeve | Fluid, Gearbox, and other non-anchor exposures that satisfy the flagship standard | Additive yield sleeve, not core | | 20% | | PT sleeve | Vanilla PT exposure | Capped satellite sleeve | 10% | 20% | | Free assets | Unallocated assets | | 2% | 100% | **Collateral Constraints** | Constraint | Limit | Notes | | ------------------------------------------------- | ----- | --------------------------------------------- | | Max per smaller unique collateral asset | 10% | Applies even inside otherwise eligible venues | | Algorithmic stablecoin / fragile synthetic dollar | 0% | | **Curator Constraints** | Constraint | Limit | Notes | | --------------- | ----- | ---------------------------------------------------------- | | Max per curator | 50% | No curator should account for more than half the portfolio | **Vault / PT Constraints** | Constraint | Limit | Notes | | ----------------------------------------------------- | ----- | ----------------------------- | | Cross-chain | 0% | Cannot bridge to other chains | | Recursive leverage or structurally amplified exposure | 0% | | ### Anchor Sleeve The anchor sleeve is the default home for capital. It holds the exposures that define the flagship policy. Anchor capital belongs in venues with established production history, live monitoring, durable liquidity, and simple exit mechanics. To qualify for anchor treatment, a venue has to satisfy all of the following requirements on an ongoing basis: * contract, market, and operational risks remain within Flagship Vault's underwriting standard * oracle inputs are observable, bounded, and robust to stale, manipulated, or thin-market pricing * governance rights, upgrade paths, and parameter controls are legible * collateral composition and correlated exposure remain within concentration limits and do not create a single shared failure mode across the sleeve * withdrawal mechanics are reliable enough for anchor sizing * proven history of handling volatile market conditions A venue can be removed from the anchor sleeve if any of the above requirements become unmet or are at risk of becoming unmet. As of the current version of this document, the following venues qualify for inclusion in the anchor sleeve: * Aave * Morpho vaults or Morpho Blue markets that satisfy Flagship Vault's current anchor underwriting standard * Yearn vaults that satisfy Flagship Vault's current anchor underwriting standard ### Tactical Sleeve The tactical sleeve exists to improve portfolio yield without changing the character of the portfolio. Tactical capital is earned, capped, and removable. Fluid and Gearbox are reference tactical venues in this policy. Other non-anchor exposures can enter only if they meet the same flagship standard, including documented audits, sufficient production history, explicit oracle design, and a defensible exit path. Good behavior does not promote a tactical position into anchor. If a position weakens monitoring quality, complicates withdrawals, or becomes difficult to defend at size, it should be removed. ### PT Sleeve The PT sleeve is permitted as a limited satellite allocation and cannot be relied on to support the core portfolio. Vanilla PT is permitted as a capped satellite sleeve. Bespoke or synthetic PT is never treated as core or tactical exposure due to liquidity concerns. It cannot determine whether the portfolio remains liquid, understandable, or defensible under stress. ## Liquidity Policy Sleeve classification and liquidity classification are separate. A position can qualify for the anchor sleeve and still fail prompt liquidity standards. Every position is tagged by current liquidity state. | Liquidity state | Definition | Minimum | Target | Hard cap | | ------------------ | --------------------------------------------------------------------------------------------------------- | -------------------------- | ------- | -------- | | Idle liquidity | Free assets sitting idle | 0.5% | N/A | 100% | | Warm liquidity | Capital that can be withdrawn with action, with high confidence that access is available in the near term | 10% | 40%-60% | 100% | | Lukewarm liquidity | Withdrawable, but likely requires waiting for market conditions or other users unwinding | Monitor, no standalone cap | | | | Term liquidity | Capital that is week-scale, or likely to require weeks to make liquid | N/A | N/A | 20% | Superform maintains a redemption buffer and keeps weighted average withdrawal latency short (≤1 hour) under normal conditions. If conditions deteriorate, Superform cuts tactical sleeves first. The policy does not guarantee instant redemption in all market states; it states the general liquidity profile Flagship SuperVaults should support. Stressed conditions can delay exits. ### Risk controls In addition to the portfolio limits above, Superform applies the following controls: * **Oracle control:** A position must use price sources and update mechanics that Superform can observe and explain. If valuation depends on opaque, stale, thin-market, or governance-adjustable inputs that cannot be monitored in practice, the position is not flagship-eligible. * **Governance control:** A position must have governance, admin, and upgrade surfaces that Superform can map and monitor. If a single governance action can materially alter collateral rules, liquidation behavior, withdrawal access, or oracle configuration without adequate notice or response time, sizing must reflect that risk or the position must be excluded. * **Correlation control:** Anchor and tactical sizing must account for correlated collateral, shared oracle dependencies, shared governance surfaces, and shared liquidation regimes. Separate wrappers or venues do not count as diversification when failure modes are materially the same. ## Operating Rules Within the Flagship policy, curation actions can include: * allocate and rebalance across approved sleeves and eligible venues * enter and exit approved positions * rotate between approved anchor sleeve exposures * rotate among approved tactical sleeve opportunities within hard caps * add or remove individual vanilla PT positions within the PT sleeve cap * claim rewards * realize rewards through approved swap paths, including standard routing venues such as DEX aggregators or routers used for execution quality * hold temporary idle balances as part of normal liquidity management Curation actions may not include: * introduce cross-chain allocation within a Flagship SuperVault * add a new venue without a formal amendment * treat bespoke or synthetic PT as core exposure * rely on incentive marks as if they were fully realizable yield * use unmodeled or weakly monitored exposures because they screen well on APY ### Reward treatment Displayed yield and underwritten yield are not the same. Reporting should distinguish: * organic yield from lending, fixed-rate carry, or other durable economic activity * incentivized yield that depends on token emissions or temporary programs * speculative upside such as points or uncertain future distributions Rewards are discounted by default based on realizable liquidity, execution certainty, and observed monetization paths. Rewards count only if they can be harvested, monetized, and defended as realized return. If that standard is not met, the correct underwriting value is zero. Reward realization may include swapping through approved routing venues as part of ordinary portfolio operations. ### Operating controls At any point, the portfolio must remain explainable across: * how a position exits * what the likely frictions are * what has to go right for current yield to be realized * what smart contract, oracle, governance, and correlation assumptions the position depends on If monitoring quality deteriorates, simulation stops being decision-useful, or exit mechanics become unclear, the position has failed the flagship standard even if headline APY still looks attractive. ## Amendments and Exclusions ### Amendment rules **Formal amendment is required for:** * adding a new venue to the policy * changing the definition of the anchor sleeve or tactical sleeve * changing the scope of assets or chains in policy scope * changing hard caps, sleeve targets, or liquidity limits **Formal amendment is not required for:** * adding new PT positions within the PT sleeve * changing reward treatment or haircut methodology * routine portfolio rebalancing within the approved action surface Current Version: 1.0 # Permissions Model Source: https://docs.superform.xyz/curate/permissions Access boundaries for primary managers, secondary managers, view-only users, registry maintainers, session keys, and keepers. SuperVaults separates protocol ownership from day-to-day operations. The safest mental model is: * **Primary manager** owns the vault. * **Secondary managers** operate the vault. * **View-only users** observe the vault. * **Registry maintainers** maintain global metadata. * **Session keys / keepers** execute narrowly-authorized automation. ## Role Tiers | Role | Onchain? | Scope | Typical Use | | -------------------- | -------------- | --------------------------- | -------------------------------- | | Primary Manager | Yes | Full vault control | Multisig / hardware-backed owner | | Secondary Manager | Yes | Operational controls | PM, operations, risk | | View Only | No | Read access | Analysts, auditors, monitors | | Registry Maintainer | No | Global registry CRUD | Internal registry operations | | Session Key / Keeper | Yes, delegated | Specific executable actions | Strategy and OMS automation | ## Primary Manager The primary manager is set at vault creation and is the highest-authority vault operator. Primary manager responsibilities: * Vault settings and sensitive configuration. * Secondary-manager assignment. * Session-key grant and revoke flows. * Upkeep funding and withdrawal control. * Pause, unpause, and emergency operations. * Merkle root proposal / publish workflows. Treat the primary manager as production infrastructure. Use a multisig or hardware-backed wallet. If the primary manager key is compromised, the attacker can control the vault. ## Secondary Managers Secondary managers are onchain delegates for operational work. They can typically: * Manage strategies. * Configure yield sources. * Work with merkle tree configuration. * Pause/unpause operational services. * Add view-only collaborators. They cannot replace the primary manager or take over primary-only settings. ## View-Only Users View-only access is offchain. It grants dashboard visibility without transaction authority. Use it for: * Risk monitors. * Analysts. * Auditors. * Partners who need vault state without write access. ## Registry Maintainers Registry maintainers manage global metadata shared across vaults, including: * Yield source registry entries. * Oracle registry entries. * Hook registry entries. * Token asset metadata. * Admin backfills. This role is not vault-scoped and should be tightly controlled. ## Session Keys and Keepers Session keys are delegated execution keys used by automation. A session key does not imply broad manager authority; it authorizes a specific automation path. Keepers and OMS execution still depend on: * Active upkeep funding. * Valid session key readiness. * Active merkle root and proofs. * Non-paused vault / services. * Strategy state and emergency locks. If any of those gates fail, automation should stop even when a session key exists. ## Permission Matrix | Action | Primary | Secondary | View Only | Registry Maintainer | Session Key / Keeper | | ------------------------------ | ------- | ------------ | --------- | ------------------- | -------------------- | | View dashboard | ✅ | ✅ | ✅ | — | — | | Create vault | ✅ | — | — | ✅ | — | | Fund upkeep | ✅ | ✅ | ❌ | — | — | | Withdraw upkeep | ✅ | Usually ❌ | ❌ | — | — | | Configure strategies | ✅ | ✅ | ❌ | — | — | | Reorder strategies | ✅ | ✅ | ❌ | — | — | | Manage yield sources | ✅ | ✅ | ❌ | — | — | | Configure hooks / merkle roots | ✅ | ✅ | ❌ | — | — | | Propose or publish merkle root | ✅ | Permissioned | ❌ | — | — | | Grant / revoke session key | ✅ | Usually ❌ | ❌ | — | — | | Pause vault | ✅ | ✅ | ❌ | — | — | | Arm emergency exit | ✅ | ✅ | ❌ | — | — | | Add/remove secondary managers | ✅ | ❌ | ❌ | — | — | | Add/remove view-only users | ✅ | ✅ | ❌ | — | — | | Registry CRUD | — | — | — | ✅ | — | | Execute authorized hooks | — | — | — | — | ✅ | ## API Patterns ```bash theme={null} # List vault users GET /api/v1/users?vault_address={address}&chain_id={chainId} # Prepare add secondary manager transaction POST /api/v1/users/secondary-manager/prepare # Prepare remove secondary manager transaction POST /api/v1/users/secondary-manager/remove/prepare # Add view-only user offchain POST /api/v1/users/view-only # Remove view-only user offchain DELETE /api/v1/users/view-only/{wallet} ``` A `403 Forbidden` means the connected wallet lacks the required role for that operation. # Prerequisites and Access Source: https://docs.superform.xyz/curate/prerequisites What you need before you can curate a SuperVault: wallet, role, and API access. ## Wallet Requirements SuperVaults uses [Dynamic.xyz](https://dynamic.xyz) for wallet connection and JWT issuance. * **EVM-compatible wallet** (MetaMask, Rabby, Frame, or any WalletConnect-compatible wallet) * **Native gas token** for each chain where you operate vaults * **Manager role** assigned on at least one vault before institution features are accessible View-only users are added offchain and do not require a wallet connection. All write operations require at minimum a secondary manager role. ## Supported Chains | Chain | Chain ID | Status | | ---------------- | -------- | ------ | | Ethereum Mainnet | 1 | ✅ Live | | Base | 8453 | ✅ Live | | HyperEVM | 999 | ✅ Live | | Flare | 14 | ✅ Live | The SuperVault contract architecture supports additional chains. Contact the Superform team for expansion requests. ## Institutional Dashboard | Environment | URL | | ----------- | ---------------------------------------------------------------- | | Production | [institutions.superform.xyz](https://institutions.superform.xyz) | ## API Base URLs Each backend service exposes its own base URL. Use these when building automation: | Service | Base URL | OpenAPI Spec | | --------------------------- | -------------------------------- | ------------ | | **Erebor** (management API) | `https://erebor.superform.xyz` | ✅ Available | | **Strategy Engine** | `https://strategy.superform.xyz` | ✅ Available | | **OMS** (DeFiX intents) | `https://oms.superform.xyz` | ✅ Available | ## Role Requirements by Page | Page | Minimum Role | | --------------- | ----------------------------------------------------- | | Home | Public (no auth) | | Dashboard | Manager | | Strategy | Manager | | Yield Sources | Manager | | Merkle Trees | Manager | | User Management | Primary Manager | | Vault Settings | Primary Manager | | Pause | Manager | | Registry | Registry Maintainer | | Admin | Registry Maintainer | | Create Vault | Manager (you become primary manager of the new vault) | | Alerts | Manager | ## Getting Access **Joining an existing vault:** The primary manager adds you via [User Management](/curate/ui/user-management) as a secondary manager (onchain transaction) or view-only user (offchain). **Creating a new vault:** Any wallet with manager access can create a vault and automatically becomes its primary manager. JWT auth flow, token storage, and programmatic access. # Quickstart Source: https://docs.superform.xyz/curate/quickstart Create, list, activate, and operate a SuperVault end to end. This guide follows the curator path from vault deployment to a live Superform listing. The launch path is complete when the vault is indexed, funded for upkeep, authorized with hooks, connected to yield sources, backed by at least one strategy, and listed as available in the Superform catalog. Prerequisites: manager access, connected EVM wallet, gas on the deployment chain, provider or institution profile, and the token/yield-source details for the vault. Open **Create Vault** (`/vaults/create`). The editable wizard has four steps: 1. **Basic Information** — select chain, provider, underlying asset, listing metadata, and the computed onchain name/symbol. 2. **Managers** — assign the primary manager and optional secondary managers. 3. **Fees** — configure fee recipient, performance fee, management fee, PPS timing, and redemption timing. 4. **Review** — inspect the full configuration before the transaction phase. After review, the modal moves through transaction signing, indexing, optional yield-source setup, and completion. See [Create Vault](/curate/ui/create-vault). Open **Setup**. Treat it as the canonical go-live checklist: * Vault is created and readable. * Upkeep is staked so automation can run. * Hooks are configured in the active root config. * Yield sources are whitelisted. * At least one strategy exists. * The vault is listed and available in the Superform catalog. The checklist coordinates operational readiness and public discovery. See [Setup Checklist](/curate/ui/setup-checklist). Open **Vault Settings → Upkeep**. Fund the required upkeep balance. Erebor marks the vault active and discoverable once the funding gate is satisfied. Without upkeep, operator pages can still show the vault by direct link, but keeper automation and discoverability remain gated. Open **Merkle Trees** and follow the manager-root flow: 1. Draft a root config. 2. Activate the config. 3. Generate the merkle tree. 4. Have the primary manager propose and publish the root onchain. Keepers and OMS intents need active merkle proofs for each hook/parameter combination they execute. Open **Yield Sources**. Add each target yield source with its paired oracle. Bulk add, bulk remove, and bulk oracle update flows prepare a single transaction for multiple rows. Emergency-exit readiness is tied to the whitelist: each whitelisted yield source can have a paired `EMERGENCY_EXIT` strategy used by the Pause page to drain that source during an incident. Open **Strategy Canvas**. Create user-defined strategies for deposits, withdrawals, and rebalances. Strategy priority is server-managed: use drag-to-reorder, which persists through `POST /api/v1/strategies/reorder`. Create and update requests do not carry `priority`. Before a strategy can publish intents, the OMS session key must be ready and the active merkle tree must contain the required withdrawal/deposit hooks. Open the catalog/listing flow. Confirm provider IDs, visibility, availability, category, description, and external URL when relevant. Onchain created means the vault contract exists. Listed and available means the vault is discoverable in Superform surfaces. See [Catalog Listing](/curate/ui/catalog-listing). Use **Dashboard** for vault health, **Intent History** for execution traces, and **Pause** for incident controls. Intent History maps Strategy Engine rows to OMS events, fills, gas usage, mined timestamps, and hook transaction details. ## Go-live checklist * [ ] Vault contract created, indexed, and readable in dashboard data * [ ] Upkeep funded and activation gate cleared * [ ] Active hook config exists * [ ] Merkle tree generated, proposed onchain by the primary manager, and published * [ ] Yield sources whitelisted with correct oracles * [ ] At least one strategy exists and intended strategies are `RUNNING` * [ ] OMS session key readiness is green * [ ] Catalog listing is `listed` and `available` * [ ] Intent History shows expected execution or no unexpected failures # Admin: Backfill & Maintenance Source: https://docs.superform.xyz/curate/ui/admin Internal data maintenance tools for backfilling APY, TVL, and event history for vaults. The Admin page provides data backfill utilities for Erebor's historical analytics. Use these when vault data is missing from the analytics database: after onboarding a vault with existing onchain history, or after a data pipeline incident. Backfill operations are long-running (up to 120s per request). The API client timeout is overridden accordingly. Requires `registry_maintainer` permission. ## Backfill Types ### APY Backfill Recomputes and stores APY snapshots for a vault over a specified date range. Max 90 days. ```bash theme={null} POST /api/v1/admin/backfill/apy { "chain_id": 8453, "vault_address": "0x...", "days": 30 } ``` Response: ```json theme={null} { "snapshots_updated": 28, "snapshots_failed": 2, "snapshots_skipped": 0, "errors": ["2025-01-03: missing PPS data for this date"] } ``` ### TVL Backfill Recomputes and stores TVL snapshots. Max 90 days. ```bash theme={null} POST /api/v1/admin/backfill/tvl { "chain_id": 8453, "vault_address": "0x...", "days": 30 } ``` Response: ```json theme={null} { "days_backfilled": 29, "days_failed": 1, "days_requested": 30, "errors": ["2025-01-15: RPC error fetching block"] } ``` ### Events Backfill Re-indexes deposit, withdrawal, and other onchain events. Max 365 days. ```bash theme={null} POST /api/v1/admin/backfill/events { "chain_id": 8453, "vault_address": "0x...", "days": 90 } ``` Response: ```json theme={null} { "deposits_found": 142, "withdrawals_found": 38, "events_created": 178, "events_skipped_duplicate": 2 } ``` ## Using the UI 1. Select backfill type (APY, TVL, or Events) 2. Select chain and vault 3. Choose days (presets: 7, 30, 90; Events adds 365) 4. Click **Run Backfill** and wait for results (up to 120s) 5. Review the results summary (updated/failed/skipped counts) Backfills can spike database load. Run during off-peak hours. Do not run concurrent backfills for the same vault. ## API Reference | Endpoint | Method | Timeout | Description | | ------------------------------- | ------ | ------- | ------------------------------ | | `/api/v1/admin/backfill/apy` | POST | 120s | APY snapshots (max 90 days) | | `/api/v1/admin/backfill/tvl` | POST | 120s | TVL snapshots (max 90 days) | | `/api/v1/admin/backfill/events` | POST | 120s | On-chain events (max 365 days) | # Alerts Source: https://docs.superform.xyz/curate/ui/alerts Notification channels and operational alert delivery for SuperVaults. Alerts connect vault events to notification destinations. The shipped flow centers on notification-channel creation, listing, and vault binding. Treat alert delivery as operational plumbing: channel setup first, event history and routing second. ## Notification channels A notification channel defines where Erebor can send vault events. | Field | Description | | --------------------------- | -------------------------------------------------------------------------------- | | `provider` | Channel provider or transport. | | `name` | Operator-facing channel name. | | `config` | Provider-specific destination config. Sensitive values should be redacted in UI. | | `created_by` | Creator metadata. | | `created_at` / `updated_at` | Audit timestamps. | ## Channel endpoints | Endpoint | Method | Description | | ------------------------------------------------------------------------------ | ------ | ---------------------------------------- | | `/api/v1/notification-channels` | GET | List channels available to the operator. | | `/api/v1/notification-channels` | POST | Create a channel. | | `/api/v1/vaults/{chain_id}/{vault_address}/notification-channels` | GET | List channels bound to a vault. | | `/api/v1/vaults/{chain_id}/{vault_address}/notification-channels/{channel_id}` | POST | Bind a channel to a vault. | | `/api/v1/vaults/{chain_id}/{vault_address}/notification-channels/{channel_id}` | DELETE | Unbind a channel. | ## Operator flow 1. Create a channel with a clear name. 2. Bind it to the relevant vault. 3. Trigger or wait for a low-risk event to confirm delivery. 4. Keep at least one secondary notification path for high-value vaults. ## Events to monitor Common alert categories include upkeep shortfall, PPS staleness, failed keeper runs, session-key readiness, emergency pause/unpause, emergency liquidity exit arm/stop, merkle publish failures, and repeated OMS execution failures. ## Troubleshooting | Symptom | Check | | ----------------------------------- | -------------------------------------------------------- | | Channel exists but receives nothing | Confirm it is bound to the vault. | | Sensitive destination appears in UI | Redact config display and rotate destination if leaked. | | Alerts are noisy | Tune event thresholds before muting the channel. | | Incident alert missing | Check audit logs and notification-channel binding first. | # Catalog Listing Source: https://docs.superform.xyz/curate/ui/catalog-listing Make an onchain SuperVault visible and available in Superform catalog surfaces. Catalog listing connects an onchain SuperVault to Superform discovery. A vault can be created, indexed, and manager-visible without being listed to users. ## State model | Layer | Meaning | | --------------------- | ------------------------------------------------------------------------ | | **Onchain vault** | Contract exists on the deployment chain. | | **Managed vault** | Erebor can read it and managers can operate it. | | **Catalog listing** | Persephone/Superform catalog knows how to display and route users to it. | | **Available listing** | The listing is both visible and available for user flows. | ## Listing fields | Field | Description | | ---------------------- | ----------------------------------------------------------------------------------------- | | `provider_ids` | Provider or institution IDs associated with the vault. | | `category` | Catalog category, commonly `yield`. | | `visibility` | Whether the vault is listed or hidden from discovery. | | `availability` | Whether the listed vault is available to users. Non-superadmins cannot set `unavailable`. | | `external_url` | Optional external detail page or provider URL. | | `name` / `description` | User-facing catalog copy. | | `yield_source_ids` | Optional catalog yield-source associations. | Default catalog inputs are `visibility: listed` and `availability: available` when the role allows it. ## Unlisted SuperVaults The **Unlisted SuperVaults** panel finds vaults you manage that are not in the catalog. Adding one pre-fills chain, address, provider IDs, category, visibility, availability, and external URL where known. ## Operational invariant Do not treat deployment as launch. Launch requires: 1. Onchain creation and indexing. 2. Upkeep-funded activation. 3. Hook and merkle proof readiness. 4. Yield sources and strategy setup. 5. Catalog row set to listed and available. The [Setup Checklist](/curate/ui/setup-checklist) is the canonical place to verify launch readiness. # Create Vault Source: https://docs.superform.xyz/curate/ui/create-vault Deploy a SuperVault, attach listing metadata, and move from onchain creation to launch-ready operations. The Create Vault flow (`/vaults/create`) deploys a new SuperVault and captures the metadata needed to list it in the Superform catalog. Creation is intentionally split into two phases: 1. Editable configuration before anything is signed. 2. Operational setup after the vault exists onchain. Treat creation as the start of launch readiness: finish upkeep, authorization, yield-source, strategy, and catalog setup before routing depositor flows. ## Before You Start You need: * A wallet with registry-maintainer access to the institutional dashboard. * The underlying token available in the token registry, or enough metadata to register it. * An approved provider / institution record for the vault's listing metadata. * The primary manager wallet that will own vault-level controls. * Fee recipient and fee parameters. ## Editable Wizard Steps The wizard has four editable steps. You can move back through these steps until you enter the transaction phase. ### 1. Basic Information Basic Information combines the old chain, asset, identity, and listing screens into one step. Configure: * **Chain** — the deployment network. This is permanent after deployment. * **Provider / institution** — the approved managed provider associated with the vault. * **Underlying asset** — the token users deposit into the vault. You can select a registered token or register a custom token. * **Listing metadata** — friendly name, description, category, and provider IDs used by the catalog/listing layer. * **Onchain identity** — name and symbol. By default these are computed from institution + asset, with an optional manual override. The onchain vault name and symbol are protocol identity. The friendly name, category, provider IDs, and description are listing metadata used by Superform distribution surfaces. ### 2. Managers Set the primary manager and optional secondary managers. * The **primary manager** owns vault settings, user management, session keys, and emergency authority. * **Secondary managers** can operate the vault but cannot replace the primary manager or change primary-only settings. For production vaults, use a hardware-backed wallet or multisig as the primary manager. ### 3. Fees Configure: * Performance fee. * Management fee. * Fee recipient. * PPS timing parameters. * Optional redeem timelock. Fee bounds and protocol constraints are read from protocol configuration. The dashboard prepares valid calldata; your wallet signs the resulting transaction. ### 4. Review Review everything before entering the transaction phase: * Chain and underlying asset. * Provider and listing metadata. * Onchain name and symbol. * Managers. * Fees and timing parameters. After this point, the wizard moves into signing and indexing. ## Transaction and Indexing Phases ### Sign the Creation Transaction The dashboard calls Erebor to prepare calldata: ```bash theme={null} POST /api/v1/vaults/create/prepare ``` Your wallet signs and submits the transaction onchain. Erebor does not execute the transaction for you. ### Confirm and Index After the transaction is broadcast, the dashboard confirms the hash: ```bash theme={null} POST /api/v1/vaults/create/confirm ``` Erebor creates an indexing job and the UI polls: ```bash theme={null} GET /api/v1/vaults/create/jobs/{job_id} ``` Statuses are `pending`, `running`, `completed`, or `failed`. During indexing, the dashboard also submits the listing metadata to the catalog layer when the vault address is resolved. If catalog submission fails, the vault can still exist onchain; complete listing from the distribution/listing flow. `409 Conflict` on confirm means a creation job already exists for that transaction hash. Check the existing job before retrying. ## Post-Create Setup Once the vault is indexed, complete the launch checklist: 1. Vault exists onchain and appears in dashboard data. 2. Upkeep is funded so the vault becomes active and discoverable. 3. Hooks are configured in the active merkle root config. 4. Yield sources are whitelisted and paired with oracles. 5. At least one strategy exists for the vault. 6. The vault is listed and available in the Superform catalog. See [Setup Checklist](/curate/ui/setup-checklist) for the canonical readiness flow. ## API Reference | Endpoint | Method | Description | | --------------------------------- | ------ | --------------------------------- | | `/api/v1/registry/token-assets` | GET | Browse registered token assets | | `/api/v1/registry/token-assets` | PUT | Register or upsert a custom token | | `/api/v1/vaults/create/prepare` | POST | Prepare creation calldata | | `/api/v1/vaults/create/confirm` | POST | Confirm creation transaction hash | | `/api/v1/vaults/create/jobs/{id}` | GET | Poll indexing job status | ### PrepareCreateVaultRequest ```json theme={null} { "chain_id": 8453, "asset": "0xUSDC...", "name": "Provider USDC SuperVault", "symbol": "proUSDC", "main_manager": "0xPrimaryManager...", "secondary_managers": [], "min_update_interval": 3600, "max_staleness": 86400, "fee_config": { "performance_fee_bps": 1000, "management_fee_bps": 200, "recipient": "0xFeeRecipient..." } } ``` ### ConfirmCreateVaultRequest ```json theme={null} { "tx_hash": "0xabc...", "chain_id": 8453, "vault_config": { "redeem_timelock_seconds": 0 } } ``` # Dashboard Source: https://docs.superform.xyz/curate/ui/dashboard 9-section vault monitoring dashboard covering TVL, PPS, fees, risk, users, operations, and the Zap Terminal. SuperVaults Vault Dashboard — monitoring panel with sidebar tabs for Overview, Allocation, PPS & Pricing, Fees & Upkeep, Managers, Performance, Risk, Users, Operations The Dashboard is the primary monitoring view for a single vault. Nine sidebar sections cover every dimension of vault health: financial state, allocation composition, pricing, fee accrual, access control, performance history, risk metrics, depositor analytics, and operational throughput. Select your chain and vault from the top dropdowns. Financial data refreshes every 15 seconds from `GET /api/v1/vaults/{chain_id}/{vault_address}` plus Erebor analytics sub-endpoints. ## 1. Overview Top-level vault health metrics: | Metric | Description | | ------------------- | --------------------------------------------------------------------------- | | TVL (USD) | Current total value locked | | Current PPS | Last onchain recorded price-per-share | | Calculated PPS | Off-chain computed PPS (may differ during update intervals) | | HWM PPS | All-time high-water mark. Performance fees accrue only above this level. | | Total Supply | Outstanding vault shares | | Free Assets | Assets in vault not deployed to yield sources | | Escrowed Assets | Assets pending cross-chain redemptions | | Pause Status | Whether vault strategy execution is paused | | \$UP Upkeep | Upkeep token balance and payment status | | Staleness Countdown | Time until PPS expires. Client-calculated from `last_pps_update_timestamp`. | ## 2. Allocation Pie chart showing current distribution of vault assets across yield sources. Each slice represents one whitelisted yield source with its allocation amount and percentage of total. ## 3. PPS & Pricing PPS history chart (time series from `analytics/pps`). Current, calculated, and HWM values displayed alongside staleness countdown with visual indicator. Min/max update intervals from SuperGovernor config. ## 4. Fees & Upkeep | Field | Description | | ----------------------- | ----------------------------------------------------------------- | | Management Fee | Annualized bps on TVL | | Performance Fee | Bps on yield above HWM | | Pending Performance Fee | Total accrued, Superform share, recipient share, profit since HWM | | Can Skim | Whether pending fees are claimable now | Fee configuration data comes from the vault detail endpoint (`GET /api/v1/vaults/{chain_id}/{address}`) in the `fee_config` field. Fee history is derived from vault analytics and audit log data. ## 5. Managers Lists all wallet addresses with roles on this vault: primary manager, secondary managers, view-only users. ## 6. Performance * APY overview: current, 7-day, 30-day from `analytics/overview` * APY time series chart with selectable periods * TVL time series chart with selectable periods * 24h and 7d TVL change ## 7. Risk | Metric | Description | | ------------------- | ------------------------------------------ | | Risk Score | Composite 0-100 score | | Risk Level | Low / Medium / High classification | | Concentration Score | Exposure to any single yield source | | Max Drawdown (30d) | Worst peak-to-trough PPS decline | | Sharpe Ratio | Risk-adjusted return | | Volatility (Daily) | Daily PPS standard deviation | | Top Holder % | Share of TVL held by the largest depositor | Risk data has a 2-minute stale time. ## 8. Users From `analytics/users`: total users, active users (24h/7d/30d), new users (24h/7d/30d), churned users, retention rate (7d/30d), average deposit size. Includes retention cohort table and top holders table. ## 9. Operations From `analytics/operations`: transaction count, deposit and withdrawal counts, gas costs (average and total), slippage (average and max), net flow. Deposit/withdraw volume chart and transaction history table. ## Zap Terminal The Zap Terminal opens as a modal overlay providing AI-powered vault analysis: a narrative covering performance, risk, yield sources, fees, and liquidity. Also includes a curated social feed and WebSocket-based AI chat for vault Q\&A. The Zap Terminal analysis endpoint (`GET /api/v1/vaults/{vaultAddress}/ai/analysis?chain_id={}`) is operational and used by the Zap Terminal. ## API Endpoints | Endpoint | Stale Time | | ---------------------------------------------- | ---------- | | `GET /api/v1/vaults/{chain_id}/{address}` | 15s | | `GET .../analytics/overview` | 15s | | `GET .../analytics/pps` | 15s | | `GET .../analytics/performance` | 15s | | `GET .../analytics/performance/apy?period={p}` | 15s | | `GET .../analytics/performance/tvl?period={p}` | 15s | | `GET .../analytics/risk` | 2min | | `GET .../analytics/risk/concentration` | 2min | | `GET .../analytics/risk/drawdown` | 2min | | `GET .../analytics/risk/volatility` | 2min | | `GET .../analytics/users` | 30s | | `GET .../analytics/users/retention` | 30s | | `GET .../analytics/users/top` | 30s | | `GET .../analytics/users/cohorts` | 30s | | `GET .../analytics/operations` | 30s | | `GET .../analytics/operations/transactions` | 30s | | `GET .../analytics/operations/volume` | 30s | | `GET .../analytics/social` | 60s | | `GET .../analytics/export` | on-demand | # Emergency Liquidity Exit Source: https://docs.superform.xyz/curate/ui/emergency-exit Arm or stop per-yield-source emergency drains from Pause Operations. Emergency Liquidity Exit lets a manager drain one whitelisted yield source without pausing the entire vault. It is an operator-armed incident control, not an automatic strategy and not a replacement for the onchain vault pause. ## Core invariant Each whitelisted yield source can have exactly one paired `EMERGENCY_EXIT` strategy. Arming it transitions that strategy to `RUNNING`, acquires an emergency lock keyed by `target_address`, and starts withdrawal intents for that source. While the lock is held, user-defined strategies with the same `action_config.target_address` stop publishing conflicting intents. Strategies targeting other yield sources continue. ## When to use it Use Emergency Exit when a single yield source needs liquidity drained because of protocol risk, oracle issues, market dislocation, or operational concerns. Do not use it for broad vault shutdown. Use onchain pause or keeper pause when every operation should stop. ## Prerequisites * Yield source is whitelisted for the vault. * Active merkle withdrawal hook and proof exist for that source. * OMS session key and Strategy Engine are healthy. * Operator has manage permission. * Vault kill switch is not blocking strategy execution. ## Arm flow 1. Open **Pause → Emergency Exit**. 2. Select a yield-source row. 3. Review allocation, APY/TVL where available, paired strategy state, and blocked strategies. 4. Type the required confirmation suffix from the yield-source address. 5. Set max slippage in bps. Default is **50 bps (0.5%)**. 6. Add an optional reason. The reason remains visible in lock banners and audit context. 7. Click **Arm emergency exit**. 8. Monitor [Intent History](/curate/ui/intent-history) for withdrawal intents, fills, gas, and errors. If no paired emergency strategy exists, the UI creates and arms one in the same flow. If another operator arms the same target first, the request returns a conflict and the UI asks you to refresh. ## Stop flow Click **Stop emergency exit** on the source. This transitions the paired strategy to `IDLE` and releases the lock. User-defined strategies targeting that address resume on their next tick. Already-published OMS withdrawal intents continue to their terminal state. ## Strategy Canvas behavior When one or more emergency locks are active, Strategy Canvas shows an emergency banner. Controls for user-defined strategies that target a locked address are disabled with a tooltip. `EMERGENCY_EXIT` strategies are system-managed, hidden from normal authoring by default, and read-only when inspected. ## Yield-source whitelist side effects | Action | Side effect | | ------------------- | ------------------------------------------------------------------------- | | Whitelist add | Provisions a paired `EMERGENCY_EXIT` withdrawal strategy for that target. | | Whitelist remove | Archives the paired emergency strategy when no lock is active. | | Remove while locked | Blocked until the drain is stopped and the lock clears. | ## Failure states | State | Meaning | | --------------------------- | ------------------------------------------------------------------------------------ | | No withdrawal hook or proof | Active merkle tree does not authorize the drain. Configure hooks and publish proofs. | | `EMERGENCY_LOCK_ACTIVE` | Another emergency drain already holds the target lock. | | Duplicate target | A paired emergency strategy already exists for that vault/target. | | Read-only banner | Current operator can inspect but cannot arm or stop drains. | | Lock data unavailable | UI fails closed and blocks controls until lock state loads. | # Home Source: https://docs.superform.xyz/curate/ui/home The global overview page showing total TVL, vault count, best APY, and a filterable vault list. The **Home** page (`/`) is the top-level entry point at [institutions.superform.xyz](https://institutions.superform.xyz). It provides a global overview of all SuperVault deployments. No authentication required. Data refreshes from `GET /api/v1/public/vaults` (Erebor) with a 5-minute stale time. ## Overview Cards Three summary cards appear at the top of the page: | Card | Description | | ---------------- | -------------------------------------------------- | | **Total TVL** | Aggregate USD value locked across all vaults | | **Total Vaults** | Number of deployed SuperVaults | | **Best APY** | Highest current annualized yield across all vaults | ## Vault List Below the overview cards, a searchable and filterable vault list displays every deployed SuperVault: | Field | Description | | ---------------- | ------------------------------------------ | | Vault Name | Institution-defined name | | Underlying Asset | Token depositors supply (USDC, WETH, etc.) | | TVL (USD) | Total value locked | | APY | Current annualized yield | | Chain | Ethereum or Base | ### Filtering and Search | Control | Options | | ------------ | --------------------------------- | | Chain filter | All, Ethereum (1), Base (8453) | | Search | Text match by vault name or asset | Clicking a vault navigates to its [Dashboard](/curate/ui/dashboard) under the **Curate** section at `/dashboard?chainId={id}&vaultAddress={addr}`. ## Navigation Context Home is one of three top-level sections in the institutional dashboard: | Section | Path | Purpose | | ----------------- | ------------ | --------------------------------------------------------------------------------------- | | **Home** | `/` | Global overview — TVL, vault count, APY, vault list | | **Curate** | `/dashboard` | Per-vault curation — dashboard, strategy, merkle trees, yield sources, settings, alerts | | **Institutional** | `/manage` | Institutional operations — deployers, providers, vault registry, and approval queues | ## API Reference | Endpoint | Method | Auth | Description | | ------------------------------- | ------ | ---- | --------------------------------------- | | `/api/v1/public/vaults` | GET | None | All vault deployments with TVL/APY | | `/api/v1/registry/token-assets` | GET | None | Token metadata (symbol, decimals, logo) | ```json theme={null} // GET /api/v1/public/vaults — response shape [ { "vault_address": "0x...", "escrow_address": "0x...", "chain_id": 8453, "name": "USDC Yield Vault", "symbol": "svUSDC", "vault_share_decimals": 18, "underlying_asset_symbol": "USDC", "underlying_asset_decimals": 6, "underlying_asset_address": "0x...", "tvl_usd": 1250000.00, "apy": 0.0847 } ] ``` Token metadata is fetched separately via `GET /api/v1/registry/token-assets?chain_id={}&addresses={}` and cached for the session (infinite stale time). # Intent History Source: https://docs.superform.xyz/curate/ui/intent-history Trace Strategy Engine intents through OMS events, fills, gas, and execution status. Intent History is the operator-facing execution explorer for Strategy and OMS activity. Use it to answer: what strategy fired, where the intent is in the lifecycle, what filled, what transaction mined, and why execution failed. ## What it shows | View | Use it for | | --------------- | ------------------------------------------------------------------------ | | Intent list | Search and filter intents across vaults and strategies. | | Facets | Count by state, action, chain, vault, and strategy. | | Lifecycle strip | See the current OMS lifecycle state and timestamps. | | Fills table | Inspect per-fill venue, token in/out, amounts, gas, fees, and fill time. | | OMS events | Audit message type, sequence, payload, and source. | | Signal snapshot | See the Strategy Engine signal context that produced the intent. | ## Lifecycle states The explorer uses the OMS lifecycle vocabulary: `RECEIVED`, `ACKED`, `STAGED`, `PLANNED`, `EXECUTING`, `COMPLETED`, `ABORTED`, `EXPIRED`, `FAILED`, `CANCELED`, and `UNKNOWN` for rows that cannot be normalized. ## How it maps to OMS The Strategy Engine projects OMS events and backfills into `strategy_intent_log`. Each row can include: * `intent_id`, `execution_id`, `correlation_id` * strategy and vault IDs * last event sequence and message type * ack/executing/completed/terminal timestamps * error code and message * total amount in/out * total gas used * fills count * signal snapshot and summary * source: `engine`, `nats`, or `backfill` ## API mapping | Endpoint | Purpose | | ---------------------------------------- | ------------------------- | | `GET /api/v1/intents` | Global intent search. | | `GET /api/v1/intents/facets` | Facet counts for filters. | | `GET /api/v1/intents/{intent_id}` | Intent detail. | | `GET /api/v1/intents/{intent_id}/fills` | Fills for one intent. | | `GET /api/v1/intents/{intent_id}/events` | OMS event audit rows. | | `GET /api/v1/fills` | Global fill search. | ## Common reads | Symptom | Check | | ------------------------------------------------ | --------------------------------------------------------------------------------------------------- | | Intent never mined | Look at `EXECUTING` events, hook transaction fields, and gas budget. | | Intent completed with no visible position change | Check fills and amount out fields; some fields can be `null` until OMS hydration completes. | | Strategy appears blocked | Check emergency locks, kill switch, session-key readiness, and merkle proof availability. | | Duplicate-looking rows | Compare `intent_id`, `execution_id`, and `correlation_id`; retries and backfills can share context. | # Institutional Source: https://docs.superform.xyz/curate/ui/manage Admin-level registry view for browsing all vaults, operating registry workflows, and provider administration. SuperVaults Institutional Vaults — full registry table with chain filter, search, and Add Vault actions The Institutional section is the admin-level registry view, accessible from the top navigation bar. It provides a comprehensive view of all registered SuperVaults across chains, vault deployment workflows, and provider administration. *** ## Vaults **Route:** `/manage` The primary Institutional view is the full vault registry table. All authenticated users can browse the complete list of registered vaults. ### Registry Table The table displays all registered SuperVaults (576+ as of March 2026) with the following columns: | Column | Description | | ------------ | ------------------------------------------------- | | Name | Vault display name | | Chain | Deployment chain (e.g., Ethereum, Base, Arbitrum) | | Symbol | Vault token symbol | | Providers | Number of yield source providers configured | | TVL | Total value locked in USD | | APY | Current annualized percentage yield | | Availability | Public / Private vault status | | Created | Vault creation timestamp | ### Filtering and Search * **Chain filter** — dropdown to filter vaults by deployment chain * **Search** — search by vault name or contract address All columns are sortable. Click a column header to sort ascending/descending. ### Actions | Action | Description | | ---------------- | ------------------------------------------------------------------------ | | **Add Vault** | Register a new vault in the registry. Opens the vault registration flow. | | **Add Provider** | Register a new yield source provider. | *** ## My Vaults **Route:** `/manage/my-vaults` The vault deployment and approval queue for registry maintainers. This sub-page shows vaults you've deployed or submitted for registry inclusion, along with their approval status. My Vaults requires the `registry_maintainer` role. Users without this role will see an **Access Denied** message. *** ## Providers **Route:** `/manage/providers` Provider operations for registry maintainers, organized into two sub-tabs: | Tab | Description | | ---------------- | ---------------------------------------------------------- | | **My Providers** | Providers you've registered, with edit and status controls | | **Queue** | Pending provider submissions awaiting approval | The Providers sub-page requires the `registry_maintainer` role. Users without this role will see an **Access Denied** message. *** ## Permission Model | Section | Required Role | Description | | ----------------------- | ---------------------- | ---------------------------------------------------- | | Vaults (registry table) | Any authenticated user | Browse and search the full vault registry | | My Vaults | `registry_maintainer` | Manage vault deployments and the approval queue | | Providers | `registry_maintainer` | Manage provider registrations and the approval queue | *** ## API Reference Vault list data comes from `GET /api/v1/public/vaults` (unauthenticated) or `GET /api/v1/vaults` (authenticated). Yield source and oracle registry data comes from `/api/v1/registry/yield-sources` and `/api/v1/registry/oracles`. | Endpoint | Method | Auth | Description | | ------------------------------------ | ------ | ---- | ------------------------------------------------------ | | `GET /api/v1/public/vaults` | GET | None | Public vault list (no auth, used on Home page) | | `GET /api/v1/vaults` | GET | JWT | Authenticated vault list (`?detail=minimal&limit=500`) | | `GET /api/v1/registry/yield-sources` | GET | JWT | List yield sources (`?chain_id={}&is_active=true`) | | `GET /api/v1/registry/oracles` | GET | JWT | List oracles | # Merkle Trees Source: https://docs.superform.xyz/curate/ui/merkle-trees Configure hook authorization, generate merkle proofs, and publish active proof artifacts. Merkle Trees are the authorization layer for keeper and strategy execution. A keeper can only execute a hook when the hook call matches a leaf in an authorized merkle tree. The operational flow is: 1. Draft a root configuration. 2. Activate the configuration version. 3. Generate a merkle tree from the active config. 4. Primary manager proposes / publishes the root onchain. 5. Publish the proof artifact for consumers. ## Hook Configs Hook configs define which hook + parameter combinations the vault authorizes. When creating a config, choose hooks from the registry and set inspect parameters such as: * `yield_source` — target yield source address. * `token` — token address. * `oracle` — oracle address. * Freeform hook parameters. Each parameterized row becomes a merkle leaf. Keepers must supply the matching proof at execution time. ## Activate Config Only one config version is active at a time. Activating a config selects the version used for the next tree generation. Activating a config does not update the onchain root by itself. It selects what Erebor will generate next. ## Generate Tree Generation is asynchronous: ```bash theme={null} POST /api/v1/merkle/generate GET /api/v1/merkle/jobs/{job_id} ``` A `409 Conflict` means generation is already in progress for the vault. The generated tree includes: * Root hash. * Leaf count. * Config hash / version. * Leaf details and proof paths. ## Sync Root Onchain The Sync action compares generated tree state to onchain state and returns the next action: | Action | Meaning | | ------------------ | ------------------------------------- | | `in_sync` | Onchain root already matches | | `needs_generation` | Generate a tree for the active config | | `propose` | Sign a root proposal transaction | | `pending_timelock` | Wait for the proposal timelock | | `execute` | Sign execution after timelock expiry | ```bash theme={null} GET /api/v1/merkle/sync?vault_address={addr}&chain_id={id} ``` Strategies and emergency exits depend on active merkle withdrawal hooks. A yield source can be whitelisted but still unavailable for execution if the active merkle root does not authorize the required hook path. ## Publish Proof Artifact Publishing makes the active proof set available to integrators, keepers, and frontends without Erebor authentication. ```bash theme={null} POST /api/v1/merkle/publish GET /api/v1/merkle/publish/jobs/{id} GET /api/v1/merkle/published/{chain_id}/{vault} ``` Publishing is separate from syncing. Sync updates the protocol root; publish exposes the proof artifact. ## Emergency Exit Dependency Emergency liquidity exit requires an active withdrawal hook for the target yield source. If the active config has no matching withdrawal hook or the proof artifact is missing the leaf, the Pause page cannot arm that source until the merkle configuration is fixed. ## API Reference | Endpoint | Method | Description | | --------------------------------------------- | ------ | ---------------------------------- | | `/api/v1/merkle/configs/versions` | GET | Config version history | | `/api/v1/merkle/configs/version` | GET | Single version by number | | `/api/v1/merkle/configs/active` | GET | Active config | | `/api/v1/merkle/configs` | POST | Create new config | | `/api/v1/merkle/configs/activate` | PUT | Activate a version | | `/api/v1/merkle/trees` | GET | List trees for a vault | | `/api/v1/merkle/proofs` | GET | List proofs | | `/api/v1/merkle/proofs/lookup` | GET | Lookup one proof | | `/api/v1/merkle/generate` | POST | Start generation | | `/api/v1/merkle/jobs/{id}` | GET | Poll generation job | | `/api/v1/merkle/sync` | GET | Determine root sync action | | `/api/v1/merkle/publish` | POST | Publish active tree proof artifact | | `/api/v1/merkle/publish/jobs/{id}` | GET | Poll publish job | | `/api/v1/merkle/published/{chain_id}/{vault}` | GET | Public published tree metadata | # Pause Operations Source: https://docs.superform.xyz/curate/ui/pause Pause vault execution, pause keeper services, and arm per-yield-source emergency liquidity exits. The Pause page is the operator safety console. It has three distinct controls: 1. **Pause Vault** — onchain contract-level pause / unpause. 2. **Pause Keepers** — offchain per-vault service controls. 3. **Emergency Exit** — per-yield-source liquidity drain through the Strategy Engine and OMS. Use the narrowest control that solves the problem. A vault pause, keeper pause, and emergency exit have different blast radii. ## Pause Vault Vault pause is an onchain control. It halts protocol-level vault operations until a manager signs an unpause transaction. ```bash theme={null} GET /api/v1/vaults/pause-status?vaults={chainId}:{vaultAddr} POST /api/v1/vaults/emergency-pause/prepare POST /api/v1/vaults/emergency-unpause/prepare ``` Pause reasons are recorded for auditability. ## Pause Keepers Keeper service controls are offchain. Use them when you want to stop a worker without changing vault contract state. ```bash theme={null} GET /api/v1/vaults/{chain_id}/{vault}/services PUT /api/v1/vaults/{chain_id}/{vault}/services/{service_name}/pause PUT /api/v1/vaults/{chain_id}/{vault}/services/{service_name}/resume PUT /api/v1/vaults/{chain_id}/{vault}/services/enabled ``` Service pause is useful for maintenance, data issues, or temporary operator intervention. ## Emergency Exit Emergency Exit drains liquidity from one whitelisted yield source. It is operator-armed, target-scoped, and designed for urgent source-level risk events. When armed for a source: * The paired `EMERGENCY_EXIT` strategy transitions to `RUNNING`. * The Strategy Engine records an emergency lock keyed by that source's `target_address`. * User-defined strategies targeting the same address are blocked from publishing intents. * Strategies targeting other yield sources continue normally unless they have their own lock. * The emergency strategy emits withdrawal intents on every tick until stopped. * In-flight OMS transactions continue to settle even after the operator stops the drain. Emergency Exit is not a replacement for vault pause. If the entire vault is unsafe, pause the vault. If one source is unsafe and the vault can keep operating elsewhere, arm an exit for that source. ### Prerequisites Emergency Exit depends on: * The yield source being whitelisted. * An active withdrawal hook for that source in the current merkle config. * Published / available strategy proofs for the active merkle tree. * A paired `EMERGENCY_EXIT` strategy, created automatically when prerequisites are available. * OMS/session-key readiness for execution. If the Pause page cannot arm a source, check [Merkle Trees](/curate/ui/merkle-trees), [Yield Source Management](/curate/ui/yield-sources), and [Strategy Canvas](/curate/ui/strategy). ### Arm an Emergency Exit 1. Open **Pause → Emergency Exit**. 2. Select the yield source. 3. Review current allocation, APY/TVL metadata, and blocked strategies. 4. Enter the confirmation suffix for the yield source address. 5. Optionally enter a reason. 6. Choose max slippage. Default is **50 bps**. 7. Click **Arm emergency exit**. The reason is stored with the emergency arm request and shown while the lock is active. If manager permission is revoked while arming, the in-flight request is not automatically cancelled. Verify the Pause page and stop the drain if it succeeded under the prior policy. ### Stop an Emergency Exit Stopping transitions the paired strategy back to `IDLE` and releases the emergency lock. User-defined strategies targeting that source are eligible again on the next tick. ```bash theme={null} PATCH /api/v1/strategies/{strategy_id}/state { "target_state": "IDLE", "version": 12 } ``` Stopping does not cancel transactions already published to the OMS. ## Strategy Canvas Locking While an emergency lock is active: * The Strategy Canvas shows a banner for active drains. * Strategy toggles that target the locked source are disabled. * Reorder controls are disabled only for lanes containing affected strategies. * Emergency strategies are hidden from normal strategy editing by default and are read-only when shown. The engine exposes locks through shard and vault state as `emergency_locks` entries with `strategy_id`, `target_address`, `started_at`, and optional `reason`. ## Control Comparison | Control | Scope | Onchain Tx? | Typical Use | | -------------- | ---------------------------- | ---------------------- | ------------------------------ | | Pause Vault | Entire vault contract | Yes | Protocol-level emergency | | Pause Keepers | One or all offchain services | No | Maintenance or worker incident | | Emergency Exit | One yield source target | Strategy/OMS execution | Drain a risky source | ## API Reference | Endpoint | Method | Description | | ---------------------------------------------------------- | ------ | ------------------------------------------- | | `/api/v1/vaults/pause-status` | GET | Batch onchain pause status | | `/api/v1/vaults/emergency-pause/prepare` | POST | Prepare pause transaction | | `/api/v1/vaults/emergency-unpause/prepare` | POST | Prepare unpause transaction | | `/api/v1/vaults/{chain_id}/{vault}/services` | GET | List keeper services | | `/api/v1/vaults/{chain_id}/{vault}/services/{name}/pause` | PUT | Pause one service | | `/api/v1/vaults/{chain_id}/{vault}/services/{name}/resume` | PUT | Resume one service | | `/api/v1/vaults/{chain_id}/{vault}/services/enabled` | PUT | Bulk enable / disable services | | `/api/v1/strategies?kind=EMERGENCY_EXIT` | GET | List paired emergency strategies | | `/api/v1/strategies/{id}/state` | PATCH | Arm or stop paired emergency strategy | | `/api/v1/engine/shards/{vault_id}/state` | GET | Read active emergency locks and allocations | # Registry Management Source: https://docs.superform.xyz/curate/ui/registry Manage the global yield source, oracle, and hook registries. Registry Maintainer access required. The Registry page is an internal tool for administering Erebor's global registries. Registry Maintainers can create, update, and delete yield sources, oracles, and hooks across three tabs. Registry access requires the `registry_maintainer` permission, separate from vault manager roles. Unlike the Yield Sources page (which filters to active entries for a vault), the Registry shows all entries including inactive ones. ## Tab 1: Yield Sources Every supported yield-generating protocol contract, organized by chain. | Field | Type | Description | | ------------------- | ------- | ----------------------------------------- | | `chain_id` | integer | Deployment chain | | `address` | address | Yield source contract | | `name` | string | Human-readable name (e.g., "Morpho USDC") | | `underlying_asset` | address | Asset the yield source accepts | | `yield_source_type` | enum | `erc4626` or `pendle_pt` | | `is_active` | boolean | Available for institution whitelisting | ### Syncing from Providers Import yield sources from external providers (Morpho, Pendle): ```bash theme={null} POST /api/v1/registry/yield-sources/sync { "url": "https://api.morpho.org/vaults", "name": "Morpho Import" } ``` The sync endpoint parses the provider format and creates or updates registry entries. ### CRUD ```bash theme={null} POST /api/v1/registry/yield-sources # Create PUT /api/v1/registry/yield-sources/{chain_id}/{address} # Update DELETE /api/v1/registry/yield-sources/{id} # Delete ``` ## Tab 2: Oracles Oracles price yield source positions back into the vault's underlying asset. | Field | Type | Description | | ---------------- | ------- | ------------------------------------------------ | | `chain_id` | integer | Deployment chain | | `oracle_address` | address | Oracle contract | | `oracle_id` | string | Identifier for onchain config | | `oracle_type` | enum | `erc4626`, `pendle_pt`, or `pendle_pt_amortized` | | `is_active` | boolean | Available for institution assignment | ### CRUD ```bash theme={null} POST /api/v1/registry/oracles # Create PUT /api/v1/registry/oracles/{chain_id}/{oracle_address} # Update DELETE /api/v1/registry/oracles/{id} # Delete ``` ## Tab 3: Hooks On-chain modules that keepers call during deposit, withdrawal, and rebalance operations. | Field | Type | Description | | ---------------- | ------- | ----------------------------------------------- | | `name` | string | Hook name | | `hook_type` | enum | `DEPOSIT`, `WITHDRAWAL`, `REBALANCE` | | `addresses` | array | Per-chain `{ chain_id, address }` pairs | | `inspect_params` | array | Typed parameters for merkle tree leaf structure | | `is_active` | boolean | Available in hook config editor | ### inspectParams Define the structure of each merkle tree leaf for this hook: ```json theme={null} [ { "name": "yield_source", "solidity_type": "address", "ref_type": "yield_source", "param_order": 0 }, { "name": "token", "solidity_type": "address", "ref_type": "token", "param_order": 1 } ] ``` `ref_type` determines the picker UI in the Merkle Trees config editor: address picker for yield sources, token picker for ERC-20s. ### CRUD ```bash theme={null} POST /api/v1/registry/hooks # Create PUT /api/v1/registry/hooks/{id} # Update DELETE /api/v1/registry/hooks/{id} # Delete ``` Hooks are global entities with per-chain addresses. A single hook entry can have addresses on both Ethereum and Base. ## All Registry Endpoints | Endpoint | Method | Description | | ------------------------------------------------------ | ---------- | -------------------- | | `/api/v1/registry/yield-sources` | GET/POST | List all / Create | | `/api/v1/registry/yield-sources/{chain_id}/{address}` | PUT | Update | | `/api/v1/registry/yield-sources/{id}` | DELETE | Delete | | `/api/v1/registry/yield-sources/sync` | POST | Import from provider | | `/api/v1/registry/oracles` | GET/POST | List all / Create | | `/api/v1/registry/oracles/{chain_id}/{oracle_address}` | PUT | Update | | `/api/v1/registry/oracles/{id}` | DELETE | Delete | | `/api/v1/registry/hooks` | GET/POST | List all / Create | | `/api/v1/registry/hooks/{id}` | PUT/DELETE | Update / Delete | # Setup Checklist Source: https://docs.superform.xyz/curate/ui/setup-checklist The canonical post-create path from onchain vault to live Superform listing. The Setup page turns a newly-created SuperVault into an operational and discoverable vault. Use it after Create Vault and after any major configuration reset. Use this page to move from created to available: fund upkeep, configure hooks, add yield sources, create strategies, and complete catalog listing. ## Checklist rows | Row | Complete when | Primary action | | ------------------------------ | ---------------------------------------------------------- | -------------------- | | **Vault is created** | Vault exists onchain and is readable in dashboard data. | Create Vault | | **Upkeep is staked** | Vault is active and can run operational automation. | Fund Upkeep | | **Hooks are set up** | At least one hook is configured in the active root config. | Configure Hooks | | **Yield sources are set up** | At least one yield source is whitelisted for this vault. | Manage Yield Sources | | **Strategy is set up** | At least one strategy exists for this vault. | Open Strategy | | **Vault is live on Superform** | Vault is listed and available in the Superform catalog. | List Vault | Statuses are `complete`, `in_progress`, `needs_setup`, or `blocked`. ## Activation gate Upkeep is the activation gate. Fund upkeep from **Vault Settings → Upkeep** to enable automation and discovery. The activation route can deep-link with `panel=upkeep&activateVault=1` so the primary manager lands on the funding action. Current Erebor-facing activation fields used by the UI include: | Field | Meaning | | ---------------------------------------- | ---------------------------------------------------------------------------- | | `activation.is_active` | Whether the vault is eligible for keeper operations. | | `activation.is_discoverable` | Whether the vault is surfaced in discovery. | | `activation.status` | `active` or `insufficient_upkeep`. | | `activation.reason` | Optional explanation when the vault is not active. | | `upkeep_info.balance` | Current upkeep balance, in raw token units. | | `upkeep_info.threshold.required_balance` | Required balance for activation, in raw token units. | | `upkeep_info.threshold.shortfall` | Amount still needed to clear the gate. `0` means the threshold is satisfied. | ## Catalog evidence The live-on-Superform row compares the managed vault against catalog data. Evidence includes `visibility` and `availability`, for example `listed` and `available`. If no matching catalog vault exists, the vault is onchain but not listed in the Superform catalog. ## Troubleshooting | Symptom | Likely cause | Next step | | ----------------------------- | --------------------------------------- | ----------------------------------------------------- | | Vault readable but not active | Upkeep shortfall. | Fund upkeep. | | Hooks count is zero | No active root config. | Draft, activate, generate, and propose a merkle root. | | Yield sources count is zero | Nothing is whitelisted. | Add yield sources and oracles. | | Strategy count is zero | No strategy exists. | Create a strategy after adding sources. | | Not live on Superform | Missing or unavailable catalog listing. | List the vault and set visibility/availability. | ## Operator rule Route deposits only after the checklist shows that upkeep, hooks, yield sources, strategies, and listing availability are all complete. # Strategy Canvas Source: https://docs.superform.xyz/curate/ui/strategy Build, order, run, and monitor automated SuperVault strategies. The Strategy Canvas is where managers define automated vault behavior. Strategies evaluate rules on ticks and dispatch DeFiX intents to the OMS when conditions pass. The canvas is organized by action lane: | Lane | Actions | Purpose | | --------- | ----------------------------- | -------------------------------------- | | Inflow | `DEPOSIT` | Deploy idle assets into yield sources | | Outflow | `WITHDRAWAL`, `CLAIM` | Source liquidity and claim proceeds | | Rebalance | `REBALANCE`, `SWAP`, `BRIDGE` | Move capital between sources or routes | ## Strategy Lifecycle | State | Meaning | | ---------- | ------------------------------------------------ | | `CREATED` | Drafted but not active | | `IDLE` | Ready but not ticking | | `RUNNING` | Evaluating rules and eligible to publish intents | | `ERROR` | Runtime error; inspect logs/events | | `ARCHIVED` | Soft-deleted | The Strategy Engine API currently models these states directly. Vault-level kill switches and emergency locks can still prevent a strategy from publishing even when the stored state is `RUNNING`. ## Create or Update a Strategy A strategy contains: * Name. * Indicators. * Rule tree. * Action config. * Conviction config. * Risk parameters. * Cooldown and concurrency controls. Create and update calls do **not** carry priority. Ordering is handled by the dedicated reorder endpoint. Updates require a `version` for optimistic concurrency. A stale version returns `409 Conflict`. ## Rule Trees Rules are boolean trees: | Node | Meaning | | ------ | ------------------------- | | `EXPR` | Evaluate one expression | | `AND` | All children pass | | `OR` | At least one child passes | | `NOT` | Negate one child | | `VOTE` | N-of-M children pass | Conviction config can require repeated passing ticks before publishing an intent. ## Action Config Action config tells the engine what to do after rules pass. Common fields include: * `action` * `size_expr` * `objective` * `execution_name` * `execution_address` * `target_address` * `from_address` * `target_type` * `execution_params` * `max_slippage_bps` A target must be whitelisted and authorized by the active merkle root before OMS execution can succeed. ## Reorder Strategies Drag within a lane to change priority. The UI persists order through: ```bash theme={null} POST /api/v1/strategies/reorder ``` The request includes `vault_id`, `lane`, and the complete ordered list of non-archived strategy IDs for that lane. The backend atomically assigns priorities `1..N` from list position. The ordered list must include every non-archived strategy in the lane exactly once. Priority is server-managed; create and update requests no longer set it. ## Emergency Locks Emergency liquidity exits create target-scoped locks. While a lock exists for a yield source address: * User-defined strategies targeting that address cannot publish new intents. * Affected toggles and reorder handles are disabled in the UI. * Strategies on other targets remain operable. * The paired `EMERGENCY_EXIT` strategy is controlled from [Pause Operations](/curate/ui/pause), not the normal editor. The canvas reads `emergency_locks` from shard / vault state and displays a banner when active. ## Intent History The Intent History view is the operator-facing execution ledger for strategies. It maps Strategy Engine projections to OMS events and fills. Use it to answer: * Which strategy created the intent? * What lifecycle state is it in? * Which OMS events have arrived? * Were fills mined? * What gas was used? * Which transaction hash settled the fill? Prefer the search endpoint for new tooling: ```bash theme={null} GET /api/v1/intents GET /api/v1/intents/{intent_id}/events GET /api/v1/intents/{intent_id}/fills GET /api/v1/fills ``` See [Strategy Engine API](/curate/api/strategy) and [OMS API](/curate/api/oms). ## Session Key Gate Strategies need OMS execution readiness. If a vault lacks a valid session key, the canvas blocks automation and points managers to grant or renew the key. See [OMS API](/curate/api/oms#session-keys) for the session-key endpoints. ## API Reference | Endpoint | Method | Description | | ---------------------------------------- | ------ | ------------------------------------ | | `/api/v1/strategies` | GET | List strategies with filters | | `/api/v1/strategies` | POST | Create strategy | | `/api/v1/strategies/{id}` | GET | Get strategy | | `/api/v1/strategies/{id}` | PUT | Update strategy with version | | `/api/v1/strategies/{id}` | DELETE | Archive strategy | | `/api/v1/strategies/{id}/state` | PATCH | Transition lifecycle state | | `/api/v1/strategies/reorder` | POST | Persist lane order | | `/api/v1/intents` | GET | Search intent history | | `/api/v1/intents/facets` | GET | Intent filter facets | | `/api/v1/intents/{intent_id}/events` | GET | OMS event timeline | | `/api/v1/intents/{intent_id}/fills` | GET | Fills for one intent | | `/api/v1/fills` | GET | Search fills across intents | | `/api/v1/engine/shards/{vault_id}/state` | GET | Live shard state and emergency locks | # User Management Source: https://docs.superform.xyz/curate/ui/user-management Add and remove secondary managers and view-only users for your vault. SuperVaults User Management — Primary, Secondary, and View Only manager role tabs This page controls vault access. Three tiers are managed here: primary manager (display only), secondary managers (onchain delegates), and view-only users (offchain read access). Primary manager access required. The full manager list is fetched from `GET /api/v1/users?vault_address={}&chain_id={}&limit=100`. ## Tab 1: Primary Manager Displays the primary manager's wallet address and display name. Set at vault creation, immutable without governance-level contract interaction. This key is the root of trust for the vault. ## Tab 2: Secondary Managers On-chain delegates with full operational access (except vault settings and user management). ### Adding 1. Click **Add Secondary Manager** 2. Enter wallet address 3. Click **Prepare** — `POST /api/v1/users/secondary-manager/prepare` → calldata 4. Sign the onchain transaction ### Removing 1. Click **Remove** — `POST /api/v1/users/secondary-manager/remove/prepare` → calldata 2. Sign the onchain transaction ### Updating Display Names Click the pencil icon to update any user's display name (offchain, no gas): ```bash theme={null} PATCH /api/v1/users/{wallet_address}/display-name { "display_name": "Alice (Operations)" } ``` ## Tab 3: View Only Read-only dashboard access. No onchain transaction required. ```bash theme={null} # Add POST /api/v1/users/view-only { "vault_address": "0x...", "chain_id": 8453, "wallet_address": "0x...", "display_name": "Risk Monitor" } # Remove DELETE /api/v1/users/view-only/{wallet_address}?vault_address={}&chain_id={} ``` ## API Reference | Endpoint | Method | Description | | ------------------------------------------------ | ------ | ------------------- | | `/api/v1/users` | GET | All users for vault | | `/api/v1/users/secondary-manager/prepare` | POST | Prepare add tx | | `/api/v1/users/secondary-manager/remove/prepare` | POST | Prepare remove tx | | `/api/v1/users/view-only` | POST | Add view-only | | `/api/v1/users/view-only/{wallet}` | DELETE | Remove view-only | | `/api/v1/users/{wallet}/display-name` | PATCH | Update display name | ### User Object ```json theme={null} { "user": { "id": "uuid", "wallet_address": "0x...", "display_name": "Alice" }, "roles": [ { "vault_address": "0x...", "chain_id": 8453, "role": "secondary_manager", "granted_by": "0xPrimary...", "granted_at": "2025-01-15T10:30:00Z" } ] } ``` Add at least one secondary manager before going live. If you lose access to the primary key, secondary managers can continue vault operations. # Vault Settings Source: https://docs.superform.xyz/curate/ui/vault-settings Configure fees, PPS parameters, redeem timelock, banned hook leaves, session keys, notifications, and upkeep activation. Vault Settings is the configuration hub for vault-level controls. Most settings use the same pattern: Erebor prepares calldata, the manager wallet signs, and the protocol contract applies the change onchain. Prepare endpoints return transaction data. They do not mutate protocol state by themselves. The connected wallet must sign and submit the transaction. ## Access Model Primary managers own sensitive vault settings. Secondary managers can operate the vault but do not control primary-only settings such as manager assignment and some security parameters. See [Permissions Model](/curate/permissions) for the full matrix. ## Fees Configure the vault's performance fee, management fee, and fee recipient. Fee changes protect depositors through the protocol's proposal/timelock rules. The dashboard displays current values and prepares the required transaction when a primary manager proposes a change. ## PPS Parameters Price-per-share controls define how often PPS can update and when stale data blocks operations. Common fields: * **Deviation threshold** — maximum allowed difference between calculated and current PPS. * **PPS expiration / staleness** — when vault operations should stop waiting for a fresh PPS update. * **Minimum update interval** — minimum time between PPS updates. These settings are read from vault detail data and changed through signed transactions. ## Redeem Timelock Redeem timelock adds a delay between redemption request and fulfillment eligibility. Use it for: * Liquidity sourcing before fulfillment. * Fraud and risk review windows. * Regulatory or operational policy requirements. Relevant APIs: | Endpoint | Method | Description | | ----------------------------------------------------------- | ------ | ----------------------------------- | | `/api/v1/redeem-timelocks` | GET | List configured timelocks | | `/api/v1/redeem-timelocks/bulk` | GET | Bulk timelock lookup | | `/api/v1/vaults/{chain_id}/{vault_address}/redeem-timelock` | GET | Read one vault's timelock | | `/api/v1/settings/redeem-timelock` | POST | Prepare timelock update transaction | Changing a redeem timelock affects future redemption requests. Existing requests are not retroactively changed. ## Banned Hook Leaves A banned hook leaf is blocked even if it appears in an authorized merkle tree. Use this to quickly stop a specific hook + parameter combination while you prepare a cleaner merkle config update. Banning a leaf is narrow: it blocks the exact leaf, not the entire hook category. ## Upkeep and Activation Upkeep funds vault automation. It is also the practical activation gate for a new vault. The Upkeep panel shows: * Current upkeep balance. * Upkeep token. * Required balance and shortfall. * Activation status: `active` or `insufficient_upkeep`. * Whether the vault is active for keeper operations and discoverable in public listing surfaces. * Pending upkeep withdrawal proposal, if any. A vault below its required upkeep threshold is not considered active. Once the required balance is in place, Erebor marks it active and discoverable. Erebor returns this as `upkeep_info` plus `activation`; upkeep withdrawal lifecycle is returned separately as `withdrawal_status`. ### Deposit Upkeep Managers deposit upkeep through the settings flow: ```bash theme={null} POST /api/v1/settings/upkeep/deposit ``` The dashboard handles token allowance checks first, then prepares the deposit transaction for wallet signature. ### Withdraw Upkeep Withdrawals use a propose / execute pattern: ```bash theme={null} POST /api/v1/settings/upkeep/withdraw/propose POST /api/v1/settings/upkeep/withdraw/execute ``` Use withdrawals carefully on live vaults. Pulling the balance below threshold can make the vault inactive or undiscoverable. ## Session Keys Session keys delegate limited execution authority for automation. They are used by the OMS / Strategy Engine path so strategies can publish and execute intents without asking the manager wallet for every tick. Erebor exposes vault-scoped session-key inventory and prepare endpoints: | Endpoint | Method | Description | | ------------------------------------------------------- | ------ | ---------------------------------------- | | `/api/v1/session-keys` | GET | List session keys visible to the manager | | `/api/v1/vaults/{chain_id}/{vault_address}/session-key` | GET | Read a vault session key | | `/api/v1/vaults/session-key/prepare` | POST | Prepare grant transaction | | `/api/v1/vaults/session-key/revoke/prepare` | POST | Prepare revoke transaction | OMS also exposes strategy-automation session-key readiness and rotation endpoints. See [OMS API](/curate/api/oms). ## Notification Channels Notification channels attach Slack, webhook, or websocket destinations to a vault. | Endpoint | Method | Description | | ------------------------------------------------------------------------------ | -------- | ------------------------------------- | | `/api/v1/notification-channels` | GET/POST | List or create manager-owned channels | | `/api/v1/vaults/{chain_id}/{vault_address}/notification-channels` | GET/POST | Attach or list vault channels | | `/api/v1/vaults/{chain_id}/{vault_address}/notification-channels/{channel_id}` | DELETE | Detach channel | Use notification channels for operational alerts, monitoring, and downstream automation. ## API Reference | Endpoint | Method | Description | | ------------------------------------------- | ------ | ----------------------------------------------------- | | `/api/v1/vaults/{chain_id}/{vault_address}` | GET | Full vault detail including fee, PPS, and upkeep data | | `/api/v1/settings/upkeep` | POST | Prepare upkeep settings update | | `/api/v1/settings/upkeep/deposit` | POST | Prepare upkeep deposit | | `/api/v1/settings/upkeep/withdraw/propose` | POST | Prepare upkeep withdrawal proposal | | `/api/v1/settings/upkeep/withdraw/execute` | POST | Prepare upkeep withdrawal execution | | `/api/v1/settings/redeem-timelock` | POST | Prepare redeem timelock update | # Yield Source Management Source: https://docs.superform.xyz/curate/ui/yield-sources Whitelist yield sources, pair oracles, and keep emergency-exit coverage in sync. Yield Source Management controls where a SuperVault is allowed to deploy capital. Each source is a protocol position, such as an ERC-4626 vault or Pendle PT, paired with an oracle that prices the position back into the vault's underlying asset. All whitelist mutations use the prepare pattern: Erebor prepares calldata and the manager wallet signs the transaction. ## Page Layout * **Whitelisted Yield Sources** — sources currently enabled for the selected vault. * **Yield Source Registry** — global source catalog filtered by chain and active status. * **Oracle Registry** — compatible pricing sources. Registry entries can include provider data imported from external provider URLs, such as Morpho or Pendle metadata. ## Add a Yield Source 1. Select a registry yield source. 2. Select a compatible oracle. 3. Prepare the add transaction. 4. Sign with the manager wallet. 5. Confirm the source appears in the whitelisted table. ```bash theme={null} POST /api/v1/yield-sources/prepare-add POST /api/v1/yield-sources/prepare-bulk-add ``` Adding a source also keeps the paired emergency-exit strategy lifecycle in sync. The app provisions one `EMERGENCY_EXIT` withdrawal strategy for the source when the active hook and proof prerequisites are available. ## Remove a Yield Source ```bash theme={null} POST /api/v1/yield-sources/prepare-remove POST /api/v1/yield-sources/prepare-bulk-remove ``` Before removing, confirm the vault has no deployed liquidity in the source. Removing a yield source does not unwind assets. Withdraw first, or use [Pause Operations](/curate/ui/pause) to arm an emergency drain when the situation requires immediate exit. When a whitelisted source is removed, the paired `EMERGENCY_EXIT` strategy is archived. If an emergency lock is active for that source, removal is blocked until the operator stops the drain. ## Update Oracle Use oracle updates when the current oracle is wrong, deprecated, or needs a safer pricing path. ```bash theme={null} POST /api/v1/yield-sources/prepare-update-oracle POST /api/v1/yield-sources/prepare-bulk-update-oracle ``` ## Registry Sync Registry maintainers can import external provider sources: ```bash theme={null} POST /api/v1/registry/yield-sources/sync ``` Synced entries may include provider-specific metadata in `provider_data`. Keep `is_active` accurate so inactive sources are hidden from normal operator flows. ## Source and Oracle Types | Type | Meaning | | ---------------------------- | ---------------------------------- | | `erc4626` | Standard tokenized vault position | | `pendle_pt` | Pendle principal token position | | `erc4626` oracle | Prices by share value | | `pendle_pt` oracle | Prices by PT market value | | `pendle_pt_amortized` oracle | Amortizes PT value toward maturity | ## API Reference | Endpoint | Method | Description | | ----------------------------------------------------- | -------- | -------------------------------------- | | `/api/v1/yield-sources/{chain_id}/{vault_address}` | GET | Whitelisted sources for a vault | | `/api/v1/registry/yield-sources` | GET/POST | Browse or create registry source | | `/api/v1/registry/yield-sources/{chain_id}/{address}` | GET/PUT | Read or update source by chain/address | | `/api/v1/registry/yield-sources/{id}` | DELETE | Delete registry source | | `/api/v1/registry/yield-sources/sync` | POST | Import provider sources | | `/api/v1/registry/oracles` | GET/POST | Browse or create oracles | | `/api/v1/yield-sources/prepare-add` | POST | Prepare add transaction | | `/api/v1/yield-sources/prepare-remove` | POST | Prepare remove transaction | | `/api/v1/yield-sources/prepare-update-oracle` | POST | Prepare oracle update transaction | | `/api/v1/yield-sources/prepare-bulk-add` | POST | Prepare bulk add transaction | | `/api/v1/yield-sources/prepare-bulk-remove` | POST | Prepare bulk remove transaction | | `/api/v1/yield-sources/prepare-bulk-update-oracle` | POST | Prepare bulk oracle update transaction | # Deployment Addresses Source: https://docs.superform.xyz/deployment-addresses All Superform v2 contract addresses across Ethereum, Base, Optimism, Arbitrum, BNB, Polygon, Avalanche, Unichain, and HyperEVM. Superform Core v2 contracts are deployed at deterministic addresses across Ethereum, Base, Optimism, Arbitrum, BNB, Polygon, Avalanche, Unichain, and HyperEVM. ## Core Contracts ### Infrastructure | Contract | Address | | ------------------------- | -------------------------------------------- | | SuperExecutor | `0x9cC8EDCC41154aaFC74D261aD3D87140D21F6281` | | SuperDestinationExecutor | `0x6ac58e854798D4aae5989B18ad5a1C0fF17817EF` | | SuperValidator | `0xB46b4773C5F53FF941533F5dfEFFD0713f5f9f8E` | | SuperDestinationValidator | `0xADEFF5A0684392C4c273a9C638d1dB8c5dfd0098` | | SuperBundler | `0x1101eec94dd79bee1b5a77b96c15ac24a4691e2e` | | SuperSenderCreator | `0xBC6FB94D2f10A3B4349F592FFA80C4B7C97C1799` | | SuperLedger | `0x04916bB42564CdED96E10F55C059d65E4FCb1Be6` | | SuperLedgerConfiguration | `0x2e2D71289CBA19f831856f85DEC7f194B0165e69` | | FlatFeeLedger | `0xAb56d09Ad9975116fCeb14970F2fFb3bB0ad683E` | | Nexus | `0xA3AA31f8d4Da6005aAfB0d61e5012a64d15F5B3A` | | NexusAccountFactory | `0x4153Db38136E74a88A77b51a955A88823820C050` | | NexusBootstrap | `0x5eBeb4d51723bA345080D81bBF178D93E84bC9BE` | | NexusProxy | `0x8a3A6698C3D142b9dAD80F114947d46671A5290E` | | SuperNativePaymaster | `0x2288C49689c2CceD5C5bdd74Ac3b775E61a7A532` | | SuperSponsorshipPaymaster | `0x8C71Eb1817a2707E8e40aC978B1993b98F1366aa` | ### Bridge Adapters | Contract | Address | | --------------- | -------------------------------------------- | | AcrossV3Adapter | `0x4dC34c4Eb23973F3551526C2AFE8ffb7f70F0fD7` | | DebridgeAdapter | `0x5bE003c2cD2DaCD4Cd23488DB7E74568475a36d8` | ### Hooks Hook addresses may vary across chains. Always verify hook addresses against the onchain registry before integrating directly. | Contract | Address | | --------------------------------------------- | -------------------------------------------- | | AcrossSendFundsAndExecuteOnDstHook | `0x39962bE24192d0d6B6e3a19f332e3c825604d16A` | | ApproveAndAcrossSendFundsAndExecuteOnDstHook | `0x72422aB917e4a698369767F7AcE667a769E0F3f2` | | ApproveAndDeposit4626VaultHook | `0xF37535D96712FBaEf6D868e721E7b987ad1E6A86` | | ApproveAndDeposit5115VaultHook | `0x44c7a40f05771FdAEAee61f36902D95cbf593988` | | ApproveAndRequestDeposit7540VaultHook | `0x840B2b0553683dE46c5e6382D1a405f44773b43F` | | ApproveAndSwapKyberSwapHook | `0xdC9D10d9710DBf82924a3F7733293457Ad12D37D` | | ApproveAndSwapOdosV2Hook | `0x067696e1EfBD25cAfD3B55648ED253C20A7d9671` | | ApproveERC20Hook | `0x8b789980dc6cC7d88E30C442D704646ff7F6d306` | | BatchTransferFromHook | `0x816d5de8835FB7A003896f486fCce46a6DEBB00A` | | BatchTransferHook | `0x852c6E00A7eC7590318DEAaD03030d4ddD74C93a` | | CancelDepositRequest7540Hook | `0x0BBA42ddaa6ef6CCd228BD6270565F87154E921A` | | CancelRedeemRequest7540Hook | `0x542601AfAEeB2E5dFc7d1F2fEEF5911285f0c2c0` | | CircleGatewayAddDelegateHook | `0xa7aE1263fd7D6017770147393CE130f16E1fE2cC` | | CircleGatewayMinterHook | `0x659b720a5E8E08D2c379165D17bA5F74dd104824` | | CircleGatewayRemoveDelegateHook | `0x00FbC4e3608A26E0d05905759C2A6188fDa0e2Cd` | | CircleGatewayWalletHook | `0x6383d09cF761FeAa4108B65130793c7eDA356dB5` | | ClaimCancelDepositRequest7540Hook | `0xdf958A047D90b202A7097b5f9B67Bb8CB5285858` | | ClaimCancelRedeemRequest7540Hook | `0x0668f9a638f34928f0bD91588E7B157F0699D594` | | DeBridgeCancelOrderHook | `0xc5DbbBe2D8B9ff884a7ed33f1352021CD2b482C9` | | DeBridgeSendOrderAndExecuteOnDstHook | `0x162225095A384787a257bced9b8893b29C8f1795` | | Deposit4626VaultHook | `0xa067037B29431C1ff23dEB9b10CC8a1669B0698E` | | Deposit5115VaultHook | `0x32209A2302865784bC1Dc0bd3C55D0A6eB205851` | | Deposit7540VaultHook | `0x0aB1b12E090775fA67DF6e1b44DFAEe676C1DC84` | | EthenaCooldownSharesHook | `0x1bD7698cc3E3f4cCF5D6CBC74a611bdDEaB18aeF` | | EthenaUnstakeHook | `0xaEBeEc6548B727fd4f3464B19D99f4676d7e7796` | | MarkRootAsUsedHook | `0xE61774Aa87a05fB1B5665158F2b5E0E10C71B5e2` | | MerklClaimRewardHook | `0x6C12D4453Ed2278B37eCd169F4B8693537b228Df` | | MetaMorphoReallocateHook | `0x0709C6211aDaf33D218275FdaceEA7d8c872Ab79` | | MorphoBorrowHook | `0x860EC9cFA60C0FD3c0b5940C2C3376cfc57DaaF1` | | MorphoLendHook | `0xFdA77d57d22c7EB62ef768212BA8B08f82c9b5E3` | | MorphoRepayAndWithdrawHook | `0x65f289Cdf87b54095401A17af3C3Eb626B8e5e01` | | MorphoRepayHook | `0xF78c9aB2AD1104a0597a1E32e4b2F5e074C5c4a2` | | MorphoSupplyAndBorrowHook | `0xAf77F98fd73524253A19086105c6C3944039053d` | | MorphoSupplyHook | `0xa8d51a5E4D774D5FD65a915aF732AC23c388D1dc` | | MorphoWithdrawHook | `0x2E80aace37Fb7A9C858B9398f23Dd3e771D52B22` | | OfframpTokensHook | `0xFbbD9a7026E29e889d28882606660fc5Be0BeA73` | | PendleRouterRedeemHook | `0xAae2DB58E2f426b910f518cCbB627545aEdaff2F` | | PendleRouterSwapHook | `0x02A0A95C379220E9759960A8Ee923cBbC2d305cd` | | PendleUnifiedHook | `0x433f9A343e4132a294e02D4a09Da4b575EebA9aD` | | RecordPurchasePendlePTAmortizedOracleHook | `0x771D4fF615F87eA00488a2dbcb70DF98BDA03FA3` | | RecordPurchasePendlePTAmortizedOracleHookV2 | `0xA0E61eb90817E28aBbb5a40045921B69bb784431` | | RecordRedemptionPendlePTAmortizedOracleHook | `0xb68a34AF34E64a8b3bB72983088ACeB2fAE326Fc` | | RecordRedemptionPendlePTAmortizedOracleHookV2 | `0x2A4F700923324B14bd546630Fe87B1ee08C89634` | | Redeem4626VaultHook | `0x5c3edf3F7c43828Bb72a668e2B29f9e2D9Af5A69` | | Redeem5115VaultHook | `0x6aB1fD107825F9bB3E079d23508A07486b44e6F5` | | Redeem7540VaultHook | `0xE165FBBc89a60756F57Cf0E34c04c35Cc1BbA79D` | | RequestDeposit7540VaultHook | `0xBE7738b26992a322D53edEb9a39331Bf11b60097` | | RequestRedeem7540VaultHook | `0x9c21c130aCF3EADd781AE79d75FF5fC4Bd216797` | | SetOperator7540Hook | `0x86f9DcE0a1A83C501Ba95a1aB1088d67978636A8` | | SetSlippageHook | `0x6551d0140FFdB28920E5e84DC3DA31f4bfe4364E` | | Swap1InchHook | `0x1303d5f3e3D9e4a81945cB0C2e309E1940d2425C` | | SwapKyberSwapHook | `0x828d8a9c015CEf90b373a071500AE463eD204dF9` | | SwapOdosV2Hook | `0xf09106eAA87a5d81DAcFC91F6F564131c57495bE` | | TransferERC20Hook | `0x6031c3953BC12D9Af4651B7ed517190A31a67ca4` | | TransferHook | `0x0d54e1b4060bBD598eE6ec8F7A587fF1789164E9` | ### Yield Source Oracles | Contract | Address | | --------------------------- | -------------------------------------------- | | ERC4626YieldSourceOracle | `0xd12A40B2aBD166e17f18854f57ccd202091D9fB8` | | ERC5115YieldSourceOracle | `0xEC64fE4256e9e2B935F9acb13cF0B1cE06C6DD5C` | | ERC7540YieldSourceOracle | `0x8963d668adCE629996cA0247885771B490612005` | | FirelightYieldSourceOracle | `0x211E048350c5b61704245BDABfefe95a1239dfE7` | | PendlePTAmortizedOracle | `0xD64089698f82cbCD91ba5e0422aDFa81D247eB62` | | PendlePTAmortizedOracleV2 | `0x2185B40476510Ad27d17AF90889CE91BE9282A04` | | PendlePTYieldSourceOracle | `0x98D40e5B9D0911F15278223D58fdDCB5cB4799A6` | | SpectraPTYieldSourceOracle | `0x30Ecd1150B3d198d75c51DBb41Bb7711Bd1E3260` | | StakingYieldSourceOracle | `0x6A685CAD15b7BB46094497243DcF94aD6557394c` | | SuperYieldSourceOracle | `0x98F0682ef39dE9cd6028D91090Be6EdAE129f52D` | | SuperVaultYieldSourceOracle | `0xeEbb42210D8a8B165dCF154b325C588EE8dF149A` | | YoYieldSourceOracle | `0x125d43f5F35c032a45aaD41EBE344d5c65D626D4` | ## SuperVault Contracts Core SuperVault infrastructure contracts. These addresses are the same on both Ethereum and Base. | Contract | Address | | -------------------- | ----------------------------------------------------------------------------------------------------------------------- | | SuperGovernor | [`0xB5396ef2bF8CA360cEB4166b77AFb2bed20e74d4`](https://etherscan.io/address/0xB5396ef2bF8CA360cEB4166b77AFb2bed20e74d4) | | SuperVaultAggregator | [`0x10AC0b33e1C4501CF3ec1cB1AE51ebfdbd2d4698`](https://etherscan.io/address/0x10AC0b33e1C4501CF3ec1cB1AE51ebfdbd2d4698) | | ECDSAPPSOracle | [`0x366d88F03B8EF34eb49F32a927ff6e1609F694F2`](https://etherscan.io/address/0x366d88F03B8EF34eb49F32a927ff6e1609F694F2) | | SuperBank | [`0x6fCc6a6A825FC14e6e56Fd14978FC6B97ACB5d15`](https://etherscan.io/address/0x6fCc6a6A825FC14e6e56Fd14978FC6B97ACB5d15) | | SuperOracle | [`0x8943128DbAb4279D561654dEED2930Bb975AA070`](https://etherscan.io/address/0x8943128DbAb4279D561654dEED2930Bb975AA070) | Individual vault, strategy, and escrow addresses vary per institution. To look up a specific SuperVault's contracts, visit the [institutional dashboard](https://institutions.superform.xyz). ## \$UP Token | Chain | Address | | ------------------------ | -------------------------------------------------------------------------------------------------------------------------- | | Ethereum | [`0x1d926bbe67425c9f507b9a0e8030eedc7880bf33`](https://etherscan.io/token/0x1d926bbe67425c9f507b9a0e8030eedc7880bf33) | | Base (LayerZero OFT) | [`0x5b2193fdc451c1f847be09ca9d13a4bf60f8c86b`](https://basescan.org/address/0x5b2193fdc451c1f847be09ca9d13a4bf60f8c86b) | | HyperEVM (LayerZero OFT) | [`0x642fFC3496AcA19106BAB7A42F1F221a329654fe`](https://hyperevmscan.io/address/0x642fFC3496AcA19106BAB7A42F1F221a329654fe) | | sUP (Base staking vault) | [`0x2c71f70e2Ec720AE061Ae7E0316fC9654d94f417`](https://basescan.org/address/0x2c71f70e2Ec720AE061Ae7E0316fC9654d94f417) | # What is Superform? Source: https://docs.superform.xyz/index Superform is the user-owned neobank enabling single-signature execution of arbitrary actions across chains and optimized vaults. ## Do less. Earn more. Learn, build, and curate on smart account earn infrastructure and vaults. Protocol architecture, smart accounts, hooks, and the execution model. Write hooks, curate a SuperVault, or become a validator. Full technical reference for institutions curating SuperVaults. Contract addresses across all supported chains. # Glossary Source: https://docs.superform.xyz/learn/glossary Definitions of key terms used throughout the Superform protocol documentation. * **Aggregator** - Registry/oracle contract where new `Vault`, `Strategy`, and `Escrow` clones are permissionlessly deployed and where validators attest to Price‑Per‑Share updates. * **Circuit Breaker** - On‑chain guard that pauses swaps or vault actions when oracle‑price dispersion or depeg thresholds are breached. * **Depeg Guard** - Absolute price‑deviation threshold for an individual asset in a SuperAsset. If breached, swaps pause and the asset weight decays. * **Dispersion Guard** - Parameterised limit on the standard deviation of constituent‑asset prices inside a SuperAsset; tripping it triggers a circuit breaker. * [**EIP‑7702**](https://eip7702.io/) - Ethereum standard that allows EOAs to act as smart accounts, enabling existing wallets to interact with account‑abstraction systems without migration. * [**ERC‑4337**](https://www.erc4337.io/) - Account‑abstraction standard that lets smart‑contract wallets function like EOAs via a `UserOperation` mempool and bundler system. * [**ERC‑7540**](https://eips.ethereum.org/EIPS/eip-7540) - Vault standard defining interfaces for asynchronous deposits and withdrawals. * [**ERC‑7579**](https://erc7579.com/) - Modular smart‑account standard enabling plug‑and‑play functionality through a standardised module interface. * **Escrow** - Immutable contract that holds idle funds between strategy steps and enforces withdrawal rights. * **Hook** - Lightweight, ERC‑7579‑compliant module that performs a single action (e.g., lend, bridge) inside a strategy call‑graph. * **Insurance Fund** - Reserve funded by yield earned and swap fees; used to cover shortfalls during black‑swan events. * **Ki** - Governance‑set efficiency coefficient that weights a vault's importance when computing incentives. * **Price‑Per‑Share (PPS)** - Cumulative value of a SuperVault denominated in its underlying asset; monotonically increasing and signed by validators. * **SuperBank** - Coordination contract that routes protocol‑level fees, burns `$UP` for upkeep, and handles escrow/bond commitments. * **SuperBundler** - Off‑chain relayer that batches user deposits and executes hook graphs across chains, returning a single receipt. * **SuperGovernor** - On‑chain module where `sUP` holders propose and vote on protocol events, such as SuperAsset target weights. * **SuperOracle** - Modular oracle‑adapter set providing fair‑market prices and volatility metrics to SuperAssets and circuit breakers. * **SuperPosition** - ERC‑20 receipt minted by Vault Banks representing a user's position on the source chain; streams yield back to mainnet. * **SuperVault** - Validator‑secured vault template that can run arbitrary hook‑based strategies while exposing deterministic PPS and fee rules. * \*\*$UP / sUP** - `$UP`is the utility token of the Superform Protocol; staking it yields`sUP`, granting governance rights. Later protocol phases add validator bonding utility for `\$UP\`. * **Validator** - Actor that signs SuperVault PPS updates and, in future phases, may participate in additional oracle and bonding systems. * **Vault Bank** - Chain‑specific deposit contract that mints SuperPositions and handles cross‑chain locking/unlocking for SuperVaults. # Hooks Source: https://docs.superform.xyz/learn/hooks Permissionless composable contracts that can be chained to execute any multi-chain DeFi strategy. Hooks are lightweight, permissionless contracts which perform a specific action: bridge, swap, lend, and more. They are designed to be composable and can be chained together to create complex transaction flows which can achieve any multichain strategy. Hook chains can be fulfilled by any ERC-7579 smart account with the SuperValidator and SuperExecutor modules installed. If you are interested in building hooks, jump straight to [Write a Hook](/build/hooks). ## Key Concepts Hooks implement transient storage during the execution of a transaction to temporarily hold state changes. This allows for efficient inter-hook communication that avoids the high gas costs associated with permanent storage writes. If any hook in a chain fails, the entire transaction is reverted, ensuring atomicity. ### Hook Lifecycle The execution of hooks is a four-step sequence: 1. **Build** - Frontend selects the addresses and calldata of the hook(s) in a chain. 2. **Bundle** - SuperBundler splits hooks per chain and wraps each into an ERC-4337 UserOp, storing a Merkle tree. 3. **Validate** - Smart Account delegates to SuperValidator onchain to perform Merkle proof verification. 4. **Execute** - SuperExecutor iterates through hooks, performs each action and updates SuperLedger. ### Hook Classifications Hooks are classified based on `HookType` and `HookSubType`. `HookType` must be defined as `NONACCOUNTING`, `INFLOW`, or `OUTFLOW` depending on the hook's expected interaction with the accounting system. `HookSubTypes` designate which action category the hook implements. * **NONACCOUNTING** - Utility hooks that do not update the accounting system. These hooks include `HookSubTypes` like `SWAP`, `STAKE`, `BRIDGE`. * **INFLOW** - Hooks that process additions to positions and typically encode some sort of `deposit` action. They include a `HookSubType` corresponding to the underlying vault being deposited into like `ERC4626`, `ERC7540`, etc. * **OUTFLOW** - Hooks that process reductions to positions and typically encode some sort of `redeem` action. They include similar `HookSubType` to `INFLOW` hooks. # Superform Protocol Source: https://docs.superform.xyz/learn/learn Superform Core and Superform Periphery: how the protocol components fit together. The Superform Protocol consists of [Superform Core](https://github.com/superform-xyz/v2-core) and [Superform Periphery](https://github.com/superform-xyz/v2-periphery). Superform Core is a suite of persistent, non-upgradeable contracts that compresses arbitrary actions into a single, Merkle-verified signature. Execution lives inside ERC-7579 Smart Accounts and composition comes from plug-and-play Hooks. Anyone can deploy Superform Core and add new hooks to the protocol. On top of Superform Core, various structured products have been created to optimize and simplify the yield generation process, collectively referred to as Superform Periphery. SuperVaults are permissionlessly createable, validator-secured vaults that can run arbitrary hooks while exposing deterministic price-per-share (PPS) and fee rules. SuperAssets are user-facing omnichain savings tokens (e.g., SuperUSD, SuperETH) that support decentralized allocation and incentivized rebalancing across many SuperVault positions. ### Why Superform v2? * **One-signature UX verifiable onchain:** Hook bundles turn bridge, swap, lend, and deposit into one click regardless of the number of chains without usage of offchain APIs. * **Validator-secured vaults:** Validators attest to SuperVault price-per-share, with quorum, signature, and strategy-level safety checks minimizing the risk surface around centralized strategists. * **Omnichain liquidity:** SuperAssets stream yield from any chain back to Ethereum L1. ## Technical Resources * [Whitepaper](https://github.com/superform-xyz/whitepapers/blob/main/superform_v2_whitepaper.pdf) - Superform v2 published June 2025 * [v2-core GitHub](https://github.com/superform-xyz/v2-core) - Core smart contracts and diagrams * [v2-periphery GitHub](https://github.com/superform-xyz/v2-periphery) - Periphery smart contracts and diagrams # Links Source: https://docs.superform.xyz/learn/links Superform whitepapers, source code, standards, community, and developer tools. ## Source Code | Repository | Description | | ------------------------------------------------------------- | ---------------------------------------------------------- | | [v2-core](https://github.com/superform-xyz/v2-core) | Core smart contracts: hooks, executors, validators, ledger | | [v2-periphery](https://github.com/superform-xyz/v2-periphery) | Periphery contracts: SuperVaults, \$UP, oracles | | [Brand Kit](https://github.com/superform-xyz/brand-kit) | Logo pack and color palette | ## Papers * [Superform v2 Whitepaper](https://github.com/superform-xyz/whitepapers/blob/main/superform_v2_whitepaper.pdf) - Published June 2025 ## Standards | Standard | Relevance | | --------------------------------------------------- | ------------------------------------------------------------------------------------------- | | [ERC-4626](https://eips.ethereum.org/EIPS/eip-4626) | Tokenized vault standard, base interface for yield sources | | [ERC-7540](https://eips.ethereum.org/EIPS/eip-7540) | Asynchronous tokenized vault; SuperVault implements this for async deposits and redemptions | | [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) | Minimal modular smart accounts; Superform's execution and validation modules | | [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) | Account abstraction; SuperBundler packages hook chains into UserOperations | | [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) | EOA passthrough for smart account delegation | ## Community & Links ### Sites * [Superform](https://www.superform.xyz/) — main website * [Foundation](https://www.superformfoundation.org/) — foundation website * [Help Center](https://help.superform.xyz/) — general support ### Community * [Twitter / X](https://x.com/superformxyz) * [Discord](https://discord.gg/superform) * [Mirror](https://mirror.xyz/superform.eth) ### Audit Reports * [v2-core Audits](https://github.com/superform-xyz/v2-core/tree/dev/audits) — core smart contract audits * [v2-periphery Audits](https://github.com/superform-xyz/v2-periphery/tree/dev/audits) — periphery smart contract audits ### Legacy * [v1 Docs](https://superform.gitbook.io/superform-v1) * [v1 App](https://legacy.superform.xyz/) # Smart Accounts Source: https://docs.superform.xyz/learn/smart-accounts ERC-7579 Smart Accounts powering Superform: gas abstraction, passkey login, and session keys while remaining fully self-custodial. Superform routes every interaction through lightweight ERC-7579 Smart Accounts. They add gas abstraction, passkey or social-recovery login, and session keys, while remaining fully self-custodial. All accounts created through Superform currently use [Biconomy Nexus v1.2.0](https://github.com/bcnmy/nexus/releases/tag/v1.2.0). ## Modules Smart accounts access Superform by installing four ERC-7579 modules: | Module | Purpose | | ------------------------- | -------------------------------------------------------------------------------------------------------------------- | | SuperExecutor | Executes same-chain ordered Hook bundles atomically | | SuperDestinationExecutor | Executes cross-chain ordered Hook bundles | | SuperValidator | Validates same-chain Merkle proofs and bundled signatures (EIP-1271 compliant) | | SuperDestinationValidator | Validates cross-chain Merkle proofs and bundled signatures (not EIP-1271 compliant; do not use for EntryPoint calls) | EOA users can opt to only passthrough these smart accounts via EIP-7702 if they do not wish to store funds on the smart account. # Superform Core Source: https://docs.superform.xyz/learn/superform-core A suite of persistent, non-upgradeable contracts providing Merkle-verified execution through ERC-7579 Smart Accounts and composable Hooks. Superform Core is a suite of persistent, non-upgradeable contracts that compresses arbitrary actions into a single, Merkle-verified signature. Execution lives inside ERC-7579 Smart Accounts and composition comes from plug-and-play Hooks. Anyone can deploy Superform Core and add new hooks to the protocol. Core consists of the following components: * **Execution Layer**: ERC-7579 executors (SuperExecutor, SuperDestinationExecutor) that process hook bundles with transient storage for gas-efficient inter-hook communication. * **Validation Layer**: Merkle-proof validators (SuperValidator, SuperDestinationValidator) enabling single-signature authorization for batched multi-chain operations. * **Accounting Layer**: SuperLedger and YieldSourceOracles for trustless cost basis tracking and performance fee calculation across vault standards (ERC-4626, ERC-5115, ERC-7540, Pendle). * **Infrastructure**: Bridge adapters for cross-chain messaging, SuperNativePaymaster for ERC-20 gas sponsorship, and SuperBundler for batched UserOperation processing. ## Hooks Hooks are lightweight, permissionless contracts which perform a specific action: bridge, swap, lend, and more. They are designed to be composable and can be chained together to create complex transaction flows which can achieve any multichain strategy. See [Hooks](/learn/hooks) for a full overview of Hook types, lifecycle, and classifications. # SuperVaults Source: https://docs.superform.xyz/learn/supervaults ERC-7540 tokenized vaults with institution-curated yield strategies, Merkle-verified execution, and validator-secured price-per-share. SuperVaults are ERC-7540 tokenized vaults that give institutions full programmatic control over yield strategy execution. Depositors supply assets; institutions decide where that capital goes: which protocols, in what order, under what conditions. ## Key Properties * **Non-custodial** — Depositor funds live in the vault contract, never in institution-controlled wallets * **ERC-4626 compatible** — Standard deposit/withdraw interface with async ERC-7540 extensions for cross-chain and illiquid positions * **Merkle-verified execution** — Keepers can only call hooks that match leaves in the onchain Merkle tree * **Automated operations** — Workers handle any actions, from deposits, redemptions, reward claims, swaps, and transaction management * **Oracle-secured PPS** — Price-per-share is computed by validators, signed as EIP-712 typed data, and accepted onchain only after two-thirds weighted consensus ## How SuperVaults Work A depositor submits a deposit or redemption request onchain via the ERC-7540 interface. Seven automated workers process requests: allocating deposits to configured yield sources, fulfilling redemptions, claiming rewards, and executing swaps. The institution sets allocation priorities, defines automated rebalancing rules, configures Merkle authorization for keeper operations, and monitors vault health. Off-chain validators compute price-per-share, sign EIP-712 attestations, and anyone can submit the update onchain. The oracle accepts updates only with supermajority validator consensus. ## Security Model SuperVaults enforce safety at every layer: * **Double-Merkle hook allowlisting and blacklisting** at both global and strategy levels ensures vaults can only perform pre-approved actions like depositing, redeeming, swapping, and bridging. Hook-root updates are proposal based, with the aggregator hook-root timelock defaulting to 15 minutes, and keepers still cannot execute anything outside approved leaves. * **Clear role separation** across Managers, Validators, and Guardians. Managers operate through pre-approved hooks, validators attest price-per-share, and guardians retain emergency intervention powers such as pause controls. * **Validator-signed price-per-share (PPS)** with quorum, signature, staleness, and deviation checks enforced onchain. * **Timelocked sensitive parameters** such as fee changes, PPS expiration threshold updates, active PPS oracle changes after first set, and some governance actions, while other operational controls remain immediate. * **Automated emergency controls** provide pause functionality, banned leaves, and emergency withdrawal paths using authorized hooks. ## Efficiency SuperVaults are engineered for scale: * **Native ERC-7540 compliance** enables efficient flows for low-cost deposits and async withdrawals as well as onchain composability. * **Cross-chain deposits** via the SuperBundler support 8+ networks with a single signature. * **Modular hook engine** allows SuperVaults to access any yield source. Strategies can evolve as markets do, without redeployments or protocol upgrades. ## Transparency Depositors do not have to trust; they can verify: * A **live transparency dashboard** surfaces allocations, caps, validator signatures, and performance in real time. * **PPS can be recomputed by anyone**, onchain or offchain, with circuit breakers that pause abnormal behavior automatically. * All **underlying protocols can continuously be monitored** for exploit vectors and governance risk, and quickly allocated out of if needed. ## Architecture SuperVault Architecture The architecture comprises three systems: vault triads, governance coordination, and PPS validation. ### Vault Triads Each SuperVault deployment consists of three onchain contracts managed through the **SuperVaultAggregator** (factory, registry, PPS oracle forwarding, upkeep accounting, and governor-level constraints): | Contract | Purpose | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **SuperVault** | ERC-7540 vault that holds depositor assets and issues share tokens. Entry-point for synchronous deposits and asynchronous redeems. | | **SuperVaultStrategy** | Execution engine that manages hook authorization via dual Merkle roots, tracks PPS high water mark, queues/fulfills redemption requests, and enforces fee policies. | | **Escrow** | Holds user shares during pending redemptions (allowing cancellation) and assets during fulfillment claims. | ### Governance & Coordination | Component | Role | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | **\$UP Token** | Protocol token used for governance via sUP, funds SuperVault upkeep for PPS update writes, and gains validator-bonding utility in later phases. | | **SuperGovernor** | Central registry and access control hub. Manages role-based permissions, selected timelocked parameter updates, and emergency intervention powers. | | **SuperBank** | Protocol treasury that handles protocol fees and executes governance-approved hook operations. | ### PPS Validation | Component | Role | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | **ECDSAPPSOracle** | Validates price-per-share updates using ECDSA signatures and forwards validated updates to the Aggregator. | | **Validator Network** | Validators monitor PPS updates and submit attestations, with consensus, signature, and quorum checks enforced before updates are accepted. | ## Supported Yield Sources | Type | Description | Example | | ----------- | ------------------------ | -------------------------------- | | `erc4626` | Standard ERC-4626 vaults | Morpho, Aave wrappers, Euler | | `pendle_pt` | Pendle Principal Tokens | Fixed-rate yield via PT purchase | ERC-7540 yield sources (async vaults) are on the roadmap. ## Live Deployments SuperVaults are live on **Ethereum** and **Base** with flagship vaults for USDC, WETH, and WBTC. See [Deployment Addresses](/deployment-addresses) for the full contract list. ## Next Steps End-to-end flow from deposit request to yield accrual. Fee structure, \$UP upkeep, and protocol revenue. Full technical guide for institutions curating vaults. # Economics Source: https://docs.superform.xyz/learn/supervaults/economics SuperVault fee structure: management fees, performance fees, upkeep costs, and the fee skimming mechanism. All fees taken are configurable by the strategist with a split to Superform that can be turned on by governance. | Fee Type | Description | | ----------- | ------------------------------------------------------------------------------------- | | Management | Configurable by the strategist, applied to the initial deposit amount | | Performance | Configurable by the strategist, applied to profits above cost basis when users redeem | | Upkeep | Strategists pay UP tokens for validators to update prices | ### Fee Skimming Mechanism Performance fees are taken only from realized profits, these fees are obtained periodically via the fee skimming mechanism. The high-water mark is the last price-per-share level at which performance fees were taken; fees are only charged on PPS growth above this baseline, and after each fee skim the new (post-fee) PPS becomes the updated high-water mark for future calculations. Because deposits and redemptions are PPS-neutral, the high-water mark only moves when yield has actually been realized and fees are skimmed on that realized profit. # Transaction Lifecycle Source: https://docs.superform.xyz/learn/supervaults/transaction-lifecycle End-to-end transaction flows for SuperVault deposits, PPS updates, strategy operations, and redemptions. #### Deposits 1. User Deposit: User calls `deposit()` with assets 2. Immediate Minting: Shares minted synchronously at current PPS 3. Asset Custody: Assets transferred to strategy for allocation 4. Hook Execution: Strategist executes hooks to deploy capital 5. PPS Updates: Validators provide updated PPS reflecting new positions #### PPS Updates 1. Off-Chain Calculation: Validators calculate PPS independently 2. Signature Generation: Validators sign PPS with private keys 3. Aggregation: Signatures collected and submitted to oracle 4. Validation: Oracle verifies signatures and quorum 5. Forwarding: Validated PPS forwarded to aggregator 6. Strategy Checks: Aggregator runs dispersion/deviation checks 7. Storage: PPS stored and made available for vault operations #### Strategy Management 1. Hook Approval: Strategist proposes new hook root 2. Timelock Period: 15-minute delay configurable by governance for strategy hooks 3. Execution: Hook root updated and new strategies enabled 4. Governance Oversight: Root updates remain visible during the timelock before they take effect #### Redemptions 1. Redeem Request: User calls `requestRedeem()` with shares 2. Redeem Slippage: User optionally calls `setRedeemSlippage()` (default 0.5%) 3. Share Escrow: Shares transferred to escrow contract 4. Fulfillment: Strategist executes hooks to free up assets 5. PPS Validation: Slippage protection against recorded request PPS 6. Asset Claim: User calls `withdraw()` to receive assets ## PPS Validation Flexibility and security is economically enforced by validators submitting PPS, supported by oracle infrastructure in Superform core. If interested in becoming a SuperVault validator, we recommend reading [Become a Validator](/build/become-a-validator). Validators currently use a ECDSAPPSOracle which validates price updates using cryptographic signatures from the decentralized validator network. 1. Message Construction: `keccak256(abi.encodePacked(strategy, pps, ppsStdev, validatorSet, totalValidators, timestamp))` 2. Signature Recovery: Uses ECDSA to recover signer addresses 3. Validator Verification: Checks signers against registered validator set 4. Quorum Enforcement: Requires minimum number of signatures 5. Duplicate Prevention: Prevents the same validator from signing twice # $UP Source: https://docs.superform.xyz/learn/usdup The UP token: ownership, staking, governance participation, and planned validator utility in the Superform Protocol. $UP is the token representing ownership over Superform. Staking it yields sUP, which confers voting power over SuperVault parameters, SuperAsset weights, economic configuration, and SuperVault upkeep participation. Later protocol phases introduce validator bonding and slashing utility for $UP. Contract addresses for \$UP and sUP across all supported chains are listed on the [Deployment Addresses](/deployment-addresses) page. ## Phases | Phase | Milestone | New \$UP utility | | ----- | -------------------------- | --------------------------------------------------------- | | 0 | Open Access | Initial set of protocol params | | **1** | SuperVault launch | Validator-secured PPS and \$UP-funded upkeep payments | | 2 | Validator decentralization | Bond \$UP to join validator sets with slashing | | 3 | SuperAsset launch | Voting on SuperAsset composition | | 4 | Bundler decentralization | Bundlers may bond \$UP in a similar fashion to validators | # Security Source: https://docs.superform.xyz/security Superform has been reviewed by multiple independent security firms across both core and periphery contracts. The protocol enforces security at multiple layers: onchain Merkle verification for keeper operations, timelocked governance updates, circuit-breaker PPS validation, role-based access control, and continuous invariant monitoring. ## Audits ### v2-core | Auditor | Date | Report | | ------------------- | ----------- | ----------------------------------------------------------------------------------------------------- | | Orion Security | August 2025 | [Report](https://github.com/superform-xyz/v2-core/blob/dev/audits/2025.08.22-orion.pdf) | | Cantina Competition | June 2025 | [Report](https://github.com/superform-xyz/v2-core/blob/dev/audits/2025.06.02-cantina-competition.pdf) | | Node Security | May 2025 | [Report](https://github.com/superform-xyz/v2-core/blob/dev/audits/2025.05.05-nodesecurity.pdf) | | Cantina Code | April 2025 | [Report](https://github.com/superform-xyz/v2-core/blob/dev/audits/2025.04.19-cantinacode.pdf) | | Sujith Somraaj | March 2025 | [Report](https://github.com/superform-xyz/v2-core/blob/dev/audits/2025.03.24-sujithsomraaj.pdf) | ### v2-periphery | Auditor | Date | Report | | --------------- | ------------- | ------------------------------------------------------------------------------------------------------ | | Octane Security | November 2025 | [Report](https://github.com/superform-xyz/v2-periphery/blob/dev/audits/2025.11.30-octane-security.pdf) | | 0xMacro | November 2025 | [Report](https://github.com/superform-xyz/v2-periphery/blob/dev/audits/2025.11.27-0xMacro.pdf) | | GetRecon | November 2025 | [Report](https://github.com/superform-xyz/v2-periphery/blob/dev/audits/2025.11.07-GetRecon.pdf) | | Cantina Code | June 2025 | [Report](https://github.com/superform-xyz/v2-periphery/blob/dev/audits/2025.06.30-cantinacode.pdf) | All critical properties and invariants are tested through the [GetRecon](https://getrecon.xyz/) invariant suite using both Echidna and Medusa across >100 million runs, and continuously monitored through [Tenderly](https://tenderly.co/monitoring). ## Security Model ### Dual Merkle Hook Validation All keeper operations are gated by a dual Merkle tree system. A hook can only execute if the exact combination of hook address + parameters matches a leaf in one of two onchain roots: | Root | Controlled By | Timelock | Scope | | ----------------------- | ---------------------------------------------- | ------------------ | ------------------------------------------------------------- | | **Global hooks root** | SuperGovernor (governance) | Default 15 minutes | Protocol-wide hooks without beneficiary-specific arguments | | **Strategy hooks root** | Main manager proposal via SuperVaultAggregator | Default 15 minutes | Vault-specific hooks including beneficiary-specific arguments | Both hook-root flows pass through the aggregator's configurable hook-root timelock. Protocol governance can update that delay. ### PPS Oracle Security Price-per-share updates pass through multiple validation layers before acceptance: 1. **ECDSAPPSOracle** validates EIP-712 signatures, quorum (≥ 2/3 weighted stake), and nonce binding 2. **SuperVaultAggregator** checks future timestamps, pause state, and staleness 3. **Strategy-level checks** enforce monotonicity, rate limiting, deviation bounds, dispersion thresholds, and upkeep balance Strategies are automatically paused when: * PPS dispersion exceeds threshold (high standard deviation among validator submissions) * PPS deviation is too large (absolute change from current onchain value) * Validator participation rate falls below minimum * Updates are stale beyond the configured window ### Access Controls | Role | Permission | | ------------------------- | -------------------------------------------------------------------------------------------------------------- | | **SuperGovernor** | Protocol governance with strategy-level overrides; updates global hook root | | **Primary Strategist** | Full vault control: hook whitelisting, fees, deposit/redemption processing | | **Secondary Strategists** | Day-to-day operations, everything except hook whitelisting and fees | | **Guardians** | Threat monitoring network with emergency intervention powers such as pause controls | | **Validators** | Validators that attest to PPS updates and must satisfy signature and quorum checks before updates are accepted | ### Timelock Protections | Change Type | Timelock | | --------------------------------------- | ---------------------------------------------- | | Hook-root updates (global and strategy) | Default 15 minutes, configurable by governance | | Post-unpause skim | 12 hours | | Upkeep withdrawal | 24 hours | | Min update interval changes | 3 days | | Primary strategist updates | 7 days | | Fee configuration changes | 7 days | | PPS expiration threshold updates | 7 days | ### Emergency Controls **Strategy Pausing** — Both primary and secondary managers can pause strategies immediately. Unpausing marks PPS as stale until a fresh oracle update is posted. **Banned Hook Leaves** — Individual Merkle leaves can be banned without regenerating the entire tree. This provides an immediate block on specific hook + parameter combinations. **Guardian Controls** — Guardians retain emergency intervention powers such as pause controls, allowing rapid response to detected threats. **Emergency Withdrawals** — Managers can perform emergency withdrawals through authorized hooks. Expanding that authorization still requires a hook-root proposal and execution flow before new leaves become active.