Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions 1/products.json
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,7 @@
"vaults": [],
"deprecatedVaults": [
"0x716bF454066a84F39A2F78b5707e79a9d64f1225",
"0xf2f826c190D020A6D1EC422bF2269E63b8b315E0",
"0x19756be2473A9C3351A426568FF9630c9D67f300",
"0x57995a00bA1e3E17BCF08BA775BF225cA1f5F25e",
"0xd45a56F5b67eBA836295B4c95fd6EA48D1334938",
"0x49aEC2e6137615f0cf1CAE334B5f3211953144bE"
"0xf2f826c190D020A6D1EC422bF2269E63b8b315E0"
],
"deprecationReason": "Deprecated due to low activity.",
"notExplorable": true
Expand Down
4 changes: 2 additions & 2 deletions 143/entities.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"k3-capital": {
"name": "K3 Capital",
"logo": "k3.svg",
"description": "K3 Capital provide leading institutional asset and risk management in decentralized finance.",
"description": "K3 Capital is a third-party curator using Euler infrastructure to configure vaults across selected DeFi markets. The curator is responsible for asset selection, vault parameter configuration, and management of the vaults it configures.",
"url": "https://k3.capital/",
"addresses": {
"0x060DB084bF41872861f175d83f3cb1B5566dfEA3": "K3 Capital Multisig"
Expand All @@ -65,7 +65,7 @@
"hyperithm": {
"name": "Hyperithm",
"logo": "hyperithm.png",
"description": "Hyperithm maximizes yield by swiftly integrating unique collaterals, providing borrowers with high-return opportunities through active optimization while ensuring effective risk management.",
"description": "Hyperithm is a third-party curator using Euler infrastructure to configure markets involving selected collateral assets. The curator is responsible for asset selection, vault parameter configuration, and management of the vaults it configures.",
"url": "https://www.hyperithm.com/",
"addresses": {
"0x75178137D3B4B9A0F771E0e149b00fB8167BA325": "Hyperithm MPC wallet",
Expand Down
9 changes: 6 additions & 3 deletions 146/products.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
"re7-labs-sonic": {
"name": "Re7 Labs Cluster",
"entity": "re7-labs",
"vaults": ["0xF3c631B979EB59d8333374baA7c58B5Aff5e24D2"],
"vaults": [],
"deprecatedVaults": [
"0xF3c631B979EB59d8333374baA7c58B5Aff5e24D2",
"0xEeb63D2d370C5318ef174D366A41507F9380f093",
"0x8537a336fC74EA742527E8300175A7b07a108c9E",
"0x4c0AF5d6Bcb10B3C05FB5F3a846999a3d87534C7",
Expand All @@ -12,7 +13,8 @@
"0xeEb1DC1Ca7ffC5b54aD1cc4c1088Db4E5657Cb6c",
"0x2ad7546A19eb5D5eDb37EC88A4775995078d1CAd"
],
"notExplorable": true
"notExplorable": true,
"deprecationReason": "Deprecated due to low activity."
},
"mev-capital-sonic": {
"name": "MEV Capital Sonic Cluster",
Expand Down Expand Up @@ -45,7 +47,8 @@
"0x2A377470DcF0e00F6DEd484DDf82df65F0Fb50af",
"0xB6a53021E7A40ea20DF1e1991B0C89432073386E"
],
"notExplorable": true
"notExplorable": true,
"deprecationReason": "Deprecated due to low activity."
},
"mev-capital-beets": {
"name": "MEV Capital Beets Cluster",
Expand Down
2 changes: 1 addition & 1 deletion 59144/entities.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"alphagrowth": {
"name": "AlphaGrowth",
"logo": "alphagrowth.svg",
"description": "AlphaGrowth curates lending markets and structured DeFi products on Euler V2.",
"description": "AlphaGrowth is a third-party curator using Euler infrastructure to configure lending markets and structured DeFi products. The curator is responsible for asset selection, vault parameter configuration, and management of the vaults it configures.",
"url": "https://alphagrowth.io/",
"addresses": {
"0x4f894Bfc9481110278C356adE1473eBe2127Fd3C": "AlphaGrowth Multisig"
Expand Down
5 changes: 1 addition & 4 deletions 8453/products.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,6 @@
"entity": "jpeg-trading",
"url": "https://jpeg-trading.com/",
"tags": ["suppress high utilisation warning"],
"vaults": [
"0xFA493128d7D092A082571b92334574A0a529E799",
"0xcc639251bd612A0930990162c15227D1Ba996354"
]
"vaults": ["0xcc639251bd612A0930990162c15227D1Ba996354"]
}
}
2 changes: 1 addition & 1 deletion 999/entities.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"clearstar": {
"name": "Clearstar",
"logo": "clearstar.svg",
"description": "Clearstar is a third-party curator using Euler infrastructure to configure vaults focused on decentralized markets.",
"description": "Clearstar is a third-party curator using Euler infrastructure to configure vaults across selected decentralized markets. The curator is responsible for asset selection, vault parameter configuration, and management of the vaults it configures.",
"url": "https://www.clearstar.xyz/",
"addresses": {
"0x65A067b5955F11F6202F14C3b9Cd64830C4170fB": "Clearstar Governor",
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Inside each network's directory are JSON files that correspond to the following

Each entry in this object corresponds to a company, organisation, or other entity. Each key is an **entity ID**, which is a unique string per entity. It should be in "URL slug" format (lowercase ASCII letters or numbers, separated by `-` characters).

An entity ID identifies the same organisation on every chain. When an entity appears in several chain files, its `name`, `logo`, `description`, `url` and `social` must be identical in each of them; only `addresses` may differ per file. Verification rejects drift.

* `name`: Full, official name of the entity. (Required)
* `logo`: The filename of a logo stored in the `logo/` directory.
* `description`: Long-form description of the entity, displayed on the entity's page.
Expand All @@ -29,7 +31,7 @@ Each entry in this object corresponds to a lending product, which is primarily a

* `name`: Official name of the product. (Required)
* `description`: Long-form description of the product, displayed on the product's page.
* `entity`: An entity ID that refers to the organisation responsible for governing and/or creating this vault, or a list of entity IDs if the vault is joint-managed.
* `entity`: An entity ID, or a list of entity IDs. The first (or only) entity is the organisation responsible for governing this product; any further entities are display-only co-brands (for example the issuer of the collateral a market is built on) and hold no governance role. Order matters, and every entry must be a distinct entity from `entities.json`.
* `url`: Website where users can learn more about the product.
* `logo`: The filename of a logo stored in the `logo/` directory.
* `vaults`: An array of the vault addresses (in checksumed hex format) that comprise the product. A vault may only appear in one product.
Expand Down
54 changes: 51 additions & 3 deletions verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ const ISO_ALPHA2_RE = /^[A-Za-z]{2}$/;
// label wherever it is listed. Registrations accumulate across validateChain
// calls so conflicts between chain files are caught too.
const entityAddressRegistry = new Map();
// Entity profiles are chain-agnostic too: the same entity ID must describe the
// same organisation (name, logo, description, url, social) in every chain file.
const ENTITY_PROFILE_FIELDS = ["name", "logo", "description", "url", "social"];
const entityProfileRegistry = new Map();

for (const file of fs.readdirSync(".")) {
if (!/^\d+$/.test(file)) continue;
Expand All @@ -98,6 +102,7 @@ function validateChain(chainId) {
const assets = loadJsonFileIfExists(`${chainId}/assets.json`) || [];

registerEntityAddresses(chainId, entities);
registerEntityProfiles(chainId, entities);
validateEarnVaults(`${chainId}/earn-vaults.json`, earnVaults);
validateAssets(`${chainId}/assets.json`, assets);

Expand Down Expand Up @@ -169,9 +174,7 @@ function validateChain(chainId) {
);
}

for (const entity of getArray(product.entity)) {
if (!entities[entity]) throw Error(`products: no such entity ${entity}`);
}
validateProductEntities(product.entity, entities, productId);

if (product.logo && !logos[product.logo])
throw Error(`products: logo not found: ${product.logo}`);
Expand Down Expand Up @@ -449,6 +452,51 @@ function registerEntityAddresses(chainId, entities) {
}
}

/**
* Validates that an entity ID describes the same organisation in every chain
* file. Addresses are checked separately by registerEntityAddresses.
*/
function registerEntityProfiles(chainId, entities) {
for (const entityId of Object.keys(entities)) {
const entity = entities[entityId];
const previous = entityProfileRegistry.get(entityId);

if (!previous) {
entityProfileRegistry.set(entityId, { chainId, entity });
continue;
}

for (const field of ENTITY_PROFILE_FIELDS) {
const before = JSON.stringify(previous.entity[field] ?? null);
const after = JSON.stringify(entity[field] ?? null);
if (before !== after)
throw Error(
`entities: ${entityId}.${field} differs between chain ${previous.chainId} and chain ${chainId}; entity profiles must match across chains`,
);
}
}
}

/**
* The first entity governs the product; any further entities are display-only
* co-brands. Every entry must be a distinct, known entity ID.
*/
function validateProductEntities(entity, entities, productId) {
const ids = getArray(entity);
if (ids.length === 0)
throw Error(
`products: entity must name at least one entity ID: ${productId}`,
);
const seen = new Set();
for (const entityId of ids) {
if (typeof entityId !== "string" || !entities[entityId])
throw Error(`products: no such entity ${entityId}: ${productId}`);
if (seen.has(entityId))
throw Error(`products: entity lists ${entityId} twice: ${productId}`);
seen.add(entityId);
}
}

function loadJsonFile(file) {
return JSON.parse(fs.readFileSync(file).toString());
}
Expand Down