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.ioWe reverse-engineered the IPOR Fusion React SPA and discovered the
/fusion/vaults-customization-listendpoint that serves vault metadata including descriptions, logos, links and optionalcuratorNamevaluesThe frontend bundle also contains the public IPOR app vault configuration. We parse only the address-keyed
atomistvalues from this bundle, because production API rows currently exposecuratorNamesparsely.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:
API base URL:
https://api.ipor.ioCustomisation endpoint:
GET /fusion/vaults-customization-list
Module Attributes
IPOR data API base URL (S3-backed, served via CloudFront) |
|
IPOR Fusion frontend base URL. |
|
How long fetched IPOR offchain metadata is trusted before refresh. |
Functions
|
Fetch known IPOR atomist names for curator maintenance checks. |
|
Fetch and cache the IPOR vault customisation list. |
|
Fetch and cache IPOR frontend atomist metadata. |
|
Fetch an IPOR vault atomist display name. |
|
Check whether IPOR lists a vault in public offchain sources. |
|
Fetch and cache IPOR's public vault list. |
|
Fetch vault metadata from IPOR's offchain customisation API. |
Classes
Metadata about an IPOR-listed vault from the public vault list. |
|
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
xatomistfrom being treated as the manager field.
- class IPORVaultMetadata
Bases:
TypedDictMetadata 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:
- 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"
- __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:
TypedDictMetadata 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:
- __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 -> atomistmapping, 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
cache_path (pathlib.Path) – Directory for cache files.
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
Dict keyed by lower-case vault address.
- Return type
- 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
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
Set of atomist display names IPOR exposes through API or frontend data.
- Return type
- 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)toIPORVaultMetadata- 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
vaultsarray 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
cache_path (pathlib.Path) – Directory for cache files.
api_base_url (str) – IPOR data API base URL.
now – Override current time for tests.
max_cache_duration (datetime.timedelta) – How long before refreshing cache.
now_ (Optional[datetime.datetime]) –
- Returns
Dict mapping
(chain_id, checksummed_address)toIPORListedVaultMetadata.- 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-listis intentionally not treated as the sole source of truth because many live IPOR vaults have no custom description. The broad/fusion/vaultslist 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
Truewhen the vault appears in IPOR public offchain data.- Return type
- 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
Noneif 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
Noneif IPOR does not expose one.- Return type