Skip to content

Feat/por cre workflows - #2

Open
Emeric-Mi wants to merge 6 commits into
mainfrom
feat/por-cre-workflows
Open

Feat/por cre workflows#2
Emeric-Mi wants to merge 6 commits into
mainfrom
feat/por-cre-workflows

Conversation

@Emeric-Mi

Copy link
Copy Markdown
Collaborator

Add CRE attestation and verification workflows for mFONE and mHyperBTC

Implements Chainlink CRE-based Proof of Reserves for Midas tokens using the SAVE framework.

Attestation workflow triggers on NewClaim events, fetches ops data from IPFS, reads oracle price on-chain, verifies the fund manager email via Vlayer TLS Notary (mFONE only), runs an overcollateralization check using 1token AUM + Fasanara email NAV, builds a signed SAVE attestation, and pushes it on-chain.

Verification workflow triggers on AttestationSet events, fetches the attestation from IPFS, verifies all claims via @save/core, and pushes the verification result on-chain.

Supported tokens: mFONE (Fasanara email + 1token on-chain) and mHyperBTC (1token pv_base in BTC).

Includes per-token config templates for third-party verifiers (LlamaRisk etc.) to deploy their own independent verification workflows.

- mFONE method-1: combine Fasanara email NAV + 1token on-chain AUM
- Remove offchain-onchain fallback path and onchainAssets config entirely
- Fix HTTP call limit (skip fetchSupplyDetails for fundManager tokens)
- Fix optional Pinata group ID (try/catch instead of hard fail)
- Fix trailing comma in dev JSON configs
- Update publicKeySource to midas.app/.well-known/save-keys.json
- Update README: new overcol formulas, secrets table, token config fields
- Remove hasOffchainData flag — detected dynamically from fund_manager_claim
- Move vlayerEndpoint to global config level (not per-token)
- Simplify token entries in verification config to name only
- Update per-token templates to match current schema
- Trim overcollateralization section in README

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the Midas Proof of Reserves (PoR) workflows for attestation and verification within the Chainlink CRE framework. The changes include logic for on-chain oracle price retrieval, Vlayer TLS Notary verification for fund manager emails, and overcollateralization checks using 1token reports. Feedback highlights a logic error in the report-fetching loop that skips fallback timestamps if a threshold check fails, as well as risks associated with fragile regex parsing for email data and the sanitization of malformed API responses to zero. It is also recommended to make the hardcoded attestation expiration configurable to better accommodate different token risk profiles.

Comment on lines +414 to +437
for (const ts of [timestamps.exact, timestamps.before]) {
let report: OneTokenReportData | null = null
try {
report = fetchOneTokenReport(runtime, ts, tokenConfig.oneTokenApi)
} catch (e) {
runtime.log(`1token "${ts}" error: ${e instanceof Error ? e.message : String(e)}`)
continue
}
if (!report || typeof report.equity?.total !== 'number') {
runtime.log(`1token "${ts}" no data — trying next`)
continue
}
if (!oneTokenRawReport) {
oneTokenRawReport = report
oneTokenTimestamp = ts
}
const passed = evaluateReport(report, ts)
if (passed) {
oneTokenReport = report
} else {
runtime.log('1token ratio below threshold — falling back to Method 2')
}
break
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The loop tries to fetch the 1token report for the exact timestamp and then falls back to before. However, if the exact fetch succeeds but the evaluateReport check fails (ratio below threshold), it immediately falls back to Method 2 without trying the before timestamp. If the intent is to find any valid report within the window that passes the threshold, the logic should continue to the next timestamp if passed is false.


const parseAmount = (line: string): number | null => {
// Match a number like 58,489,684.03 at the end of the line (before optional USD/currency)
const match = line.match(/([\d,]+\.?\d*)\s*(?:USD)?[\s]*$/)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The regular expression used to parse amounts from the email body is somewhat fragile. It assumes the amount is at the end of the line and optionally followed by 'USD'. If the email format changes slightly (e.g., trailing spaces, different currency indicators, or thousands separators other than commas), this parser will fail. Consider a more robust parsing strategy or ensuring the source data is strictly formatted.


// Return only numeric fields to avoid null values crashing the CRE WASM serializer
const sanitize = (obj: Record<string, unknown>): Record<string, number> =>
Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, typeof v === 'number' ? v : 0]))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The sanitize function replaces any non-numeric value with 0. While this prevents the CRE WASM serializer from crashing on null/undefined values, it can lead to incorrect calculations if a required field is missing or malformed in the 1token API response. It would be safer to validate the presence of critical fields before sanitization.

References
  1. Enforce defensive programming: ensure appropriate null/nil/None checks or other language-idiomatic guards exist before object property accesses.

issuer: { identity: attesterPublicKey, name: 'Midas' },
publicKeySource: 'https://midas.app/.well-known/save-keys.json',
createdAt: now.toISOString(),
expiresAt: new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The attestation expiration is hardcoded to 7 days. While reasonable, it might be better to make this configurable per token or environment, especially if different tokens have different reporting frequencies or risk profiles.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant