erc_4626.vault_protocol.ipor.offchain_metadata

Documentation for eth_defi.erc_4626.vault_protocol.ipor.offchain_metadata Python module.

IPOR vault offchain metadata.

  • IPOR stores vault descriptions, logos and optional atomist names in a public S3-backed JSON file at api.ipor.io

  • We reverse-engineered the IPOR Fusion React SPA and discovered the /fusion/vaults-customization-list endpoint that serves vault metadata including descriptions, logos, links and optional curatorName values

  • The frontend bundle also contains the public IPOR app vault configuration. We parse only the address-keyed atomist values from this bundle, because production API rows currently expose curatorName sparsely.

  • Vault accessors fetch this metadata lazily on first access and then reuse the local disk cache and module-level in-process cache where available.

Data flow in the IPOR frontend

The vault info page loads descriptions from two sources and merges them:

description = customization?.description ?? config?.description

Where customization comes from the API and config is hardcoded in the JS bundle. Descriptions come from the API customisation source. Manager names prefer API curatorName and fall back to the frontend atomist value.

Reference:

  • IPOR Fusion app

  • API base URL: https://api.ipor.io

  • Customisation endpoint: GET /fusion/vaults-customization-list

Module Attributes

DEFAULT_API_BASE_URL

IPOR data API base URL (S3-backed, served via CloudFront)

DEFAULT_APP_BASE_URL

IPOR Fusion frontend base URL.

DEFAULT_CACHE_DURATION

How long fetched IPOR offchain metadata is trusted before refresh.

Functions

fetch_ipor_atomist_names([cache_path, ...])

Fetch known IPOR atomist names for curator maintenance checks.

fetch_ipor_customisation_list([cache_path, ...])

Fetch and cache the IPOR vault customisation list.

fetch_ipor_frontend_atomists([cache_path, ...])

Fetch and cache IPOR frontend atomist metadata.

fetch_ipor_vault_atomist(web3, vault_address, *)

Fetch an IPOR vault atomist display name.

fetch_ipor_vault_is_listed(web3, ...[, ...])

Check whether IPOR lists a vault in public offchain sources.

fetch_ipor_vault_list([cache_path, ...])

Fetch and cache IPOR's public vault list.

fetch_ipor_vault_metadata(web3, vault_address)

Fetch vault metadata from IPOR's offchain customisation API.

Classes

IPORListedVaultMetadata

Metadata about an IPOR-listed vault from the public vault list.

IPORVaultMetadata

Metadata about an IPOR vault from the offchain customisation API.

DEFAULT_CACHE_PATH = PosixPath('/home/runner/.tradingstrategy/cache/ipor')

Where we cache fetched IPOR metadata files

DEFAULT_API_BASE_URL = 'https://api.ipor.io'

IPOR data API base URL (S3-backed, served via CloudFront)

DEFAULT_APP_BASE_URL = 'https://app.ipor.io'

IPOR Fusion frontend base URL. The React bundle contains the atomist config used by the public app when the API customisation entry has no curator name.

DEFAULT_CACHE_DURATION = datetime.timedelta(days=2)

How long fetched IPOR offchain metadata is trusted before refresh.

FRONTEND_BUNDLE_RE = re.compile('["\'](?P<asset>https?://[^"\']+/assets/[^"\']+\\.js|/assets/[^"\']+\\.js|assets/[^"\']+\\.js)["\']')

Script path pattern in the IPOR app HTML.

IPOR currently serves same-origin Vite assets under /assets/, but allow absolute CDN URLs as well so a frontend hosting change does not make manager cache refreshes depend only on stale disk data.

FRONTEND_ADDRESS_RE = re.compile('address:"(?P<address>0x[a-fA-F0-9]{40})"')

Vault address pattern inside the minified frontend config.

FRONTEND_ATOMIST_RE = re.compile('(?<![A-Za-z0-9_])atomist:"(?P<atomist>[^"]+)"')

Atomist field pattern inside the minified frontend config.

The negative lookbehind keeps similarly named keys such as xatomist from being treated as the manager field.

class IPORVaultMetadata

Bases: TypedDict

Metadata about an IPOR vault from the offchain customisation API.

Fetched from api.ipor.io/fusion/vaults-customization-list. Discovered by reverse-engineering the IPOR Fusion React SPA JavaScript bundles.

The customisation endpoint returns a flat JSON array. Each entry corresponds to a vault that has had its metadata edited by the vault’s atomist (operator). Not all IPOR vaults have customisation entries — only those whose atomists have set descriptions via the IPOR frontend.

Reference:

chain_id: int

EVM chain ID (e.g. 1 for Ethereum, 8453 for Base)

vault_address: str

Vault contract address (checksummed)

description: Optional[str]

Full vault strategy description set by the atomist.

Example: "The Bitcoin Dollar USDC Vault generates yield by acquiring sBTCD, a yield bearing 50% BTC 50% USD collateralized token..."

vault_logo_url: Optional[str]

URL to the vault logo image on IPOR’s API.

Example: "https://api.ipor.io/fusion/vaults-customization/1/0xf8.../vault-logo"

curator_name: Optional[str]

Optional atomist/curator display name from the IPOR customisation API.

URL to a disclaimer document, if provided by the atomist

URL to a prospectus document, if provided by the atomist

__init__(*args, **kwargs)
__new__(**kwargs)
clear()

Remove all items from the dict.

copy()

Return a shallow copy of the dict.

fromkeys(value=None, /)

Create a new dictionary with keys from iterable and values set to value.

get(key, default=None, /)

Return the value for key if key is in the dictionary, else default.

items()

Return a set-like object providing a view on the dict’s items.

keys()

Return a set-like object providing a view on the dict’s keys.

pop(k[, d]) v, remove specified key and return the corresponding value.

If the key is not found, return the default if given; otherwise, raise a KeyError.

popitem()

Remove and return a (key, value) pair as a 2-tuple.

Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.

setdefault(key, default=None, /)

Insert key with a value of default if key is not in the dictionary.

Return the value for key if key is in the dictionary, else default.

update([E, ]**F) None.  Update D from mapping/iterable E and F.

If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

values()

Return an object providing a view on the dict’s values.

class IPORListedVaultMetadata

Bases: TypedDict

Metadata about an IPOR-listed vault from the public vault list.

Fetched from api.ipor.io/fusion/vaults.

Unlike vaults-customization-list, this endpoint is the broad public inventory the Fusion app uses for vault metrics. Most production vaults do not have custom descriptions, but they still appear in this list and should not be treated as unofficial merely because no customisation row exists.

Reference:

chain_id: int

EVM chain ID (e.g. 1 for Ethereum, 8453 for Base)

vault_address: str

Vault contract address (checksummed)

name: Optional[str]

IPOR display name for the vault.

asset: Optional[str]

Denomination asset symbol, e.g. USDC.

asset_address: Optional[str]

Denomination token address (checksummed), when IPOR exposes it.

tvl: Optional[str]

Current TVL as returned by IPOR, usually a decimal string or None.

__init__(*args, **kwargs)
__new__(**kwargs)
clear()

Remove all items from the dict.

copy()

Return a shallow copy of the dict.

fromkeys(value=None, /)

Create a new dictionary with keys from iterable and values set to value.

get(key, default=None, /)

Return the value for key if key is in the dictionary, else default.

items()

Return a set-like object providing a view on the dict’s items.

keys()

Return a set-like object providing a view on the dict’s keys.

pop(k[, d]) v, remove specified key and return the corresponding value.

If the key is not found, return the default if given; otherwise, raise a KeyError.

popitem()

Remove and return a (key, value) pair as a 2-tuple.

Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.

setdefault(key, default=None, /)

Insert key with a value of default if key is not in the dictionary.

Return the value for key if key is in the dictionary, else default.

update([E, ]**F) None.  Update D from mapping/iterable E and F.

If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

values()

Return an object providing a view on the dict’s values.

fetch_ipor_frontend_atomists(cache_path=PosixPath('/home/runner/.tradingstrategy/cache/ipor'), app_base_url='https://app.ipor.io', now_=None, max_cache_duration=datetime.timedelta(days=2))

Fetch and cache IPOR frontend atomist metadata.

The cache stores only the parsed vault_address -> atomist mapping, not the full JavaScript bundle. If refresh fails but an older cache exists, the stale cache is returned so manager attribution does not disappear because of a transient app or CDN failure.

Parameters
Returns

Dict keyed by lower-case vault address.

Return type

dict[str, str]

fetch_ipor_atomist_names(cache_path=PosixPath('/home/runner/.tradingstrategy/cache/ipor'), api_base_url='https://api.ipor.io', app_base_url='https://app.ipor.io', now_=None, max_cache_duration=datetime.timedelta(days=2))

Fetch known IPOR atomist names for curator maintenance checks.

This helper is for audits and tests that need the set of IPOR manager names, not a vault-specific lookup. Vault accessors should call fetch_ipor_vault_atomist() instead.

Parameters
Returns

Set of atomist display names IPOR exposes through API or frontend data.

Return type

set[str]

fetch_ipor_customisation_list(cache_path=PosixPath('/home/runner/.tradingstrategy/cache/ipor'), api_base_url='https://api.ipor.io', now_=None, max_cache_duration=datetime.timedelta(days=2))

Fetch and cache the IPOR vault customisation list.

The API returns a single JSON array covering all chains. We index by (chain_id, checksummed_address) for fast lookup.

  • Single JSON cache file for all chains

  • Multiprocess safe via file lock

Parameters
  • cache_path (pathlib.Path) – Directory for cache files (default ~/.tradingstrategy/cache/ipor/)

  • api_base_url (str) – IPOR data API base URL

  • now – Override current time (for testing)

  • max_cache_duration (datetime.timedelta) – How long before refreshing cache (default 2 days)

  • now_ (Optional[datetime.datetime]) –

Returns

Dict mapping (chain_id, checksummed_address) to IPORVaultMetadata

Return type

dict[tuple[int, str], eth_defi.erc_4626.vault_protocol.ipor.offchain_metadata.IPORVaultMetadata]

fetch_ipor_vault_list(cache_path=PosixPath('/home/runner/.tradingstrategy/cache/ipor'), api_base_url='https://api.ipor.io', now_=None, max_cache_duration=datetime.timedelta(days=2))

Fetch and cache IPOR’s public vault list.

The API returns an object whose vaults array covers all chains. We index by (chain_id, checksummed_address) for fast lookup.

This is the broad official IPOR offchain source used for deciding whether a vault is listed by IPOR. Do not use the customisation endpoint for that: customisation rows are sparse and mostly indicate descriptions, logos or prospectus links.

Parameters
Returns

Dict mapping (chain_id, checksummed_address) to IPORListedVaultMetadata.

Return type

dict[tuple[int, str], eth_defi.erc_4626.vault_protocol.ipor.offchain_metadata.IPORListedVaultMetadata]

fetch_ipor_vault_is_listed(web3, vault_address, *, cache_path=PosixPath('/home/runner/.tradingstrategy/cache/ipor'), api_base_url='https://api.ipor.io', app_base_url='https://app.ipor.io', now_=None, max_cache_duration=datetime.timedelta(days=2))

Check whether IPOR lists a vault in public offchain sources.

vaults-customization-list is intentionally not treated as the sole source of truth because many live IPOR vaults have no custom description. The broad /fusion/vaults list is checked first, with the customisation API and frontend atomist map as fallbacks for temporary API shape changes.

Parameters
  • web3 (web3.main.Web3) – Web3 instance used to get the EVM chain id.

  • vault_address (eth_typing.evm.HexAddress) – Vault contract address.

  • cache_path (pathlib.Path) – Directory for IPOR cache files.

  • api_base_url (str) – IPOR data API base URL.

  • app_base_url (str) – IPOR Fusion app base URL.

  • now – Override current time for tests.

  • max_cache_duration (datetime.timedelta) – Cache time-to-live.

  • now_ (Optional[datetime.datetime]) –

Returns

True when the vault appears in IPOR public offchain data.

Return type

bool

fetch_ipor_vault_metadata(web3, vault_address)

Fetch vault metadata from IPOR’s offchain customisation API.

  • Uses a two-level cache: in-process dict + disk cache

  • Returns None if the vault has no customisation entry (i.e. the atomist has not set a description via the IPOR frontend)

Parameters
  • web3 (web3.main.Web3) – Web3 instance (used to get chain_id and checksum address)

  • vault_address (eth_typing.evm.HexAddress) – Vault contract address

Returns

Metadata dict or None if the vault has no customisation entry

Return type

Optional[eth_defi.erc_4626.vault_protocol.ipor.offchain_metadata.IPORVaultMetadata]

fetch_ipor_vault_atomist(web3, vault_address, *, cache_path=PosixPath('/home/runner/.tradingstrategy/cache/ipor'), api_base_url='https://api.ipor.io', app_base_url='https://app.ipor.io', now_=None, max_cache_duration=datetime.timedelta(days=2))

Fetch an IPOR vault atomist display name.

This accessor-style helper mirrors other protocol offchain metadata modules: the first vault property access triggers the fetch, then the local disk cache and in-process cache are reused by later calls.

Parameters
  • web3 (web3.main.Web3) – Web3 instance used to get the EVM chain id.

  • vault_address (eth_typing.evm.HexAddress) – Vault contract address.

  • cache_path (pathlib.Path) – Directory for IPOR cache files.

  • api_base_url (str) – IPOR data API base URL.

  • app_base_url (str) – IPOR Fusion app base URL.

  • now – Override current time for tests.

  • max_cache_duration (datetime.timedelta) – Cache time-to-live.

  • now_ (Optional[datetime.datetime]) –

Returns

Atomist display name, or None if IPOR does not expose one.

Return type

Optional[str]